Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ index-state:
, hackage.haskell.org 2026-02-17T10:15:41Z
, cardano-haskell-packages 2026-03-19T11:07:17Z

-- BEGIN SRP STANZAS MANAGED BY STANZAMAN --

source-repository-package
type: git
location: https://github.com/IntersectMBO/cardano-api.git
tag: 58a67f618a2462edd38b00b0789c8fe024edf581
subdir: cardano-api
--sha256: 0j3lk8hfvy9045h41ndparibw27bkz2az29fn0nnqp6sqhrgvfsg

-- END SRP STANZAS MANAGED BY STANZAMAN --

packages:
cardano-cli

Expand Down
2 changes: 2 additions & 0 deletions cardano-cli/src/Cardano/CLI/EraIndependent/Key/Run.hs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ runNonExtendedKeyCmd
vk@AGenesisUTxOVerificationKey{} -> goFail vk
vk@AKesVerificationKey{} -> goFail vk
vk@AVrfVerificationKey{} -> goFail vk
vk@ABlsVerificationKey{} -> goFail vk
vk@AStakePoolVerificationKey{} -> goFail vk
vk@AStakeVerificationKey{} -> goFail vk
vk@ADRepVerificationKey{} -> goFail vk
Expand Down Expand Up @@ -248,6 +249,7 @@ readExtendedVerificationKeyFile evkfile = do
k@AGenesisUTxOVerificationKey{} -> goFail k
k@AKesVerificationKey{} -> goFail k
k@AVrfVerificationKey{} -> goFail k
k@ABlsVerificationKey{} -> goFail k
k@AStakePoolVerificationKey{} -> goFail k
k@AStakeVerificationKey{} -> goFail k
k@ADRepVerificationKey{} -> goFail k
Expand Down
32 changes: 32 additions & 0 deletions cardano-cli/src/Cardano/CLI/EraIndependent/Node/Command.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ module Cardano.CLI.EraIndependent.Node.Command
, NodeKeyGenColdCmdArgs (..)
, NodeKeyGenKESCmdArgs (..)
, NodeKeyGenVRFCmdArgs (..)
, NodeKeyGenBLSCmdArgs (..)
, NodeKeyHashVRFCmdArgs (..)
, NodeKeyHashBLSCmdArgs (..)
, NodeIssuePopBLSCmdArgs (..)
, NodeNewCounterCmdArgs (..)
, NodeIssueOpCertCmdArgs (..)
)
Expand All @@ -25,7 +28,10 @@ data NodeCmds
= NodeKeyGenColdCmd !NodeKeyGenColdCmdArgs
| NodeKeyGenKESCmd !NodeKeyGenKESCmdArgs
| NodeKeyGenVRFCmd !NodeKeyGenVRFCmdArgs
| NodeKeyGenBLSCmd !NodeKeyGenBLSCmdArgs
| NodeKeyHashVRFCmd !NodeKeyHashVRFCmdArgs
| NodeKeyHashBLSCmd !NodeKeyHashBLSCmdArgs
| NodeIssuePopBLSCmd !NodeIssuePopBLSCmdArgs
| NodeNewCounterCmd !NodeNewCounterCmdArgs
| NodeIssueOpCertCmd !NodeIssueOpCertCmdArgs
deriving Show
Expand Down Expand Up @@ -55,13 +61,36 @@ data NodeKeyGenVRFCmdArgs
}
deriving Show

data NodeKeyGenBLSCmdArgs
= NodeKeyGenBLSCmdArgs
{ keyOutputFormat :: !(Vary [FormatBech32, FormatTextEnvelope])
, vkeyFile :: !(VerificationKeyFile Out)
, skeyFile :: !(SigningKeyFile Out)
}
deriving Show

data NodeKeyHashVRFCmdArgs
= NodeKeyHashVRFCmdArgs
{ vkeySource :: !(VerificationKeyOrFile VrfKey)
, mOutFile :: !(Maybe (File () Out))
}
deriving Show

data NodeKeyHashBLSCmdArgs
= NodeKeyHashBLSCmdArgs
{ vkeySource :: !(VerificationKeyOrFile BlsKey)
, mOutFile :: !(Maybe (File () Out))
}
deriving Show

data NodeIssuePopBLSCmdArgs
= NodeIssuePopBLSCmdArgs
{ blsSkeyFile :: !(SigningKeyFile In)
-- ^ The BLS signing key.
, outFile :: !(File () Out)
}
deriving Show

