@@ -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 )
2126where
2227
@@ -35,6 +40,7 @@ import Cardano.Crypto.DSIGN.Class qualified as Crypto
3540import Cardano.Crypto.Hash.Class qualified as Crypto
3641import Cardano.Ledger.Hashes (HASH )
3742
43+ import Data.ByteString (ByteString )
3844import Data.Either.Combinators (maybeToRight )
3945import 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"
0 commit comments