const_crypto/ed25519/
mod.rs1use sha2_const_stable::Sha256;
17
18mod choice;
19mod field_element;
20
21use choice::Choice;
22use field_element::FieldElement;
23
24#[derive(Copy, Clone, Eq, PartialEq, Hash)]
35#[repr(C)]
36pub struct CompressedEdwardsY(pub [u8; 32]);
37
38#[derive(Copy, Clone)]
39#[repr(C)]
40#[allow(non_snake_case)]
41pub struct EdwardsPoint {
42 pub(crate) X: FieldElement,
43 pub(crate) Y: FieldElement,
44 pub(crate) Z: FieldElement,
45 pub(crate) T: FieldElement,
46}
47
48pub const fn crypto_unsafe_is_on_curve(key: &[u8; 32]) -> bool {
51 let (is_valid_y_coord, _, _, _) = decompress_step_1(key);
52
53 is_valid_y_coord.into()
56}
57
58const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
59
60pub const fn derive_program_address(seeds: &[&[u8]], program: &[u8; 32]) -> ([u8; 32], u8) {
61 let mut bump = u8::MAX;
62
63 loop {
64 let mut hasher = Sha256::new();
65
66 let mut i = 0;
67 while i < seeds.len() {
68 hasher = hasher.update(seeds[i]);
69 i += 1;
70 }
71 hasher = hasher.update(&[bump]);
72
73 hasher = hasher.update(program);
75 hasher = hasher.update(PDA_MARKER);
76
77 let candidate = hasher.finalize();
78
79 if !crypto_unsafe_is_on_curve(&candidate) {
81 return (candidate, bump);
82 }
83
84 bump -= 1;
86 }
87}
88
89#[derive(Clone)]
90pub struct PartialPda {
91 inner: Sha256,
92}
93
94impl PartialPda {
95 pub const fn from_partial_preimage(partial_preimage: &[&[u8]]) -> PartialPda {
96 let mut inner = Sha256::new();
97 let mut i = 0;
98
99 while i != partial_preimage.len() {
100 inner = inner.update(partial_preimage[i]);
101 i += 1
102 }
103
104 PartialPda { inner }
105 }
106
107 pub const fn finalize_with(
108 self,
109 remaining_seeds: &[&[u8]],
110 program: &[u8; 32],
111 ) -> ([u8; 32], u8) {
112 let mut bump = u8::MAX;
113
114 loop {
115 let mut hasher: Sha256 = unsafe { core::mem::transmute_copy(&self.inner) };
117
118 let mut i = 0;
119 while i < remaining_seeds.len() {
120 hasher = hasher.update(remaining_seeds[i]);
121 i += 1;
122 }
123 hasher = hasher.update(&[bump]);
124
125 hasher = hasher.update(program);
127 hasher = hasher.update(PDA_MARKER);
128
129 let candidate = hasher.finalize();
130
131 if !crypto_unsafe_is_on_curve(&candidate) {
133 return (candidate, bump);
134 }
135
136 bump -= 1;
138 }
139 }
140}
141#[rustfmt::skip] pub(super) const fn decompress_step_1(
143 repr: &[u8; 32],
144) -> (Choice, FieldElement, FieldElement, FieldElement) {
145 let y = FieldElement::from_bytes(repr);
146 let z = FieldElement::ONE;
147 let yy = y.square();
148 let u = yy.sub(z); let v = yy.mul(FieldElement::EDWARDS_D).add(z); let (is_valid_y_coord, x) = FieldElement::sqrt_ratio_i(u, v);
151
152 (is_valid_y_coord, x, y, z)
153}
154
155#[test]
156fn test_on_curve() {
157 fn safe_is_on_curve(key: &[u8; 32]) -> bool {
158 curve25519_dalek::edwards::CompressedEdwardsY::from_slice(key.as_ref())
159 .unwrap()
160 .decompress()
161 .is_some()
162 }
163
164 for _ in 0..50_000 {
165 let bytes = rand::random::<[u8; 32]>();
166 assert_eq!(crypto_unsafe_is_on_curve(&bytes), safe_is_on_curve(&bytes));
167 }
168}
169
170#[test]
171fn test_with_partial_preimage() {
172 for _ in 0..50_000 {
173 let first = rand::random::<[u8; 32]>();
174 let second = rand::random::<[u8; 32]>();
175
176 let program = [0; 32];
177
178 let direct = derive_program_address(&[&first, &second], &program);
179 let via_parial =
180 PartialPda::from_partial_preimage(&[&first]).finalize_with(&[&second], &program);
181
182 assert_eq!(direct, via_parial)
183 }
184}