Skip to content

Commit

Permalink
Generate VRF signing key file with owner permissions only
Browse files Browse the repository at this point in the history
  • Loading branch information
Jimbo4350 committed Oct 22, 2020
1 parent 810c956 commit bf3d930
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 19 deletions.
8 changes: 7 additions & 1 deletion cardano-api/src/Cardano/Api/Typed.hs
Expand Up @@ -228,6 +228,8 @@ module Cardano.Api.Typed (
writeFileTextEnvelope,
readTextEnvelopeFromFile,
readTextEnvelopeOfTypeFromFile,
textEnvelopeToJSON,

-- *** Reading one of several key types
FromSomeType(..),
deserialiseFromTextEnvelopeAnyOf,
Expand Down Expand Up @@ -3112,7 +3114,11 @@ writeFileTextEnvelope path mbDescr a =
runExceptT $ do
handleIOExceptT (FileIOError path) $ BS.writeFile path content
where
content = LBS.toStrict $ encodePretty' TextView.textViewJSONConfig (serialiseToTextEnvelope mbDescr a) <> "\n"
content = LBS.toStrict $ textEnvelopeToJSON mbDescr a

textEnvelopeToJSON :: HasTextEnvelope a => Maybe TextEnvelopeDescr -> a -> LBS.ByteString
textEnvelopeToJSON mbDescr a =
encodePretty' TextView.textViewJSONConfig (serialiseToTextEnvelope mbDescr a) <> "\n"

readFileTextEnvelope :: HasTextEnvelope a
=> AsType a
Expand Down
13 changes: 11 additions & 2 deletions cardano-cli/app/cardano-cli.hs
@@ -1,5 +1,10 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}

#if !defined(mingw32_HOST_OS)
#define UNIX
#endif

import Cardano.Prelude hiding (option)

import Control.Monad.Trans.Except.Exit (orDie)
Expand All @@ -9,13 +14,17 @@ import Cardano.CLI.Parsers (opts, pref)
import Cardano.CLI.Run (renderClientCommandError, runClientCommand)
import Cardano.CLI.TopHandler
import qualified Cardano.Crypto.Libsodium as Crypto

#ifdef UNIX
import System.Posix.Files
#endif

main :: IO ()
main = toplevelExceptionHandler $ do
-- TODO: Remove sodiumInit: https://github.com/input-output-hk/cardano-base/issues/175
Crypto.sodiumInit

#ifdef UNIX
_ <- setFileCreationMask (otherModes `unionFileModes` groupModes)
#endif
co <- Opt.customExecParser pref opts

orDie renderClientCommandError $ runClientCommand co
6 changes: 6 additions & 0 deletions cardano-cli/cardano-cli.cabal
Expand Up @@ -140,6 +140,8 @@ executable cardano-cli
-Wall
-rtsopts
"-with-rtsopts=-T"
if !os(windows)
build-depends: unix
build-depends: base >=4.12 && <5
, cardano-cli
, cardano-config
Expand All @@ -154,6 +156,10 @@ test-suite cardano-cli-test
hs-source-dirs: test
main-is: cardano-cli-test.hs
type: exitcode-stdio-1.0
if !os(windows)
build-depends: unix
, cardano-node
other-modules: Test.Cli.FilePermissions

