Skip to content

Commit 5575b9c

Browse files
committed
Add support for BLS possession proof
Make `show` and `pretty` for `BlsPossessionProof` produce something that evaluates to the actual term Add test for `show` instance of `BlsPossessionProof` Improve explanation of minSigPopPContext and DST in comments
1 parent 78fb9c5 commit 5575b9c

5 files changed

Lines changed: 128 additions & 0 deletions

File tree

cardano-api/cardano-api.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ test-suite cardano-api-test
416416
Test.Cardano.Api.Json
417417
Test.Cardano.Api.KeysByron
418418
Test.Cardano.Api.Ledger
419+
Test.Cardano.Api.Leios
419420
Test.Cardano.Api.Metadata
420421
Test.Cardano.Api.Ord
421422
Test.Cardano.Api.Orphans

cardano-api/src/Cardano/Api/Key.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ module Cardano.Api.Key
7272

7373
-- | BLS12-381 key type
7474
, BlsKey
75+
, BlsPossessionProof
76+
, blsPossessionProof
77+
, createBlsPossessionProof
7578

7679
-- ** Type proxy
7780
, HasTypeProxy (..)

cardano-api/src/Cardano/Api/Key/Internal/Leios.hs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ module Cardano.Api.Key.Internal.Leios
1717
, Hash (..)
1818
, VerificationKey (..)
1919
, SigningKey (..)
20+
21+
-- * Possession proof
22+
, BlsPossessionProof
23+
, blsPossessionProof
24+
, createBlsPossessionProof
2025
)
2126
where
2227

@@ -35,6 +40,7 @@ import Cardano.Crypto.DSIGN.Class qualified as Crypto
3540
import Cardano.Crypto.Hash.Class qualified as Crypto
3641
import Cardano.Ledger.Hashes (HASH)
3742

43+
import Data.ByteString (ByteString)
3844
import Data.Either.Combinators (maybeToRight)
3945
import Data.String (IsString (..))
4046

@@ -149,3 +155,80 @@ instance HasTextEnvelope (SigningKey BlsKey) where
149155