data NodeNewCounterCmdArgs
= NodeNewCounterCmdArgs
{ coldVkeyFile :: !ColdVerificationKeyOrFile
Expand Down Expand Up @@ -89,6 +118,9 @@ renderNodeCmds = \case
NodeKeyGenColdCmd{} -> "node key-gen"
NodeKeyGenKESCmd{} -> "node key-gen-KES"
NodeKeyGenVRFCmd{} -> "node key-gen-VRF"
NodeKeyGenBLSCmd{} -> "node key-gen-BLS"
NodeKeyHashVRFCmd{} -> "node key-hash-VRF"
NodeKeyHashBLSCmd{} -> "node key-hash-BLS"
NodeIssuePopBLSCmd{} -> "node issue-pop-BLS"
NodeNewCounterCmd{} -> "node new-counter"
NodeIssueOpCertCmd{} -> "node issue-op-cert"
52 changes: 52 additions & 0 deletions cardano-cli/src/Cardano/CLI/EraIndependent/Node/Option.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ module Cardano.CLI.EraIndependent.Node.Option
)
where

import Cardano.Api (File (..), FileDirection (In))

import Cardano.CLI.EraBased.Common.Option
import Cardano.CLI.EraIndependent.Node.Command
import Cardano.CLI.EraIndependent.Node.Command qualified as Cmd
import Cardano.CLI.Parser
import Cardano.CLI.Type.Common (SigningKeyFile)

import Data.Foldable
import Options.Applicative hiding (help, str)
Expand Down Expand Up @@ -43,12 +46,32 @@ pNodeCmds =
mconcat
[ "Create a key pair for a node VRF operational key"
]
, Opt.hsubparser $
commandWithMetavar "key-gen-BLS" $
Opt.info pKeyGenBLS $
Opt.progDesc $
mconcat
[ "Create a key pair for a node BLS operational key"
]
, Opt.hsubparser $
commandWithMetavar "key-hash-VRF" . Opt.info pKeyHashVRF $
Opt.progDesc $
mconcat
[ "Print hash of a node's operational VRF key."
]
, Opt.hsubparser $
commandWithMetavar "key-hash-BLS" . Opt.info pKeyHashBLS $
Opt.progDesc $
mconcat
[ "Print hash of a node's operational BLS key."
]
, Opt.hsubparser $
commandWithMetavar "issue-pop-BLS" $
Opt.info pIssuePopBLS $
Opt.progDesc $
mconcat
[ "Issue a BLS proof of possession"
]
, Opt.hsubparser $
commandWithMetavar "new-counter" $
Opt.info pNewCounter $
Expand Down Expand Up @@ -97,13 +120,42 @@ pKeyGenVRF =
<*> pVerificationKeyFileOut
<*> pSigningKeyFileOut

pKeyGenBLS :: Parser NodeCmds
pKeyGenBLS =
fmap Cmd.NodeKeyGenBLSCmd $
Cmd.NodeKeyGenBLSCmdArgs
<$> pKeyOutputFormat
<*> pVerificationKeyFileOut
<*> pSigningKeyFileOut

pKeyHashVRF :: Parser NodeCmds
pKeyHashVRF =
fmap Cmd.NodeKeyHashVRFCmd $
Cmd.NodeKeyHashVRFCmdArgs
<$> pVerificationKeyOrFileIn
<*> pMaybeOutputFile

pKeyHashBLS :: Parser NodeCmds
pKeyHashBLS =
fmap Cmd.NodeKeyHashBLSCmd $
Cmd.NodeKeyHashBLSCmdArgs
<$> pVerificationKeyOrFileIn
<*> pMaybeOutputFile

pIssuePopBLS :: Parser NodeCmds
pIssuePopBLS =
fmap Cmd.NodeIssuePopBLSCmd $
Cmd.NodeIssuePopBLSCmdArgs
<$> pBlsSigningKeyFile
<*> pOutputFile

pBlsSigningKeyFile :: Parser (SigningKeyFile In)
pBlsSigningKeyFile =
File
<$> parseFilePath
"bls-signing-key-file"
"Input filepath of the BLS signing key."

pNewCounter :: Parser NodeCmds
pNewCounter =
fmap Cmd.NodeNewCounterCmd $
Expand Down
81 changes: 81 additions & 0 deletions cardano-cli/src/Cardano/CLI/EraIndependent/Node/Run.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module Cardano.CLI.EraIndependent.Node.Run
, runNodeKeyGenKesCmd
, runNodeKeyGenVrfCmd
, runNodeKeyHashVrfCmd
, runNodeKeyHashBlsCmd
, runNodeNewCounterCmd
)
where
Expand All @@ -37,7 +38,10 @@ runNodeCmds = \case
Cmd.NodeKeyGenColdCmd args -> runNodeKeyGenColdCmd args
Cmd.NodeKeyGenKESCmd args -> runNodeKeyGenKesCmd args
Cmd.NodeKeyGenVRFCmd args -> runNodeKeyGenVrfCmd args
Cmd.NodeKeyGenBLSCmd args -> runNodeKeyGenBLSCmd args
Cmd.NodeKeyHashVRFCmd args -> runNodeKeyHashVrfCmd args
Cmd.NodeKeyHashBLSCmd args -> runNodeKeyHashBlsCmd args
Cmd.NodeIssuePopBLSCmd args -> runNodeIssuePopBLSCmd args
Cmd.NodeNewCounterCmd args -> runNodeNewCounterCmd args
Cmd.NodeIssueOpCertCmd args -> runNodeIssueOpCertCmd args