build-depends: base
, aeson
Expand Down
67 changes: 57 additions & 10 deletions cardano-cli/src/Cardano/CLI/Shelley/Run/Node.hs
@@ -1,22 +1,36 @@
{-# LANGUAGE CPP #-}

#if !defined(mingw32_HOST_OS)
#define UNIX
#endif

module Cardano.CLI.Shelley.Run.Node
( ShelleyNodeCmdError
, renderShelleyNodeCmdError
, runNodeCmd
) where

import Cardano.Prelude hiding ((<.>))
import Prelude (id)

import Control.Monad.Trans.Except (ExceptT)
import Control.Monad.Trans.Except.Extra (firstExceptT, hoistEither, newExceptT)
import qualified Data.ByteString.Char8 as BS
import qualified Data.Text as Text
#ifdef UNIX
import qualified Control.Exception as Exception
import qualified Data.ByteString.Lazy as LBS
import System.Directory (removeFile, renameFile)
import System.FilePath.Posix (splitFileName, (<.>))
import System.IO (hClose, openTempFile)
#endif

import Cardano.Api.TextView (TextViewDescription (..))
import Cardano.Api.Typed
import Cardano.CLI.Shelley.Commands
import Cardano.CLI.Shelley.Key (InputDecodeError, VerificationKeyOrFile,
readSigningKeyFileAnyOf, readVerificationKeyOrFile)
import Cardano.CLI.Types (SigningKeyFile (..), VerificationKeyFile (..))
import Cardano.Prelude
import Control.Monad.Trans.Except (ExceptT)
import Control.Monad.Trans.Except.Extra (firstExceptT, hoistEither, newExceptT)
import Prelude (id)

import qualified Data.ByteString.Char8 as BS
import qualified Data.Text as Text

{- HLINT ignore "Reduce duplication" -}

Expand All @@ -25,11 +39,20 @@ data ShelleyNodeCmdError
| ShelleyNodeCmdReadKeyFileError !(FileError InputDecodeError)
| ShelleyNodeCmdWriteFileError !(FileError ())
| ShelleyNodeCmdOperationalCertificateIssueError !OperationalCertIssueError
| ShelleyNodeCmdVrfSigningKeyCreationError
FilePath
-- ^ Target path
FilePath
-- ^ Temp path
deriving Show

renderShelleyNodeCmdError :: ShelleyNodeCmdError -> Text
renderShelleyNodeCmdError err =
case err of
ShelleyNodeCmdVrfSigningKeyCreationError targetPath tempPath ->
Text.pack $ "Error creating VRF signing key file. Target path: " <> targetPath
<> " Temporary path: " <> tempPath

ShelleyNodeCmdReadFileError fileErr -> Text.pack (displayError fileErr)

ShelleyNodeCmdReadKeyFileError fileErr -> Text.pack (displayError fileErr)
Expand Down Expand Up @@ -100,15 +123,18 @@ runNodeKeyGenKES (VerificationKeyFile vkeyPath) (SigningKeyFile skeyPath) = do
skeyDesc = TextViewDescription "KES Signing Key"
vkeyDesc = TextViewDescription "KES Verification Key"


runNodeKeyGenVRF :: VerificationKeyFile -> SigningKeyFile
-> ExceptT ShelleyNodeCmdError IO ()
runNodeKeyGenVRF (VerificationKeyFile vkeyPath) (SigningKeyFile skeyPath) = do
skey <- liftIO $ generateSigningKey AsVrfKey
let vkey = getVerificationKey skey
#ifdef UNIX
writeTextEnvelopeFileWithOwnerPermissions skeyPath (Just skeyDesc) skey
#else
firstExceptT ShelleyNodeCmdWriteFileError
. newExceptT
$ writeFileTextEnvelope skeyPath (Just skeyDesc) skey
. newExceptT
$ writeFileTextEnvelope skeyPath (Just skeyDesc) skey
#endif
firstExceptT ShelleyNodeCmdWriteFileError
. newExceptT
$ writeFileTextEnvelope vkeyPath (Just vkeyDesc) vkey
Expand Down Expand Up @@ -244,3 +270,24 @@ readColdVerificationKeyOrFile coldVerKeyOrFile =
, FromSomeType (AsVerificationKey AsGenesisDelegateKey) castVerificationKey
]
fp

#ifdef UNIX
writeTextEnvelopeFileWithOwnerPermissions
:: HasTextEnvelope a
=> FilePath
-> Maybe TextEnvelopeDescr
-> a
-> ExceptT ShelleyNodeCmdError IO ()
writeTextEnvelopeFileWithOwnerPermissions targetPath mbDescr a = do
let content = textEnvelopeToJSON mbDescr a
(targetDir, targetFile) = splitFileName targetPath
liftIO $ Exception.bracketOnError
(openTempFile targetDir $ targetFile <.> "tmp")
(\(tmpPath, fHandle) -> do
hClose fHandle >> removeFile tmpPath
return $ ShelleyNodeCmdVrfSigningKeyCreationError targetPath tmpPath)
(\(tmpPath, fHandle) -> do
LBS.hPut fHandle content
hClose fHandle
renameFile tmpPath targetPath)
#endif
47 changes: 47 additions & 0 deletions cardano-cli/test/Test/Cli/FilePermissions.hs
@@ -0,0 +1,47 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}

module Test.Cli.FilePermissions
( tests
) where

import Cardano.Prelude


import Cardano.Node.Run (checkVRFFilePermissions)
import Hedgehog (Property, discover, success)
import qualified Hedgehog
import qualified Hedgehog.Extras.Test.Base as H
import Hedgehog.Internal.Property (failWith)

import Test.OptParse (execCardanoCLI)

-- | This property ensures that the VRF signing key file is created only with owner permissions
prop_createVRFSigningKeyFilePermissions :: Property
prop_createVRFSigningKeyFilePermissions =
H.propertyOnce . H.moduleWorkspace "tmp" $ \tempDir -> do
-- Key filepaths
vrfVerKey <- H.noteTempFile tempDir "VRF-verification-key-file"

vrfSignKey <- H.noteTempFile tempDir "VRF-signing-key-file"

