LeanSig KeyGen & API
PQ Ethereum Workshop 2025 · 6 Oct 2025 · 57:58
A session from day 3 of the PQ Ethereum Workshop, held in Cambridge, UK on 6 October 2025 as part of lean week 2025 and hosted by the Ethereum Foundation. The workshop brought researchers, cryptographers and client developers together on post-quantum signatures, proof systems and devnet planning for Ethereum.
Topics
From PQ Ethereum Workshop 2025, hosted by Ethereum Foundation.
Transcript
My goal for this session would be first to give you sort of a a reminder of how how leans looks like and what the current API is that we sort of expose to the clients and uh then we can discuss about how to change it to make it more flexible and to make it more useful for the clients. Uh as a sort of quick reminder in the beginning how does how does leanick work? It follows this XMSS approach where you have a a tree of one-time public keys. So the leaves in this merkeler tree are onetime public keys and these onetime public keys are just a bunch of in the concrete case of positive field elements or a bunch of hashes. So uh we have a bunch of hash chains and the the ends of these hash
chains are our one time public key and then we have the starting points of these hash chains which are our our one-time secret key and all together here could be treated as the secret key. But of course you can uh this is a lot of a lot of uh data. So think of this tree as being very very large 2 to the 32 leaves. Um then of course you don't want to store all of these uh one-time secret keys. You just expand them from a PRF seat using any sort of PRF. Um, and I guess the question that I've already mentioned uh on on Saturday is that there's s sort of a trade-off between uh signing and and memory like
signing uh time and and memory. uh you can either store the entire mercle tree so you would not store the leaves that's that's even more data but like you can store just the PRF seed but then you can either store the entire mercle tree that would give you extremely fast signing and that is what's currently implemented uh but of course the secret key is now large and we will see how large in in a second um the other option or the other extreme is to to regenerate the tree when you are signing this is extremely slow uh but it would few small keys and now the question is how do we uh balance between these two things how do we make this flexible uh for the clients so what I want to talk about is sort of this
interface between the signature library and any lean consensus client so uh ideally we want to encapsulate as much cryptography as possible in this signature library and not make the clients deal with the cryptography internals um but we still needed to be flexible. Okay, so uh let's look at the current API what sort of the signature library gives us. Uh again there's this sort of tree with say 2 to the 32 leaves um where each leaf is the hash actually of a one-time public key and then it has three interfaces as a standard signature scheme has three functions. It has a key
gen function that outputs a public key and a secret key. has a sign function and it has a verifi verify function. Okay. And the first uh kind of unusual thing is that we take as input for sign and verify these epochs and epoch is a bit of a a misuse here. So uh think more like slot. Uh so epoch is just the pointing to the to the leaf. Um, okay. And I think the most important takeaway whenever you use this library in terms of security, uh, and I can't stress this too much is, uh, this is sort of what you need to ensure if you want to get security. You never call this function twice with the same secret key epoch pair. Okay, this is sort of
the one restriction that you don't have in a standard signature scheme, but in this scheme, you have it. This is very important. Okay, now let's look at at key genen a bit more closely. So it takes us input these two parameters. Uh first it didn't but now it does. What do these parameters mean? So it could be that you you're a client and you say okay I don't want to have really a lifetime of 2 to the 32 uh slots. Maybe that's too long for me and it takes too long to generate the key. Maybe I just want to be uh active for this period in time. Okay.
Okay. So what you would specify when you call key genen, you specify a pointer to this uh to this first uh epoch. Let me just call it epoch but think slots. Uh and then you specify for how long do I want to be active and then what the key gen function internally does it it generates this sort of sparse mer tree where actually only this part is generated and then the rest is filled up with with uh random nodes so that you can have copaths. Um okay so how long does this take? So key genen is really the slowest algorithm in this whole thing. Uh it takes uh linear and this all of this is times the time that it takes to evaluate a hash function.
Linear in the number of active epochs plus some log l um log l because you need to complete this here. So if you go for like uh 2 to the 32 you really want to have the full lifetime. That's really really slow and that would take hours or even days I think days probably. Um but maybe if you you know my validator lives for 500 years then maybe generating a key for two days is probably okay. How is that?
How is for keyen is it faster because I still need to compute those uh onification nodes. >> What if I run a key gen for this sub tree? >> You say >> yes. >> And then uh I still need to produce the public key that will >> this one. >> Yes. >> Yeah. So you you generate this this tree on every layer you at mo you need to pad it sometimes but you add at most two nodes that are that are sort of artificial nodes >> and then you compute the next layer and so on. Ah, those are not fair keys.
>> This this this is not filled. This is sort of a sparse mer tree. So that uh this one is actually filled and these ones you extend them using your PRFC >> and then to make sure that you can for all of those compute the Merkel path you have some random nodes that sort of just log element. >> Yeah, makes sense. >> Demetri. >> Yeah. Um maybe a little comment on this uh never call twice. >> Maybe that sounds a little bit scary particular when testing. Can we say rather never publish twice?
>> If it makes sense for >> that's a I mean never publish twice. What does that mean? If I let's say I I call this I call it twice and then I take the second one and somehow use it to compute something else. If I land then leak that something else I can make no guarantee about security. So what the security model that we have says is never call this twice. >> Well we have already called it technically to to generate the key right. So you have already used the secret key to generate all the chains.
So technically you cannot say that you >> uh never called this function twice. Sure you can call this function but like >> what is the repercussion of calling it twice? >> You would leak parts of your secret key. You you cannot you can call it twice under the same message. But I think a easier statement you cannot you cannot because there's a randomness going on. >> But is it PR PF randomness? >> You mean the the the row the randomness? >> Well, can it be deterministic the randomness?
>> You take a secret seed, you take some iterator, you take the message and this is this will be not a pure randomness but a pure randomness. Then >> this could work. Yeah. >> So, >> but still I mean never publish twice is just not a well specified thing because what does it mean to publish the result of that? If I now take it, I remove a bit and I publish it. Is that publishing or not? >> Yes. Well, we're not purists here, but >> Oh, I am. This reminds me a bit of ECDSA story, right? That like when you never >> you never reuse the nons, right?
>> Yeah. And then like we had like Sony that did that and the PlayStation. So if there's any anything we can avoid situation of link if there's any any anything that can be done. >> Yeah. I mean for me this is the thing that needs to be clearly documented and I think right now it is. >> Maybe if there is any chance to make the library stateful just to encapsulate this requirement. >> Oh sure sure. Okay, we can make the secret key. Uh then you need to Okay, then you need to make uh this function so that you can this function can write to the secret key right?
>> Yes. >> This is maybe coming to the future API. This may be a problem in terms of >> like so some epoch whenever you sign it. >> Yeah. Then the secret key would store I have signed that already. And then when you call it a second time, it would just abort. That is a good way. But then this function needs to have right access to the secret key which is maybe a problem in uh in for the future API that has multiple functions taking the secret key.
>> Not sure about secret key. Just have to mark epoch as as used. >> Where do you mark it? >> Yeah, I mean >> somewhere. Yeah. In the client, right? >> Yeah. >> So, so this this current API, which I'm not saying we should keep this. I'm just saying this is the current API. Uh this current API says the client needs to keep track of what it has signed and what it has not signed. >> So, right now, each client has their own anti-slashing mechanism. Um, but I'm wondering if we could kind of simplify things for clients and just do anti-lashing directly in the signature library. Um, I'm also thinking could we have like defense in depth where we have like anti- slashing in the library and
anti- slashing in the client >> or or maybe adding a new parameter known on on the signing you had another parameter. >> What would that help if you have nons >> or or randomness? >> Why would that help? >> Hopefully people don't pass the randomness twice. The same randomness twice. But if they use the same epoch even with a different message, that's still a security. >> That's the core problem to use the same epoch with the different message. If it is, if we're just signing the same message, I think we can make it produce the same data.
But if it will produce a new signature uh under the same epoch, that's the problem. Yeah. So, so if you sign with the same secret key, same epoch, same message >> currently in the current implementation that's a problem because signing is probabilistic. If we now make >> Well, I didn't finish my question yet. The question is, would you get the same signature both times? >> Currently not. >> So, this is not a pure function. There's some additional state. It's an algorithm. >> The output randomized depends on >> No, there's there's no state in this function. It's it's just a randomized algorithm. So, so if there's no other state that it depends on besides those three parameters, then it must >> randomness generator.
>> Okay. So, so what is the randomness generator >> that is passed as input to this thing correctly. >> Okay. That's an additional parameter. It's not written there. >> Yeah, that's what I meant. I mean, you can if you pass the now you're using the randomness inside, right? So, you produce different value. But if you're passing the random as an input, >> yeah, but then you don't solve that problem. If someone takes a different message and calls this, then still a security violation. >> Anyway, I think we should not like spend too much time on this. I think there's a more pressing problem. Uh I think uh I agree with Justin that we should try to to ensure this as good as we can ideally
in both the library and the clients and uh yeah we we can can do that. Okay. Okay, but coming back to key gen. Okay, so key genen takes that much time. There's no way to improve this. So with this design, with this scheme, we have to pay the price of a of a large uh key gen time. Okay. Now what about the secret key? Currently the secret key is the seed and the entire tree. Why did we put the entire tree in the secret key? Because we want fast signing.
Okay. What is the size of this tree? It's this. So it's uh linear in the number of active epochs. Then you have some two times log L and that many hashes is what you have. And the hash is 31 byt. So it's 38 field elements and each field element is 31 bit. Um okay so this can actually go to like very very high number of gigabytes. So if you have the full tree that's 2 to the 32 leaves that means the tree has about 2 to the 33 nodes uh then this term is zero like there's no additional thing on top and that times so 2 to the
32 uh 33 and that times about 30 uh 32 so that's 2 to the 38 many bytes and that's I think something like no that's even 250 56 GB. So it's even more okay if you populate the full thing. And with the current implementation, we even would have to have that in run. So this uh not doable currently. Um okay. So this is the current API and I think the main challenge if we now want to change this API is we want to have give the client flexibility. The clients should be able to decide how much memory they want to give to the to the signature
library or at least to some extent. Uh they should be able to decide when to store something on disk and read it later or when to sort of uh regenerate parts of the tree. But at the same time, we want that most of the cryptography is really encapsulated in that library. and clients should ideally use this library as a black box and don't look into the specifics of how this signature scheme works. Okay, so here's a proposal and I already can tell you I'm not even 100% happy with that proposal. It's like a one way to do it. I'm happy if there are any other suggestions that that are better
or if we can play around with it. This is sort of a starting point. Okay, so let's start with key genen. Keygen would take the same inputs, but now it would output three things. The public key, the secret key, and some auxiliary value that you have initially. Think of this as being the full tree or the partial tree or empty. We could even maybe have like a parameter here that says do you want to uh keep keep the full tree in this auxiliary auxiliary value or do you want to have some partial tree or should it be empty and each such auxiliary value which will show up a bunch of times in other functions should have some way to to tell us in which interval it is valid.
So for which interval of the tree we can use it to sign. Okay. Okay. And then we can sort of play around with that. Let me come back to that in a second. And then when we sign, we not only pass the secret key, which is now just the PRF seed. Uh we also pass any auxiliary value that we currently have. And see, and this is still fast assuming that the epoch is in that interval specified by that auxiliary value. So think of for simplicity think of this auxiliary value as being all co-paths that you would need when you want to sign in this interval.
Okay. And then you could either write it to disk or throw it away and recomputee it later uh or just store it in run whatever you want depending on the size. And then there of course need to be some ways to play around with these cops. So for example, if you decide to throw it away after key generation, how do you get it back because you need it to sign? Well, we could have a function that takes any auxiliary value which could be empty and sort of extends its interval. So it would internally uh compute more mer paths and or like populate the tree uh more.
And maybe we don't even need this full this in full generality. Maybe this function next ox is is sufficient where we just say okay I want to be prepared for the next epoch and then I call this uh in some in some thread way before I have to sign for that slot and I and then I'm prepared and then in that slot I can can uh sign very fast and if we do that then we can actually implement nice tree traversal algorithms for merkel trees that sort of have some certain uh caching behavior and that that have a good trade-off between memory and and uh time. But in the worst case, this time for playing around with these parts of
the tree with the auxiliary value can take as long as key gen. I think Mike corrected me. It's like half as long as keychain in the in the worst case. But >> maybe it's maybe not. >> If you go from the left sub tree to the right sub tree like this sort of takes long and then clients would need to decide when to call this whether to store this in memory and and read it or whether to throw it away and uh and so on. Yeah. So this is one way to do it. Verify would be the same of course. Uh yeah >> I would add here an important thing is um if we take in the signing this uh if if we take in the signing this extra
input. >> Yes. So essentially what this extra input somehow contains intermediate notes of Merkel tree. You can do it different ways and um the idea is that the signing then when it will take this given intermediate note has to check whether the signature will will be validated afterwards. >> Uh it's not only only that it's interval. The problem is if your extra input was corrupted due to misuse of the computer or adversarily or whatever the problem what will happen if you produce an invalid signature because you will get punished probably somehow yeah in
the protocol and so uh the requirement here will be okay I take those values I put them but I will do the same process as validator. This is still faster but I need to check that uh this extra auxiliary input was not >> yeah I mean I think it's in general a good idea that the signing algorithm before it outputs the signature checks that it's valid. >> Yeah. >> Uh that again some sort of hard mechanism. >> Yeah.
>> Yeah. So if we keyen uh if we run call the keyun and get uh auxiliary output uh this activation output but it's the parameters are related to the a and b the get interval. >> Yeah. So that good question I think I did not decide that in that way of presenting it because it's I say either full tree or partial tree or empty. Uh let's say we go for full tree then the interval AB would be a equals activation epoch b equals activation epoch plus num active epochs minus one or something. uh but we could even decide that we have either multiple keygen functions where one outputs the full tree, one outputs
partial tree or we would have uh just a parameter given to that key gen function that says okay I'm a client I want to generate a key I want to keep track of the full tree and full tree here means this sparse tree like not the two to the 32 um or I say I'm a client I want to keep track of nothing because I want to be very efficient. I want to not have that much memory and then keygen could just not output anything. Uh yeah, so this is you can always get back these auxiliary values by calling these functions. I'm not even sure if it's 10 ox is the best way to call it like yeah so so uh we would have some functions
that the secret key and give us a new uh part of the tree that we want. Um I wonder if uh clients do need this sort of extra flexibility. Wouldn't it be okay if we just provide a single optimization scenario where we like cut the tree in half so that eventually what you have store and use is just the square root of the total tree size. Yeah, that's kind of the only option that you guys have because maybe if we look at this closer maybe it should be okay like for everyone so that they don't even bother with selecting parameters and so on because for example uh here uh we probably when we
kind of allow given arbitrary parameters we'll have to say oh well if this uh things is within sub tree then it's fast if it's kind of goes to the next sub tree it's super slow and client starts kind of thinking this extra information about the details of the tree maybe kind of just one variant yeah I I agree and I think that also what I thought first uh I think there's maybe one caveat that is if you have a client that generates only a very sparse a very small part of the tree then why would why would that client uh want to have this sort of split it up I mean if that client could just keep
the entire tree, right? >> If it's small, then uh and these two use this trick, then kind of regenerating this sub trees every square root of the size is not a big deal to them anymore, >> right? But if it's small, then why would they even need to regenerate it? Why? They they could just use the current version. >> Yeah, they could. But if even if they regenerate, it won't harm them terribly, >> right? I mean there's also the question, do you regenerate it or do you store it on disk and and load it? No, >> you you uh you store the top and you generate the bottoms, >> right? But you could also instead of
generating the bottoms, put them into I don't know two to the like some number of files, one file for each thing and >> yeah, I agree. There can be tons of different optimizations. I just wonder maybe just one single variant is okay. So they don't >> right. Yeah, I I agree. I I would I would really from the perspective of developing this library, I would really love the most simple API that where I don't need to have all these functions and think about what could happen. Now >> a question like which one is like the public key that will go on chain or whether or not they >> the root the mer route.
>> So the public key is this one. I mean it's a slightly lying here. It's the merkel root plus a public parameter which is five field elements. So the public key is uh eight field elements plus five field elements 13 field elements that go on chain which should be around 60 60 bytes or something. Yeah.
>> So, uh if we use the new propos key contains the tree uh or so the initial ox does the same thing as the tree in the secret key. Right. >> Right. Think of this this pair as being what currently is the secret key. >> Yeah. And now it's it's harder to to give that like coming back to this never call the thing twice. Now it's even harder because now we have more functions that take the secret key and now these functions if they are implemented correctly you can call them as often as you want. They should not leak anything about the secret key. Um whereas this function would still have the same
restrictions as before. >> Yeah. >> Um so uh I want to suggest a slightly different way of doing this that I think might help. I'm wondering if it's been considered. So, um it seems to me like the the situation with these keys is that you need to um like the the the the the more time you want to spend um you know offline between uh using your key and refreshing your key, the bigger the key has to be. Um and so, for example, with the current idea, you could be offline for two to the 32 slots. uh before you have to come online to to
refresh your key. Um now uh it it seems to me like maybe you could uh replace this idea of um one leaf per slot uh with one leaf per instance of signing. Um so for validator nodes which uh sign I assume once per slot it wouldn't make a difference in terms of you know how how frequently you'd need to refresh the key related to the signing but validator nodes are like online most of the time as far as I understand and so they could plausibly have a smaller key than two to the 32 leaves because they're online and
they can they can you know presumably refresh fresh their key at some frequency. And then for wallets, you know, some people are going to want to have their wallets offline for a long time. Um, but they but then they're not going to be signing in every slot. Um, and so and so >> what do you mean wallets? I mean, >> well, this scheme is just for for consensus layer for validators. >> Okay. Okay. Um so so >> for like execution layer we would not use a stateful scheme or like >> so the so the transactions are going to be signed with what type of signature >> I don't know I was assuming they would be signed with this kind as well but I can then restrict my comment to just the validator nodes >> right and maybe to like where is it
ah maybe this so the reason why we we allow that sort of the party that wants to generate two to the key for two to the 10 uh slots that that we allow that this interval starts here in the middle of the tree and not I mean we could have said okay the I want two to the 10 trees then I always generate this left most leftmost part but then the problem is that you have multiple uh validators that sign and you want to aggregate those then the paths that they take in the tree are different and if we do it like this then they always if you are in slot I then the path party who is aggregating knows okay slot I means this is the path that I follow and so the at least from I from what I understand
proving proving this during aggregation is much more uh it's much easier if they have sort of the same if they take the same path in the same slot >> I think the bigger reason has to do with uh slashing right so you might want to slash like very old messages and here you need to have in the state all of the the pup keys So you want immutable pub keys. So is is is the idea that you refresh the pup key maybe the idea. >> No, I was just saying why why do we have uh an activation epoch and why not activation epoch always?
>> Oh yeah, that's just for simplicity. >> Yeah. Yeah. >> I also think um for now the secret key contains just the seat and maybe extra information. But with this key gen the secret key should also contain uh the range. >> It does. Yeah. >> Okay. Because if technically this this sign first checks if epoch is in the range. >> Okay. Yes. Okay. >> Yeah. Yeah, I simplified a bunch of things on the slides, but like yeah, >> one thing that could be helpful is knowing the performance of keychain uh and not just the performance today, but also the predicted performance. My
understanding is that we're not using CMD right now. >> That is correct. >> So we could get like a free 8x basically improvement. >> Um and if I remember correctly, we were doing like one day of key gen per second or per minute. Um >> I don't >> per second. I think >> that metric I just know like >> it was like 8 years and it took few hours but I I mean that's hard to come up with a number now >> right so 8 years times 365 yeah so >> yeah a few thousand um so I think it was like roughly one day per second um and
now per second if we multiply it by 8x that's like eight days per second. So I guess I'm wondering if we go with the very the super simple approach of of Mitri's approach which I I like. I mean I want this to be simple. I I guess the question with the simple approach is more to the clients. Would that be something that you could use or would that be not flexible enough? Yes. So, do you do you uh recall what Dimitri's approach was? Maybe we can write it down on the board or something.
>> Um, can I ask a question while we're getting ready for that? Why why why do we need these keys to last so long? What is the tradeoff if we just make those smaller and refresh them more frequently? So, I as I understand the current currently the only way to refresh a key would be to exit as your validator and then join as a new validator >> and I'm not sure if people if people would want to do that every year >> sometime sometimes like the queue it's really long so can be like days maybe now not but there's been days that like the queue to like go on and do any deposit or consolidate might take long >> okay >> the execute is 40 right now.
>> Yeah. >> Is there any any way we could have a mechanism for validators to just renew their their memberships? >> It's on chain. So, >> this goes back to the slashing comment. You want to be able to slash arbitrarily past statistications. Um, and if you've refreshed the the root, then you can't slash anymore because the oldest station was relative to a route that is no longer stored in state. Okay. So basically in this simple
optimiz in this simple optimizations don't worry uh suppose we have a tree of size to the L and basically we we chop the tree into two layers basically and we store the top L over two layers which in our case is most of the 16 and uh the part we are the sub tree of uh height 16 which is again to the L / two size we regenerate and this to store is again to the 16. So basically when we are in the sub tree we we keep it and when we reach
the step we just regenerate next ones but since it's 2 to the 16 it's not really much and the total amount of memory you need is >> like 2 megabytes I think for the top three >> probably yes 16 * 13 2 >> 13 is to the One hash one hash is >> 302. >> Oh yeah. Yeah.
>> 2. So that's 2 to the 21 by that's like two 2. That's okay to have that in. >> That's okay. one >> just one I mean if you have one validator one at least right >> and then the question is do you want to regenerate this or do you want to store it and and load it from disk and I think we could even support both options maybe we can give you the freedom to choose >> so that would be the um so to 16 slots
would be the equivalent of 3 days which would take less than 1 second uh with the CND optimization. So >> right so so you have an interval of 3 days and >> during that time you whenever you want you can generate for the next interval of 3 days. >> Yeah I mean there's also the super simple logic is like you literally reach the very end slot of that three days and then it you take it takes one second to generate the next three days. So you don't even need to pre-generate necessarily because it's so fast, >> right? But I guess it would be good to give clients the choice when they >> they have the choice.
>> Okay. So maybe if there's if people think this is okay, maybe we can like uh write down the API for this method and then we can implement it after the >> Yeah. Sorry. Uh yeah just small question like once validator decided to update their key do they need to sign it with uh this schema? I mean if it's one time signature then >> what do you mean with update the key?
>> I mean well >> public key >> uh yeah >> no I updating the updating the public key I think the only way is to just leave as a validator but and then exit. But >> but how do I signal that I want to leave? I need to sign some message or something. >> Same way you do it right now. >> But do I need to sign it with this key because I can't because it's one time signature per slot or I do it with some another kind of key. >> I mean, we could reserve the last slot for the exit message or something. >> Yeah, but I don't want to exit on the last slot. I want to do to be able to do this uh in the middle. For example, I suspect that my keys are compromised and I want to update. Okay. But then just
for that slot where you want to exit, sign a message that says I want to exit. But don't sign anything else. >> Yes. >> Is that okay? >> That's okay. Yeah. I'm just saying u I >> mean you can choose what you want to sign, right? I >> Yeah. >> The signature library doesn't tell you what the >> Yeah. As long as it is okay for me not to sign attestation for this slot, but instead sign the message that I want to leave. If it's okay for everyone that if if we don't see any problem with that, then yeah, >> I see. you before you got the slots for blocks that you're signing now your leave message you need to control yourself that you're not reusing the slot
>> right but I mean why not just this is just about what what do we sign I mean we could just say okay if in in one slot I want to sign the block and I want to leave then we just define find a special message format that has these two components and then you sign the hash. >> Yeah. Yeah. But this this is just another careful u just not to reuse the >> right. Right. But I think this should not be part of the signature library logic. This should be part of the client logic.
>> Yeah. >> Uh yeah. So uh in this in this setup so we only have to sign uh once with the same app. So uh there in practical there's a uh quite a multiple domains to sign for example block provider should sign to block a tester should sign to a test a test agent. So uh is there any um advice or recommendation for uh the multiple domains?
>> Yeah I think that's a good question. Uh I don't have a perfect answer for that but I there are multiple options. One option is to have two keys probably not the thing that we want to do because it like doubles size of all keys that we need to store. Uh the other option is to pack these two things that you want to sign in one message. That of course only works if you sign at the same time and not in two different two different times. Probably that works for the proposal. I'm not sure. >> Yeah, it does. Uh I think that's the simplest one. Then there's the other thing where we sort of cut virtually cut the lifetime in half and we say like every even odd is for being a prop every even leave is for being a proposer every
odd one but then we really cut the lifetime in half which we probably don't want >> inefficient that >> yeah so I think without thinking too much about it I think the packing two or multiple messages into one message that you then sign uh would be the >> but uh does block proposer sign uh foremost of the of testation in this current B contain setup. So uh you mean that packing means this should publish at the same time, right? Um there's only one message which is the the block message that you're signing and within the block message there's an attestation message. All right, >> which is on site.
I guess I have a sorry I have a very basic question for maybe for Justin. The question is do we want to get rid of the notion of withdrawal keys and uh validator signing keys? >> Yes, please. >> Um so we would um I guess only have a withdrawal address which is an E1 address. Um uh so when when you withdraw, you withdraw, it's just basically a pointer to where you want the funds to go. Um >> so that's a question.
>> I guess this ties into the exit message debate we just had. So >> yeah, >> right now we can trigger exits from the execution layer. >> And so yeah, so there >> yes, that would be actually a very clean solution where you just trigger your exit from the execution layer and then we don't have to worry about signing a withdrawal message from the consensus layer. >> This is now possible, right? >> This is now it's now possible. Yeah. >> Yeah, probably that's the thing.
>> That's the cleanest. Yeah. Yeah. >> And it it keeps the consensus layer super minimal on >> right. >> I think someone said that if we'll >> I can't remember who said that if we decide now the the beacon chain will not add the withdrawal key. So I know it was saying in some backlog sometimes you're right. >> Yeah makes sense. But okay, just to clarify, >> could you trigger a withdrawal from the consensus layer >> like with this because then you would obviously have to use your uh your post quantum key as part of the scheme.
>> Um we we can do it either way. Um so you know one thing that Benedict said is you know we could say okay if the epoch is you know two of 32 then that has a special meaning it means that it's the withdrawal uh message that you're signing. >> There's one problem with that approach is that if you generate a tree that looks like this but now you also need to generate uh the two to the 32 slot >> right >> now your tree will not be of that form. So you have bunch of intermediate nodes and then I'm not even sure if that in the worst case could be again a very long key generation time, >> right?
>> I'm not entirely sure but like I could imagine scenarios where this would lead to a long key generation time. >> Remind me again how long are we expecting those two how long are we expecting those two to 32 keys to last? Over 100 years, 500 years, something like that. >> Yeah, >> because that's like what? 4 million signatures. 4 billion signatures. >> We do this. Okay. >> Yeah. 4 second slots. You can >> 544 years.
>> Okay. I think if you just want 10 years, you can do something like 2 to 28 or something. But >> yeah, >> I think the nice thing about this, so first I was I was skeptical about this uh like why do we need that long key lifetime? Why just make it 2 to the 28? But with this now every client can or every validator can decide what they want and 2 to the 32 is also kind of nice. 32 is a power of two is like really we give maximum flexibility and it's not a weird number like 2 to the 28. Why did we pick that? And then if we at some point go from 4 second slots to even less seconds then we are prepared you know
>> is there a way to enforce at the API level the fact that this is impossible to >> Yeah we we were discussing that before. I think uh there are ways I'm not happy with either of them. I mean you could you could store in the secret key a bit vector that says this is what I've already signed then the problem is that this function needs right access to the secret key which is probably okay but harder to deal with if you are uh if you do things concurrently in the client like I want to call two functions that use the secret key for example one that regenerates the next part of the tree um at the same time and they both eat right access you could have an ex a
separate sort of mask that goes in there. But then who controls that this mask is correct? That's again logic of the client. >> So you you don't need a mask. Uh much simpler approach is just to to store the last signed epoch the last used epoch and then just enforce that the epoch number must increase. >> Right. But I think it would have the same problems. >> It's just simpler than the >> Sure. Okay. Simpler implementation but same same problems that you have right this function needs right access to the secret key >> which makes it actually stateful this API is not stateful >> can we say this here I mean >> yeah this slide >> no no this sign things I mean as far I'm with that like the problem is like that
you are if you sign twice you get two different signatures right >> for the same one time >> exactly secret key public key there. >> Exactly. So can you just having kind of instead some deterministic as you do like in ECDSA we do like for example for for for >> Yeah. But if you use this if you use it okay so this the problem the problem there are two problems. First you sign twice with the same message. This one you can solve like you said what also Mike said before you make the currently the thing is randomized. We make it pseudo random. So if you do the if you sign uh and this pseudo random function can be even non snark friendly right we don't care it's on the verifier uh also an easy change we we should do that for
sure so that if you input twice the same message you're fine but if you now assign twice with different messages of course this will produce different pseudo random coins for this algorithm and so you're screwed right because signing two messages with this one time signature that is sitting in the leaf can lead to a break. I can try to explain if you >> Yeah, please. >> Okay, so the remember that the the scheme has this form where we have these hash chains.
Let's try to make it simple. I use four chains of length three. Okay, so this lowest layer is our one-time secret key for this epoch. This is our onetime public key. Now let's say I uh call this and my message there's an encoding step happening. But let's now identify the encoding of the message. Let's say my message my first message is 0 0 1 one. So it's this this this this would be my signature. Okay. Now I sign a second message which is 1 one 0 0. Oh, you can see.
>> Okay. Do you see the chains at least or should I redraw them? >> Thank you. Okay. So that's the chains, right? And the message corresponds to a vector of positions in these chains via the encoding. But like let's say we ignore the encoding for now. So my first message is 0 0 1 1. That means I publish as part of my signature this thing, this thing, this thing and this thing. Okay.
So now adversary says okay please sign a second message for me. The second message is one 1 0 0. You cannot compute the signature for that message from that message because in this chain you would have to go back. That's why it's secure for one message. But if I as a signer reveal the signature for that message then I reveal this guy, this guy, this guy and this guy. And now you see I've revealed my entire onetime secret key. So now the adversary can sign everything he wants because he has my onetime secret key for that epoch.
So suppose that happened by mistake but I noticed that it happened to me before the rest of the world. What what should I do? Like is there like a panic function that immediately I don't know I would I would just exit from the validator set but I'm not sure if that would happen in time before someone can. But for now we don't have anything like we just have a warning the comment of the function but for now we don't have any >> right and I think the only way to to prevent this to h like strictly prevent this in the signature library is to make it stateful
and by stateful this would mean that sign writes or updates the secret key which I don't know if you want that so you can have sign be stateful but kind of in a different thread. So it doesn't block you. If you sign different things all at once kind of you're not blocking. >> Now let's say you have two functions, right? One one allows you to regenerate the next part of the tree. That means your secret key. The other function uh updates the last signed epoch in the secret key. So both of these functions now have right access to the secret key. If I call them concurrently, then wouldn't that cause problems?
>> Yeah. >> If I do it like >> I'm saying you can >> it would be possible. >> You call them concurrently. Okay. You allow them to sign quickly and then you have a different thread that's kind of remembers what signing then in a synchronous way checks if something was signed twice and then just alerts like oh panic panic alert you did something bad. >> The question is do we want that in the signature library or in the client? For me, I thought this would be the maybe. >> Well, you can use a mutex, right, to prevent current access to the state. >> You can, but it's slower.
>> It's I I guess it would be fine because signing doesn't take that long, right? Signing is extremely fast. But you can also split your secret key in kind of two parts because you will only write in the part that is uh technically our epoch control comes from the environment and so if something happens to that part is not that crucial. This is our kind of um secure mechanism double check of like that we're not calling this twice. And then you have your secret seat that you can use in key gems and stuff. And then you have your state also kind of part of the secret key, but it's just like logically split.
>> This would mean you have two inputs here, >> two secret keys, >> secret key and uh when did I sign last time? >> Yeah. >> But what's the point of this? I mean anyone can input anything, right? >> Yeah. Yeah. But you can while while while your your key gen is not conflicting with that part you can still have it as a single >> I'm saying that this doesn't make it >> you can still have it as a single input as like a secret key >> I'm saying this doesn't make it more secure >> maybe you progressively delete each part of the secret key that you use >> you can always regenerate if you want >> could still serve as a guard rail
Um once you let's say if you signed it twice someone got hold of it um >> is it only just the future um signatures that can be signed or can you also like >> just for this slot actually for >> if you sign twice for slot I >> then this doesn't affect your security in slot I + one it affects your security in slot I >> okay one one question in the code usually that is a lot recovery mechanism that if some thread fails or if something fails like things get redone like you try to to keep running. So there is the possibility that something crashes it puts again and sides again. Uh that's kind of the normal flow. So
>> but but but wouldn't uh >> I think >> you know currently the clients should have anti- slashing measures right they would they should already somehow make sure that they don't sign two different messages for the same slot right >> why why isn't that enough that was sort of the whole motivation why we said synchronized or like stateful schemes is fine for this >> okay I see the other one makes sense >> yeah know I mean I I see what you're trying to achieve I'm Not sure if if there's anything that already achieves it because if you input just a second thing that thing can come from the client. If the client is buggy that thing is buggy then this check in the signing doesn't really help
the only thing I'm thinking is well sometimes client do get slash well it's better to be slash than lose your private key. Okay. All right. Because you your your line department is okay, you will get slash if you sign twice. But maybe the client is like, okay, it's fine maybe to get slash one in 10,000 times, >> right? But it would not it would lose the secret key for this the one time secret key for this uh slot, but not for the next one. >> Well, >> which we didn't prove, but it's pretty obvious.
>> No, it is true. But the problem is if I got my uh secret key for this slot leaked, can I then be punished by some adversary that will continue signing messages and I will get punished for this? >> Yeah. So this is can be an issue because if I lose the slot for uh yeah for specific slot, one can just someone can just continue generating signatures and I will get punished punished punish punish once.
>> Yeah. Okay. And then you're exiting. >> Okay. Okay. >> Can Can you like sign like maybe 10 different blocks for the same slot? And then if you're a slasher as well and you take the same message to announce that you found found a double sign. >> Yeah. >> There's only at most one slashing per validator. >> Okay. Okay. Okay. There. That's good. >> Okay. I think we maybe one or two comments then we have to wrap up. >> Yeah. So it's not exactly about uh the signature themselves but more like the usability compared to what what I do for my own validator. So when I created my uh signing keys right I still have my withdrawal keys that are on the cold
wallet somewhere in the bank safe and I have the signing key that is um that is on my validator. What I would want potentially using this API, so sorry not this API but the next one, the one with the auxiliaries is to have the master key in cold storage only generate a given um you know interval uh but leave it in uh in the code uh like uh yeah leave the everything to generate the next interval in my code storage and just generate 10 years worth of uh of uh keys, right? and 10 years later I still have to do a a new session. And my question is purely uh in
terms of usability. What if there's a problem because I generate a new interval uh the next >> actually what you said doesn't work with the current scheme. >> Sorry, >> what you said doesn't work with the current scheme >> because sign still needs the secret key which is the one longterm >> right >> PRF seed. So you would need something like a constraint PRF or something like a more fancy cryptographic object to sort of have sub parts of that or you do it in a trivial way and your secret key is now one one PRF seed for each chunk that is also possible.
>> Okay. Um right okay I think I got it. I got that. Uh but my my question is more a usability thing. What if I generate you know an interval once and then accidentally I regenerate for the same interval? It's not going to be the same keys, right? Because there's a randomness uh aspect to this. >> Uh >> it will be the same. >> It will be the same. >> So this these two functions should be deterministic. >> Oh okay. >> Yeah. >> So where is the randomness then? >> In sign. >> Ah it's in sign >> and in key gen of course. >> Okay. Okay then that works. >> And in sign we can dandomize it which we should.
>> Okay. Thanks. >> Okay. I think we may >> one last question. Um in terms of the key size um with the current parameters that you suggest we are at 3 kilobytes for the signature size I think. >> Yes. Uh is there any any other future work on that to reduce that or is that like high like you know that's that's the number that >> probably the the disappointing answer is that if anything it will go up because uh for for snark friendliness we will have sort of uh yeah we would we think about removing some of the size optimizations that we did for example using tweakable hash functions uh changing it back to just without tweaks
because that would be easier. to to prove. So at that point it may go up slightly but >> 4 kilob >> I don't know >> hopefully it doesn't go up but I hope we can find a way without increasing signature size. >> Okay thanks a lot for the session.