Skip to content

Commit

Permalink
Add Cardano.Crypto.Libsodium with basic secure memory stubs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
phadej committed May 31, 2020
1 parent 8061c5d commit 9fe7236
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 0 deletions.
26 changes: 26 additions & 0 deletions cardano-crypto-class/cardano-crypto-class.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ library
Cardano.Crypto.VRF.NeverUsed
Cardano.Crypto.VRF.Simple

Cardano.Crypto.Libsodium

build-depends: base
, base16-bytestring
, bytestring
Expand All @@ -75,6 +77,8 @@ library
-fno-warn-safe
-fno-warn-unsafe

pkgconfig-depends: libsodium

if (!flag(development))
ghc-options: -Werror

Expand Down Expand Up @@ -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
59 changes: 59 additions & 0 deletions cardano-crypto-class/memory-example/Main.hs
Original file line number Diff line number Diff line change
@@ -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'
103 changes: 103 additions & 0 deletions cardano-crypto-class/src/Cardano/Crypto/Libsodium.hs
Original file line number Diff line number Diff line change
@@ -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.
--
-- <https://libsodium.gitbook.io/doc/usage>
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'.
--
-- <https://libsodium.gitbook.io/doc/memory_management>
--
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():@
--
-- <https://libsodium.gitbook.io/doc/usage>
foreign import capi "sodium.h sodium_init" c_sodium_init :: IO Int

-- | @void sodium_memzero(void * const pnt, const size_t len);@
--
-- <https://libsodium.gitbook.io/doc/memory_management#zeroing-memory>
foreign import capi "sodium.h sodium_memzero" c_sodium_memzero :: Ptr a -> CSize -> IO ()

-- | @void *sodium_malloc(size_t size);@
--
-- <https://libsodium.gitbook.io/doc/memory_management>
foreign import capi "sodium.h sodium_malloc" c_sodium_malloc :: CSize -> IO (Ptr a)

-- | @void sodium_free(void *ptr);@
--
-- <https://libsodium.gitbook.io/doc/memory_management>
foreign import capi "sodium.h &sodium_free" c_sodium_free_funptr :: FunPtr (Ptr a -> IO ())

0 comments on commit 9fe7236

Please sign in to comment.