-- Create VRF key pair
void $ execCardanoCLI
[ "shelley", "node", "key-gen-VRF"
, "--verification-key-file", vrfVerKey
, "--signing-key-file", vrfSignKey
]

result <- liftIO . runExceptT $ checkVRFFilePermissions vrfSignKey
case result of
Left err ->
failWith Nothing
$ "key-gen-VRF cli command created a VRF signing key \
\file with the wrong permissions: " <> show err
Right () -> success

-- -----------------------------------------------------------------------------

tests :: IO Bool
tests =
Hedgehog.checkParallel $$discover
Expand Up @@ -32,26 +32,35 @@ golden_shelleyGenesisKeyDelegationCertificate =
genesisKeyDelegCertFilePath <-
noteTempFile tempDir "genesis-key-delegation-certificate-file"

-- Signing Key filepaths
genesisSignKeyFilePath <- noteTempFile tempDir "genesis-signing-key-file"
genesisDelegSignKeyFilePath <- noteTempFile tempDir "genesis-delegate-signing-key-file"
vrfSignKeyFilePath <- noteTempFile tempDir "vrf-signing-key-file"

genesisDelegOpCertCounterFilePath <- noteTempFile tempDir "genesis-delegate-opcert-counter"


-- Generate genesis key pair
void $ execCardanoCLI
[ "shelley","genesis","key-gen-genesis"
, "--verification-key-file", genesisVerKeyFilePath
, "--signing-key-file", "/dev/null"
, "--signing-key-file", genesisSignKeyFilePath
]

-- Generate genesis delegate key pair
void $ execCardanoCLI
[ "shelley","genesis","key-gen-delegate"
, "--verification-key-file", genesisDelegVerKeyFilePath
, "--signing-key-file", "/dev/null"
, "--operational-certificate-issue-counter-file", "/dev/null"
, "--signing-key-file", genesisDelegSignKeyFilePath
, "--operational-certificate-issue-counter-file"
, genesisDelegOpCertCounterFilePath
]

-- Generate VRF key pair
void $ execCardanoCLI
[ "shelley","node","key-gen-VRF"
, "--verification-key-file", vrfVerKeyFilePath
, "--signing-key-file", "/dev/null"
, "--signing-key-file", vrfSignKeyFilePath
]

H.assertFilesExist
Expand Down
17 changes: 17 additions & 0 deletions cardano-cli/test/cardano-cli-test.hs
@@ -1,7 +1,14 @@
{-# LANGUAGE CPP #-}

#if !defined(mingw32_HOST_OS)
#define UNIX
#endif

import Cardano.Prelude

import Hedgehog.Main (defaultMain)

import qualified Test.Cli.FilePermissions
import qualified Test.Cli.ITN
import qualified Test.Cli.Pioneers.Exercise1
import qualified Test.Cli.Pioneers.Exercise2
Expand All @@ -11,9 +18,19 @@ import qualified Test.Cli.Pioneers.Exercise4
main :: IO ()
main =
defaultMain
#ifdef UNIX
[ Test.Cli.FilePermissions.tests
, Test.Cli.ITN.tests
, Test.Cli.Pioneers.Exercise1.tests
, Test.Cli.Pioneers.Exercise2.tests
, Test.Cli.Pioneers.Exercise3.tests
, Test.Cli.Pioneers.Exercise4.tests
]
#else
[ Test.Cli.ITN.tests
, Test.Cli.Pioneers.Exercise1.tests
, Test.Cli.Pioneers.Exercise2.tests
, Test.Cli.Pioneers.Exercise3.tests
, Test.Cli.Pioneers.Exercise4.tests
]
#endif
4 changes: 2 additions & 2 deletions scripts/lite/shelley-testnet.sh
Expand Up @@ -75,7 +75,7 @@ for i in 1 2 3; do

# Generate a KES keys
mkdir -p "${data_dir}/node-$i"
cardano-cli shelley node key-gen-KES \
cabal run exe:cardano-cli -- shelley node key-gen-KES \
--verification-key-file "${data_dir}/node-$i/kes.vkey" \
--signing-key-file "${data_dir}/node-$i/kes.skey"

Expand All @@ -93,7 +93,7 @@ for i in 1 2 3; do
chmod u+r "${data_dir}/node-$i/vrf.skey"

# Issue an operational certificate:
cardano-cli shelley node issue-op-cert \
cabal run exe:cardano-cli -- shelley node issue-op-cert \
--kes-period 0 \
--kes-verification-key-file "${data_dir}/node-$i/kes.vkey" \
--cold-signing-key-file "${data_dir}/node-$i/hotkey.skey" \
Expand Down

0 comments on commit bf3d930

Please sign in to comment.