Signature layer, first bricks: canonicity closure + hash-to-scalar foundation
Canonicity pass (the layer is now closed under its own preconditions):
- sub_val_spec post carries the exact value equation
(exists beta <= 1, scVal r + scVal b = scVal a + ell*beta, with the
underflow guard beta = 1 -> scVal a < scVal b)
- add/montgomery_reduce/mul/aggregate posts all carry scVal r < ell:
canonical inputs give canonical outputs everywhere. Needed because
from_bytes_wide (hash-to-scalar) feeds Montgomery outputs into add.
Hash-to-scalar foundation (toward Scalar::from_hash / EdDSA verify):
- extraction scope + from_bytes_wide (brings constants::R); regenerated gen
- source repos carry a documented Aeneas-compat patch: the bare
`hi[4] = words[7] >> 20` extracts ill-typed at pin bf13c42e; masked
(semantic no-op, words[7] >> 20 < 2^44)
- Proofs/ScalarWideSpec.lean: R constant lemmas (R = 2^260 mod ell,
witness 2^260 = R + 255*ell) and montgomery_mul_spec, the single
Montgomery round: [r]*2^260 = [a]*[b], canonical bounded output
check-scalar.sh: 10 proof files, 11 kernel audits, all exactly
[propext, Classical.choice, Quot.sound]. Button pressed fresh: green.
2026-07-03 21:18:29 +00:00
|
|
|
{"charon_version":"0.1.212","translated":{"crate_name":"curve25519_dalek","options":{"ullbc":false,"precise_drops":false,"skip_borrowck":false,"mir":null,"rustc_args":[],"targets":[],"monomorphize":false,"monomorphize_mut":null,"start_from":["crate::backend::serial::u64::scalar::_::add","crate::backend::serial::u64::scalar::_::sub","crate::backend::serial::u64::scalar::_::mul","crate::backend::serial::u64::scalar::_::square","crate::backend::serial::u64::scalar::_::montgomery_mul","crate::backend::serial::u64::scalar::_::montgomery_square","crate::backend::serial::u64::scalar::_::montgomery_reduce","crate::backend::serial::u64::scalar::_::montgomery_invert","crate::backend::serial::u64::scalar::_::as_montgomery","crate::backend::serial::u64::scalar::_::from_montgomery","crate::backend::serial::u64::scalar::_::from_bytes_wide"],"start_from_if_exists":[],"start_from_attribute":null,"start_from_pub":false,"include":[],"opaque":[],"exclude":[],"extract_opaque_bodies":false,"translate_all_methods":false,"duplicate_defaulted_methods":true,"lift_associated_types":["*"],"hide_marker_traits":true,"remove_adt_clauses":true,"hide_allocator":true,"remove_unused_self_clauses":true,"desugar_drops":false,"ops_to_function_calls":true,"index_to_function_calls":true,"treat_box_as_builtin":true,"raw_consts":false,"unsized_strings":false,"reconstruct_fallible_operations":true,"reconstruct_asserts":true,"unbind_item_vars":true,"print_original_ullbc":false,"print_ullbc":false,"print_built_llbc":false,"print_llbc":false,"dest_dir":null,"dest_file":"/home/oho/GitClone/Claude/FormalVerification/dalek-ed25519-verified/verification/CurveScalar.llbc","no_dedup_serialized_ast":false,"format":null,"no_serialize":false,"no_typecheck":false,"no_normalize":false,"abort_on_error":false,"error_on_warnings":false,"preset":"Aeneas"},"target_information":[{"key":"x86_64-unknown-linux-gnu","value":{"target_pointer_size":8,"is_little_endian":true}}],"files":[{"id":0,"name":{"Local":"curve25519-dalek/src/backend/serial/u64/scalar.rs"},"crate_name":"curve25519_dalek","contents":"//! Arithmetic mod \\\\(2\\^{252} + 27742317777372353535851937790883648493\\\\)\n//! with five \\\\(52\\\\)-bit unsigned limbs.\n//!\n//! \\\\(51\\\\)-bit limbs would cover the desired bit range (\\\\(253\\\\)\n//! bits), but isn't large enough to reduce a \\\\(512\\\\)-bit number with\n//! Montgomery multiplication, so \\\\(52\\\\) bits is used instead. To see\n//! that this is safe for intermediate results, note that the largest\n//! limb in a \\\\(5\\times 5\\\\) product of \\\\(52\\\\)-bit limbs will be\n//!\n//! ```text\n//! (0xfffffffffffff^2) * 5 = 0x4ffffffffffff60000000000005 (107 bits).\n//! ```\n\nuse core::fmt::Debug;\nuse core::ops::{Index, IndexMut};\nuse subtle::{Choice, ConditionallySelectable};\n\n#[cfg(feature = \"zeroize\")]\nuse zeroize::Zeroize;\n\nuse crate::constants;\n\n/// The `Scalar52` struct represents an element in\n/// \\\\(\\mathbb Z / \\ell \\mathbb Z\\\\) as 5 \\\\(52\\\\)-bit limbs.\n#[derive(Copy, Clone)]\npub struct Scalar52(pub [u64; 5]);\n\nimpl Debug for Scalar52 {\n fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n write!(f, \"Scalar52: {:?}\", &self.0[..])\n }\n}\n\n#[cfg(feature = \"zeroize\")]\nimpl Zeroize for Scalar52 {\n fn zeroize(&mut self) {\n self.0.zeroize();\n }\n}\n\nimpl Index<usize> for Scalar52 {\n type Output = u64;\n fn index(&self, _index: usize) -> &u64 {\n &(self.0[_index])\n }\n}\n\nimpl IndexMut<usize> for Scalar52 {\n fn index_mut(&mut self, _index: usize) -> &mut u64 {\n &mut (self.0[_index])\n }\n}\n\n/// u64 * u64 = u128 multiply helper\n#[inline(always)]\nfn m(x: u64, y: u64) -> u128 {\n (x as u128) * (y as u128)\n}\n\nimpl Scalar52 {\n /// The scalar \\\\( 0 \\\\).\n pub const ZERO: Scalar52 = Scalar52([0, 0, 0, 0, 0]);\n\n /// Unpack a 32 byte / 256 bit scalar into 5 52-bit limbs.\n #[rustfmt::skip] // keep alignment of s[*] calculations\n pub fn from_bytes(bytes: &[u8; 32]) -> Scalar52 {\n let mut words = [0u64; 4]
|