150156
textEnvelopeDefaultDescr :: SigningKey BlsKey -> TextEnvelopeDescr
151157
textEnvelopeDefaultDescr _ = "BLS12-381 signing key"
158+
159+
-- | BlsPossessionProof is used in the Leios protocol to prove ownership of a BLS signing key
160+
-- when registering a BLS verification key for a stake pool. This is required to prevent malicious
161+
-- actors from registering a BLS verification key for a stake pool without actually owning the
162+
-- corresponding signing key.
163+
newtype BlsPossessionProof = BlsPossessionProof (Crypto.PossessionProofDSIGN Crypto.BLS12381MinSigDSIGN)
164+
deriving stock Eq
165+
deriving newtype (ToCBOR, FromCBOR)
166+
deriving anyclass SerialiseAsCBOR
167+
168+
instance Show BlsPossessionProof where
169+
show p = "blsPossessionProof " ++ show (serialiseToRawBytesHex p)
170+
171+
instance Pretty BlsPossessionProof where
172+
pretty p = "blsPossessionProof" <+> pretty (serialiseToRawBytesHexText p)
173+
174+
instance SerialiseAsRawBytes BlsPossessionProof where
175+
serialiseToRawBytes (BlsPossessionProof proof) =
176+
Crypto.rawSerialisePossessionProofDSIGN proof
177+
178+
deserialiseFromRawBytes AsBlsPossessionProof bs =
179+
maybeToRight (SerialiseAsRawBytesError "Unable to deserialise BlsPossessionProof") $
180+
BlsPossessionProof <$> Crypto.rawDeserialisePossessionProofDSIGN bs
181+
182+
-- | Construct a 'BlsPossessionProof' from a hex-encoded raw 'ByteString'.
183+
--
184+
-- This is a partial function that calls 'error' if the input is not valid.
185+
-- It is intended to be used with the output of 'show' or 'pretty' to
186+
-- reconstruct a 'BlsPossessionProof' value.
187+
blsPossessionProof :: ByteString -> BlsPossessionProof
188+
blsPossessionProof hexBs =
189+
case deserialiseFromRawBytesHex hexBs of
190+
Left e -> error $ "blsPossessionProof: " ++ show e
191+
Right p -> p
192+
193+
-- | Signing context including the Domain Separation Tag (DST) for the proofs-of-possession of
194+
-- BLS keys using the minimal-signature-size BLS12-381 variant.
195+
--
196+
-- A Domain Separation Tag is a unique tag (like a magic number) that we add to ensure that
197+
-- the signature is used only in the context that it was intended for.
198+
-- This is because BLS keys and signatures can be used for multiple purposes, and
199+
-- we don't want a proof of possession for one purpose to be interpreted as something different
200+
-- in a different context.
201+
minSigPoPContext :: Crypto.BLS12381SignContext
202+
minSigPoPContext = Crypto.BLS12381SignContext (Just minSigPoPDST) Nothing
203+
204+
-- TODO: This is a provisional definition. Import @minSigPoPDST@ from
205+
-- @Cardano.Crypto.DSIGN.BLS12381@ (cardano-crypto-class) when
206+
-- IntersectMBO/cardano-base#635 is merged and the dependency is bumped.
207+
minSigPoPDST :: ByteString
208+
minSigPoPDST = "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_POP_"
209+
210+
-- | Create a proof of possession for a BLS signing key.
211+
--
212+
-- This proof demonstrates that the holder of a BLS verification key knows the corresponding
213+
-- secret key, which is required before the key can safely participate in signature aggregation.
214+
-- Without this proof, an attacker could register a crafted verification key that cancels out
215+
-- honest participants' keys during aggregation (a rogue key attack).
216+
createBlsPossessionProof :: SigningKey BlsKey -> BlsPossessionProof
217+
createBlsPossessionProof (BlsSigningKey sk) =
218+
BlsPossessionProof (Crypto.createPossessionProofDSIGN minSigPoPContext sk)
219+
220+
instance HasTypeProxy BlsPossessionProof where
221+
data AsType BlsPossessionProof = AsBlsPossessionProof
222+
proxyToAsType _ = AsBlsPossessionProof
223+
224+
instance HasTextEnvelope BlsPossessionProof where
225+
textEnvelopeType :: AsType BlsPossessionProof -> TextEnvelopeType
226+
textEnvelopeType _ =
227+
"BlsPossessionProof_"
228+
<> fromString (Crypto.algorithmNameDSIGN proxy)
229+
where
230+
proxy :: Proxy Crypto.BLS12381MinSigDSIGN
231+
proxy = Proxy
232+
233+
textEnvelopeDefaultDescr :: BlsPossessionProof -> TextEnvelopeDescr
234+
textEnvelopeDefaultDescr _ = "BLS12-381 possession proof"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
3+
module Test.Cardano.Api.Leios
4+
( tests
5+
)
6+
where
7+
8+
import Cardano.Api
9+
( AsType (..)
10+
, BlsPossessionProof
11+
, blsPossessionProof
12+
, createBlsPossessionProof
13+
, serialiseToRawBytesHex
14+
)
15+
16+
import Test.Gen.Cardano.Api.Typed (genSigningKey)
17+
18+
import Hedgehog (Property, forAll, property, (===))
19+
import Test.Tasty (TestTree, testGroup)
20+
import Test.Tasty.Hedgehog (testProperty)
21+
22+
tests :: TestTree
23+
tests =
24+
testGroup
25+
"Cardano.Api.Leios"
26+
[ testProperty "prop_show_blsPossessionProof_roundtrip" prop_show_blsPossessionProof_roundtrip
27+
]
28+
29+
-- | Property: @show proof == "blsPossessionProof " ++ show (serialiseToRawBytesHex proof)@,
30+
-- and the hex fed to 'blsPossessionProof' reconstructs the same value.
31+
prop_show_blsPossessionProof_roundtrip :: Property
32+
prop_show_blsPossessionProof_roundtrip = property $ do
33+
sk <- forAll $ genSigningKey AsBlsKey
34+
let proof :: BlsPossessionProof
35+
proof = createBlsPossessionProof sk
36+
hex = serialiseToRawBytesHex proof
37+
expected = "blsPossessionProof " ++ show hex
38+
show proof === expected
39+
blsPossessionProof hex === proof

cardano-api/test/cardano-api-test/cardano-api-test.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import Test.Cardano.Api.IO qualified
2222
import Test.Cardano.Api.Json qualified
2323
import Test.Cardano.Api.KeysByron qualified
2424
import Test.Cardano.Api.Ledger qualified
25+
import Test.Cardano.Api.Leios qualified
2526
import Test.Cardano.Api.Metadata qualified
2627
import Test.Cardano.Api.Ord qualified
2728
import Test.Cardano.Api.RawBytes qualified
@@ -61,6 +62,7 @@ tests =
6162
, Test.Cardano.Api.Json.tests
6263
, Test.Cardano.Api.KeysByron.tests
6364
, Test.Cardano.Api.Ledger.tests
65+
, Test.Cardano.Api.Leios.tests
6466
, Test.Cardano.Api.Metadata.tests
6567
, Test.Cardano.Api.Ord.tests
6668
, Test.Cardano.Api.RawBytes.tests

0 commit comments

Comments
 (0)