Prove sub#44
Conversation
|
|
||
| #[verifier::external_body] | ||
| pub fn select(x: &u64, y: &u64, c: Choice) -> (res: u64) | ||
| ensures boolify(c) ==> res == x, |
There was a problem hiding this comment.
The version of this on main (which I wrote) is wrong 🤦♂️
I've flipped it to be correct. And I've changed the bool to be an enum with similar names to https://docs.rs/subtle/latest/subtle/trait.ConditionallySelectable.html#tymethod.conditional_select, so now it's easier to compare to that doc and see that it's the right way around
| // subtract l if the sum is >= l | ||
| proof { lemma_l_value_properties(&constants::L, &sum); } | ||
| assert(to_nat(&sum.limbs) >= 0); | ||
| assume(to_nat(&sum.limbs) < 2 * group_order()); |
There was a problem hiding this comment.
I've added this assume in add so it can call sub. I think this can be propagated back to become another precondition for add
There was a problem hiding this comment.
assert(to_nat(&sum.limbs) >= 0);
Isn't this a tautology based on types? to_nat returns a nat, which is always >= 0
|
|
||
| // a - b | ||
| let mut borrow: u64 = 0; | ||
| assert(seq_u64_to_nat(a.limbs@.subrange(0, 0 as int)) - seq_u64_to_nat(b.limbs@.subrange(0, 0 as int )) == |
There was a problem hiding this comment.
I've moved most of the proof of sub into lemmas, but I've left in the invariants and a few asserts. I could probably squish a few more asserts into lemmas, although some of them are needed to get Verus to recognize that a lemma has the right preconditions
| seq_to_nat(limbs@.map(|i, x| x as nat)) | ||
| } | ||
|
|
||
| pub open spec fn seq_u64_to_nat(limbs: Seq<u64>) -> nat |
There was a problem hiding this comment.
Just another wrapper around seq_to_nat
| assert(0x000d63c715bea69fu64 < (1u64 << 52)) by (bit_vector); | ||
| } | ||
|
|
||
| pub proof fn lemma_seq_u64_to_nat_subrange_extend(seq: Seq<u64>, i: int) |
There was a problem hiding this comment.
I asked Claude to do this and fed it a very similar proof from https://github.com/Beneficial-AI-Foundation/dafny-bignums, but it couldn't---maybe it didn't realize how similar Dafny and Verus are? I did get it to do most of the base case though
| assert(a as nat == (a >> 52) as nat * pow2(52) + (a & mask) as nat); | ||
| } | ||
|
|
||
| pub proof fn lemma_sub_loop1_invariant(difference: Scalar52, borrow: u64, i: usize, a: &Scalar52, b: &Scalar52, old_borrow: u64, mask: u64, difference_loop1_start: Scalar52) |
There was a problem hiding this comment.
I haven't put much effort into cutting unnecessary asserts from these lemmas
| // Without the following condition, all we can prove is something like: | ||
| // to_nat(&a.limbs) >= to_nat(&b.limbs) ==> to_nat(&s.limbs) == to_nat(&a.limbs) - to_nat(&b.limbs), | ||
| // to_nat(&a.limbs) < to_nat(&b.limbs) ==> to_nat(&s.limbs) == (to_nat(&a.limbs) - to_nat(&b.limbs) + pow2(260) + group_order()) % (pow2(260) as int), | ||
| // In the 2nd case, `sub` doesn't always do subtraction mod group_order |
There was a problem hiding this comment.
It took me several tries to get the spec right
Kukovec
left a comment
There was a problem hiding this comment.
LGMT, though I'm curious if Claude can comment its code if asked; some of the lemmas would be a bit tough to read through manually if uncommented.
| // subtract l if the sum is >= l | ||
| proof { lemma_l_value_properties(&constants::L, &sum); } | ||
| assert(to_nat(&sum.limbs) >= 0); | ||
| assume(to_nat(&sum.limbs) < 2 * group_order()); |
There was a problem hiding this comment.
assert(to_nat(&sum.limbs) >= 0);
Isn't this a tautology based on types? to_nat returns a nat, which is always >= 0
| // to_nat(&a.limbs) >= to_nat(&b.limbs) ==> to_nat(&s.limbs) == to_nat(&a.limbs) - to_nat(&b.limbs), | ||
| // to_nat(&a.limbs) < to_nat(&b.limbs) ==> to_nat(&s.limbs) == (to_nat(&a.limbs) - to_nat(&b.limbs) + pow2(260) + group_order()) % (pow2(260) as int), | ||
| // In the 2nd case, `sub` doesn't always do subtraction mod group_order | ||
| -group_order() <= to_nat(&a.limbs) - to_nat(&b.limbs) < group_order(), |
There was a problem hiding this comment.
How come this is necessary though?
If a and b are bounded, then to_nat(a) < order and a - b <= a, similar for b and -order.
There was a problem hiding this comment.
limbs_bounded(a) guarantees that to_nat(a) < 2^260, but group_order ~ 2^252. So I tried requiring 0 <= a < group_order && 0 <= b < group_order, but add passes in values sometimes larger than group_order. I think add's call to sub satisfies 0 <= a < 2 * group_order && 0 <= b < 2 * group_order, which is simpler than the precondition I went with, but that precondition is too loose for sub to always be correct
Also montgomery_reduce calls sub, and I don't yet understand it well enough to know if it satisfies sub's preconditions.
There was a problem hiding this comment.
In that case, I'd consider relaxing this requirement, but changing ensures to
to_nat(&s.limbs) % (group_order() as int) == (to_nat(&a.limbs) - to_nat(&b.limbs)) % (group_order() as int),
result limbs are bounded, but may represent a value greater than the order, in which case you have to take mod p on the left too.
There was a problem hiding this comment.
The weird thing about sub is that
to_nat(&s.limbs) % (group_order() as int) == (to_nat(&a.limbs) - to_nat(&b.limbs)) % (group_order() as int),
isn't always true. If to_nat(&a.limbs) < to_nat(&b.limbs), we get
to_nat(&s.limbs) == (to_nat(&a.limbs) - to_nat(&b.limbs) + pow2(260) + group_order()) % (pow2(260) as int),
And then if to_nat(&a.limbs) - to_nat(&b.limbs) < -group_order, this simplifies to
to_nat(&s.limbs) == to_nat(&a.limbs) - to_nat(&b.limbs) + pow2(260) + group_order(),
And mod group_order, we get
s % group_order == (a - b + pow(260)) % group order != (a - b) % group order
Here's a test demonstrating this: https://github.com/Beneficial-AI-Foundation/curve25519-dalek/tree/sub_test
There was a problem hiding this comment.
Seems like there are a lot of spurious asserts, but if it's correct it's correct.
There was a problem hiding this comment.
At some point I should run verus_cleaner.py, but that script takes a while and doesn't catch everything
That's my fault; I've added some comments |
30a2274 to
55d56c8
Compare
No description provided.