Expand Down Expand Up @@ -215,6 +219,47 @@ runNodeKeyGenVrfCmd
skeyDesc = "VRF Signing Key"
vkeyDesc = "VRF Verification Key"

runNodeKeyGenBLSCmd
:: ()
=> Cmd.NodeKeyGenBLSCmdArgs
-> CIO e ()
runNodeKeyGenBLSCmd
Cmd.NodeKeyGenBLSCmdArgs
{ keyOutputFormat
, vkeyFile
, skeyFile
} = do
skey <- generateSigningKey AsBlsKey

let vkey = getVerificationKey skey

keyOutputFormat
& ( id
. Vary.on
( \FormatBech32 -> do
fromEitherIOCli @(FileError ())
. writeTextFile skeyFile
$ serialiseToBech32 skey
fromEitherIOCli @(FileError ())
. writeTextFile vkeyFile
$ serialiseToBech32 vkey
)
Comment on lines +241 to +246
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Bech32 branch writes the signing key with writeTextFile, while the text-envelope branch uses writeLazyByteStringFileWithOwnerPermissions for the signing key. If writeTextFile does not restrict permissions, this can create world-readable secret key files. Use an owner-permissions writer for the Bech32 signing key as well (and keep the verification key as-is).

Suggested change
. writeTextFile skeyFile
$ serialiseToBech32 skey
fromEitherIOCli @(FileError ())
. writeTextFile vkeyFile
$ serialiseToBech32 vkey
)
. writeLazyByteStringFileWithOwnerPermissions skeyFile
$ Data.Text.Lazy.Encoding.encodeUtf8
(Data.Text.Lazy.fromStrict (serialiseToBech32 skey))
fromEitherIOCli @(FileError ())
. writeTextFile vkeyFile
$ serialiseToBech32 vkey

Copilot uses AI. Check for mistakes.
. Vary.on
( \FormatTextEnvelope -> do
fromEitherIOCli @(FileError ())
. writeLazyByteStringFileWithOwnerPermissions skeyFile
$ textEnvelopeToJSON (Just skeyDesc) skey
fromEitherIOCli @(FileError ())
. writeLazyByteStringFile vkeyFile
$ textEnvelopeToJSON (Just vkeyDesc) vkey
)
$ Vary.exhaustiveCase
)
where
skeyDesc, vkeyDesc :: TextEnvelopeDescr
skeyDesc = "BLS Signing Key"
vkeyDesc = "BLS Verification Key"

runNodeKeyHashVrfCmd
:: ()
=> Cmd.NodeKeyHashVRFCmdArgs
Expand All @@ -232,6 +277,42 @@ runNodeKeyHashVrfCmd
fromEitherIOCli @(FileError ()) $
writeByteStringOutput mOutFile hexKeyHash

runNodeKeyHashBlsCmd
:: ()
=> Cmd.NodeKeyHashBLSCmdArgs
-> CIO e ()
runNodeKeyHashBlsCmd
Cmd.NodeKeyHashBLSCmdArgs
{ vkeySource
, mOutFile
} = do
vkey <-
readVerificationKeyOrFile vkeySource

let hexKeyHash = serialiseToRawBytesHex (verificationKeyHash vkey)

fromEitherIOCli @(FileError ()) $
writeByteStringOutput mOutFile hexKeyHash

runNodeIssuePopBLSCmd
:: ()
=> Cmd.NodeIssuePopBLSCmdArgs
-> CIO e ()
runNodeIssuePopBLSCmd
Cmd.NodeIssuePopBLSCmdArgs
{ blsSkeyFile
, outFile
} = do
skey <-
fromEitherIOCli @(FileError TextEnvelopeError) $
readFileTextEnvelope @(SigningKey BlsKey) blsSkeyFile

let pop = createBlsPossessionProof skey

fromEitherIOCli @(FileError ()) $
writeLazyByteStringFile outFile $
textEnvelopeToJSON Nothing pop

runNodeNewCounterCmd
:: ()
=> Cmd.NodeNewCounterCmdArgs
Expand Down
Loading
Loading