From 9fe723660ba6a997a8757fa67292f807daa99de0 Mon Sep 17 00:00:00 2001 From: Oleg Grenrus Date: Mon, 1 Jun 2020 00:47:21 +0300 Subject: [PATCH] Add Cardano.Crypto.Libsodium with basic secure memory stubs The allocSecureForeignPtr is as secure as `libsodium` makes it. To allocate the single `GHC.Fingerprint` (used for 128bit MD5 hash), on my machine it reserves four memory pages. With provided `memory-example`, it is possible to investigate what (and if) happens. Execute with cabal run test-memory-example -- Then, using information in /proc we can see how pages come and go. The pages around the payload page (`rw-p` one) are protection trap pages by libsodium, explained in https://libsodium.gitbook.io/doc/memory_management#guarded-heap-allocations ```diff --- before.txt 2020-06-01 00:46:35.947953980 +0300 +++ after.txt 2020-06-01 00:46:42.003877057 +0300 @@ -46,10 +46,6 @@ 7f3538e01000-7f3538e02000 rw-p 0019d000 103:05 399924 /lib/x86_64-linux-gnu/libm-2.27.so 7f3538e02000-7f3538e29000 r-xp 00000000 103:05 397952 /lib/x86_64-linux-gnu/ld-2.27.so 7f3538fff000-7f3539005000 rw-p 00000000 00:00 0 -7f3539025000-7f3539026000 r--p 00000000 00:00 0 -7f3539026000-7f3539027000 ---p 00000000 00:00 0 -7f3539027000-7f3539028000 rw-p 00000000 00:00 0 -7f3539028000-7f3539029000 ---p 00000000 00:00 0 7f3539029000-7f353902a000 r--p 00027000 103:05 397952 /lib/x86_64-linux-gnu/ld-2.27.so 7f353902a000-7f353902b000 rw-p 00028000 103:05 397952 /lib/x86_64-linux-gnu/ld-2.27.so 7f353902b000-7f353902c000 rw-p 00000000 00:00 0 ``` I think that for development this is very good option. The less memory hungry variant can be easily added later. Yet, in it we'd still like to use `libsodium`'s `mlock` and `munlock` primitives, so having this in place sooner is beneficial. --- .../cardano-crypto-class.cabal | 26 +++++ cardano-crypto-class/memory-example/Main.hs | 59 ++++++++++ .../src/Cardano/Crypto/Libsodium.hs | 103 ++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 cardano-crypto-class/memory-example/Main.hs create mode 100644 cardano-crypto-class/src/Cardano/Crypto/Libsodium.hs diff --git a/cardano-crypto-class/cardano-crypto-class.cabal b/cardano-crypto-class/cardano-crypto-class.cabal index 8a7d0ffc13..fc64893390 100644 --- a/cardano-crypto-class/cardano-crypto-class.cabal +++ b/cardano-crypto-class/cardano-crypto-class.cabal @@ -55,6 +55,8 @@ library Cardano.Crypto.VRF.NeverUsed Cardano.Crypto.VRF.Simple + Cardano.Crypto.Libsodium + build-depends: base , base16-bytestring , bytestring @@ -75,6 +77,8 @@ library -fno-warn-safe -fno-warn-unsafe + pkgconfig-depends: libsodium + if (!flag(development)) ghc-options: -Werror @@ -111,3 +115,25 @@ test-suite test-crypto if (!flag(development)) ghc-options: -Werror + +test-suite test-memory-example + type: exitcode-stdio-1.0 + hs-source-dirs: memory-example + main-is: Main.hs + + build-depends: base + , cardano-crypto-class + , unix + + default-language: Haskell2010 + + ghc-options: -Weverything + -Wno-incomplete-uni-patterns + -fno-warn-all-missed-specialisations + -fno-warn-implicit-prelude + -fno-warn-missing-import-lists + -fno-warn-safe + -fno-warn-unsafe + + if (!flag(development)) + ghc-options: -Werror diff --git a/cardano-crypto-class/memory-example/Main.hs b/cardano-crypto-class/memory-example/Main.hs new file mode 100644 index 0000000000..d39d8eafd6 --- /dev/null +++ b/cardano-crypto-class/memory-example/Main.hs @@ -0,0 +1,59 @@ +{-# LANGUAGE RankNTypes #-} +module Main (main) where + +import Foreign.ForeignPtr (ForeignPtr, finalizeForeignPtr, withForeignPtr) +import Foreign.Storable (Storable (peek, poke)) +import Control.Monad (void, when) +import GHC.Fingerprint (Fingerprint (..)) +import System.Environment (getArgs) +import System.Posix.Process (getProcessID) + +import Cardano.Crypto.Libsodium (allocSecureForeignPtr, sodiumInit) + +main :: IO () +main = do + pid <- getProcessID + + putStrLn $ "If you run this test with 'pause' argument" + putStrLn $ "you may look at /proc/" ++ show pid ++ "/maps" + putStrLn $ " /proc/" ++ show pid ++ "/smaps" + + sodiumInit + + args <- getArgs + + sodiumInit + example args allocSecureForeignPtr + +example + :: [String] + -> (IO (ForeignPtr Fingerprint)) + -> IO () +example args alloc = do + -- create foreign ptr to mlocked memory + fptr <- alloc + withForeignPtr fptr $ \ptr -> poke ptr (Fingerprint 0xdead 0xc0de) + + when ("pause" `elem` args) $ do + putStrLn "Allocated..." + void getLine + + -- we shouldn't do this, but rather do computation inside + -- withForeignPtr on provided Ptr a + fingerprint <- withForeignPtr fptr peek + + -- we have the fingeprint + print fingerprint + + -- force finalizers + finalizeForeignPtr fptr + + when ("pause" `elem` args) $ do + putStrLn "Finalized..." + void getLine + + when ("use-after-free" `elem` args) $ do + -- in this demo we can try to print it again. + -- this should deterministically cause segmentation fault + fingerprint' <- withForeignPtr fptr peek + print fingerprint' diff --git a/cardano-crypto-class/src/Cardano/Crypto/Libsodium.hs b/cardano-crypto-class/src/Cardano/Crypto/Libsodium.hs new file mode 100644 index 0000000000..80eaf6b7ff --- /dev/null +++ b/cardano-crypto-class/src/Cardano/Crypto/Libsodium.hs @@ -0,0 +1,103 @@ +{-# LANGUAGE CApiFFI #-} +{-# LANGUAGE ScopedTypeVariables #-} +module Cardano.Crypto.Libsodium + ( -- * High level interface + sodiumInit + + , allocSecureForeignPtr + + -- * Low-level interface + -- ** Initialization + , c_sodium_init + + -- ** Zeroing memory + , c_sodium_memzero + + -- ** Guarded heap allocations + , c_sodium_malloc + , c_sodium_free_funptr + ) where + +import Control.Monad (when, unless) +import Foreign.C.Error (errnoToIOError, getErrno) +import Foreign.C.Types (CSize (..)) +import Foreign.ForeignPtr (ForeignPtr, newForeignPtr) +import Foreign.Ptr (FunPtr, Ptr, nullPtr) +import Foreign.Storable (Storable (alignment, sizeOf)) +import GHC.IO.Exception (ioException) + +------------------------------------------------------------------------------- +-- Initialization +------------------------------------------------------------------------------- + +-- @sodiumInit@ initializes the library and should be called before any other +-- function provided by Sodium. It is safe to call this function more than once +-- and from different threads -- subsequent calls won't have any effects. +-- +-- +sodiumInit :: IO () +sodiumInit = do + res <- c_sodium_init + -- sodium_init() returns 0 on success, -1 on failure, and 1 if the library + -- had already been initialized. + unless (res == 0 || res == 1) $ fail "sodium_init failed" + +------------------------------------------------------------------------------- +-- Acquiring memory +------------------------------------------------------------------------------- + +-- | Allocate secure memory using 'c_sodium_malloc'. +-- +-- +-- +allocSecureForeignPtr :: Storable a => IO (ForeignPtr a) +allocSecureForeignPtr = impl undefined where + impl :: forall b. Storable b => b -> IO (ForeignPtr b) + impl b = do + ptr <- c_sodium_malloc size + when (ptr == nullPtr) $ do + errno <- getErrno + ioException $ errnoToIOError "allocSecureForeingPtr: c_sodium_malloc" errno Nothing Nothing + + newForeignPtr c_sodium_free_funptr ptr + + where + size :: CSize + size = fromIntegral size'' + + size' :: Int + size' = sizeOf b + + align :: Int + align = alignment b + + size'' :: Int + size'' + | m == 0 = size' + | otherwise = (q + 1) * align + where + (q,m) = size' `divMod` align + +------------------------------------------------------------------------------- +-- Low-level c-bindings +------------------------------------------------------------------------------- + +-- | @void sodium_init():@ +-- +-- +foreign import capi "sodium.h sodium_init" c_sodium_init :: IO Int + +-- | @void sodium_memzero(void * const pnt, const size_t len);@ +-- +-- +foreign import capi "sodium.h sodium_memzero" c_sodium_memzero :: Ptr a -> CSize -> IO () + +-- | @void *sodium_malloc(size_t size);@ +-- +-- +foreign import capi "sodium.h sodium_malloc" c_sodium_malloc :: CSize -> IO (Ptr a) + +-- | @void sodium_free(void *ptr);@ +-- +-- +foreign import capi "sodium.h &sodium_free" c_sodium_free_funptr :: FunPtr (Ptr a -> IO ())