Skip to main content

fuel_vm/interpreter/
crypto.rs

1use super::{
2    ExecutableTransaction,
3    Interpreter,
4    Memory,
5    MemoryInstance,
6    internal::{
7        clear_err,
8        inc_pc,
9        set_err,
10    },
11    memory::OwnershipRegisters,
12};
13use crate::{
14    constraints::reg_key::*,
15    error::SimpleResult,
16};
17
18use alloc::vec::Vec;
19use bn::{
20    AffineG1,
21    AffineG2,
22    Fq,
23    Fq2,
24    Fr,
25    G1,
26    G2,
27    Group,
28    Gt,
29};
30use fuel_asm::RegId;
31use fuel_crypto::{
32    Hasher,
33    Message,
34    PublicKey,
35    Signature,
36};
37use fuel_types::{
38    Bytes32,
39    Bytes64,
40    Word,
41};
42
43#[cfg(test)]
44mod tests;
45
46impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
47where
48    M: Memory,
49    Tx: ExecutableTransaction,
50{
51    pub(crate) fn secp256k1_recover(
52        &mut self,
53        a: Word,
54        b: Word,
55        c: Word,
56    ) -> SimpleResult<()> {
57        let owner = self.ownership_registers();
58        let (SystemRegisters { err, pc, .. }, _) = split_registers(&mut self.registers);
59        secp256k1_recover(self.memory.as_mut(), owner, err, pc, a, b, c)
60    }
61
62    pub(crate) fn secp256r1_recover(
63        &mut self,
64        a: Word,
65        b: Word,
66        c: Word,
67    ) -> SimpleResult<()> {
68        let owner = self.ownership_registers();
69        let (SystemRegisters { err, pc, .. }, _) = split_registers(&mut self.registers);
70        secp256r1_recover(self.memory.as_mut(), owner, err, pc, a, b, c)
71    }
72
73    pub(crate) fn ed25519_verify(
74        &mut self,
75        a: Word,
76        b: Word,
77        c: Word,
78        len: Word,
79    ) -> SimpleResult<()> {
80        let (SystemRegisters { err, pc, .. }, _) = split_registers(&mut self.registers);
81        ed25519_verify(self.memory.as_mut(), err, pc, a, b, c, len)
82    }
83
84    pub(crate) fn keccak256(&mut self, a: Word, b: Word, c: Word) -> SimpleResult<()> {
85        let owner = self.ownership_registers();
86        keccak256(
87            self.memory.as_mut(),
88            owner,
89            self.registers.pc_mut(),
90            a,
91            b,
92            c,
93        )
94    }
95
96    pub(crate) fn sha256(&mut self, a: Word, b: Word, c: Word) -> SimpleResult<()> {
97        let owner = self.ownership_registers();
98        sha256(
99            self.memory.as_mut(),
100            owner,
101            self.registers.pc_mut(),
102            a,
103            b,
104            c,
105        )
106    }
107
108    pub(crate) fn ec_operation(
109        &mut self,
110        a: Word,
111        b: Word,
112        c: Word,
113        d: Word,
114    ) -> SimpleResult<()> {
115        let owner = self.ownership_registers();
116        ec_operation(
117            self.memory.as_mut(),
118            owner,
119            self.registers.pc_mut(),
120            a,
121            b,
122            c,
123            d,
124        )
125    }
126
127    pub(crate) fn ec_pairing(
128        &mut self,
129        ra: RegId,
130        b: Word,
131        c: Word,
132        d: Word,
133    ) -> SimpleResult<()> {
134        let (SystemRegisters { pc, .. }, mut w) = split_registers(&mut self.registers);
135        let dest = &mut w[ra.try_into()?];
136        ec_pairing(self.memory.as_mut(), pc, dest, b, c, d)
137    }
138}
139
140pub(crate) fn secp256k1_recover(
141    memory: &mut MemoryInstance,
142    owner: OwnershipRegisters,
143    err: RegMut<ERR>,
144    pc: RegMut<PC>,
145    a: Word,
146    b: Word,
147    c: Word,
148) -> SimpleResult<()> {
149    let sig = Bytes64::from(memory.read_bytes(b)?);
150    let msg = Bytes32::from(memory.read_bytes(c)?);
151
152    let signature = Signature::from_bytes_ref(&sig);
153    let message = Message::from_bytes_ref(&msg);
154
155    match signature.recover(message) {
156        Ok(pub_key) => {
157            memory.write_bytes(owner, a, *pub_key)?;
158            clear_err(err);
159        }
160        Err(_) => {
161            memory.write_bytes(owner, a, [0; PublicKey::LEN])?;
162            set_err(err);
163        }
164    }
165
166    inc_pc(pc);
167    Ok(())
168}
169
170pub(crate) fn secp256r1_recover(
171    memory: &mut MemoryInstance,
172    owner: OwnershipRegisters,
173    err: RegMut<ERR>,
174    pc: RegMut<PC>,
175    a: Word,
176    b: Word,
177    c: Word,
178) -> SimpleResult<()> {
179    let sig = Bytes64::from(memory.read_bytes(b)?);
180    let msg = Bytes32::from(memory.read_bytes(c)?);
181    let message = Message::from_bytes_ref(&msg);
182
183    match fuel_crypto::secp256r1::recover(&sig, message) {
184        Ok(pub_key) => {
185            memory.write_bytes(owner, a, *pub_key)?;
186            clear_err(err);
187        }
188        Err(_) => {
189            memory.write_bytes(owner, a, [0; PublicKey::LEN])?;
190            set_err(err);
191        }
192    }
193
194    inc_pc(pc);
195    Ok(())
196}
197
198pub(crate) fn ed25519_verify(
199    memory: &mut MemoryInstance,
200    err: RegMut<ERR>,
201    pc: RegMut<PC>,
202    a: Word,
203    b: Word,
204    c: Word,
205    len: Word,
206) -> SimpleResult<()> {
207    let pub_key = Bytes32::from(memory.read_bytes(a)?);
208    let sig = Bytes64::from(memory.read_bytes(b)?);
209    let msg = memory.read(c, len)?;
210
211    if fuel_crypto::ed25519::verify(&pub_key, &sig, msg).is_ok() {
212        clear_err(err);
213    } else {
214        set_err(err);
215    }
216
217    inc_pc(pc);
218    Ok(())
219}
220
221pub(crate) fn keccak256(
222    memory: &mut MemoryInstance,
223    owner: OwnershipRegisters,
224    pc: RegMut<PC>,
225    a: Word,
226    b: Word,
227    c: Word,
228) -> SimpleResult<()> {
229    use sha3::{
230        Digest,
231        Keccak256,
232    };
233    let mut h = Keccak256::new();
234    h.update(memory.read(b, c)?);
235
236    memory.write_bytes(owner, a, *h.finalize().as_ref())?;
237
238    inc_pc(pc);
239    Ok(())
240}
241
242pub(crate) fn sha256(
243    memory: &mut MemoryInstance,
244    owner: OwnershipRegisters,
245    pc: RegMut<PC>,
246    a: Word,
247    b: Word,
248    c: Word,
249) -> SimpleResult<()> {
250    memory.write_bytes(owner, a, *Hasher::hash(memory.read(b, c)?))?;
251    inc_pc(pc);
252    Ok(())
253}
254
255fn read_g1_point_alt_bn_128(
256    memory: &MemoryInstance,
257    point_ptr: Word,
258) -> SimpleResult<G1> {
259    // Big endian required by the library
260    let arg_bytes: [u8; 2 * 32] = memory.read_bytes(point_ptr)?;
261
262    let px = Fq::from_slice(&arg_bytes[..32])
263        .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?;
264    let py = Fq::from_slice(&arg_bytes[32..64])
265        .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?;
266
267    Ok(if px == Fq::zero() && py == Fq::zero() {
268        G1::zero()
269    } else {
270        AffineG1::new(px, py)
271            .map(Into::into)
272            .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?
273    })
274}
275
276fn read_g2_point_alt_bn_128(
277    memory: &MemoryInstance,
278    point_ptr: Word,
279) -> SimpleResult<G2> {
280    // Big endian required by the library
281    let arg_bytes: [u8; 4 * 32] = memory.read_bytes(point_ptr)?;
282
283    let ay = Fq::from_slice(&arg_bytes[..32])
284        .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?;
285    let ax = Fq::from_slice(&arg_bytes[32..64])
286        .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?;
287    let by = Fq::from_slice(&arg_bytes[64..96])
288        .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?;
289    let bx = Fq::from_slice(&arg_bytes[96..128])
290        .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?;
291
292    let a = Fq2::new(ax, ay);
293    let b = Fq2::new(bx, by);
294    Ok(if a.is_zero() && b.is_zero() {
295        G2::zero()
296    } else {
297        G2::from(
298            AffineG2::new(a, b)
299                .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?,
300        )
301    })
302}
303
304pub(crate) fn ec_operation(
305    memory: &mut MemoryInstance,
306    owner: OwnershipRegisters,
307    pc: RegMut<PC>,
308    dst: Word,
309    curve_id: Word,
310    operation_type: Word,
311    points_ptr: Word,
312) -> SimpleResult<()> {
313    match curve_id {
314        0 => {
315            match operation_type {
316                // Two points addition
317                0 => {
318                    let point1 = read_g1_point_alt_bn_128(memory, points_ptr)?;
319                    let point2 = read_g1_point_alt_bn_128(
320                        memory,
321                        points_ptr
322                            .checked_add(64)
323                            .ok_or(fuel_tx::PanicReason::MemoryOverflow)?,
324                    )?;
325                    let mut output = [0u8; 64];
326                    // SAFETY: The library override the addition and is tested and
327                    // audited. Here is the code of the addition :
328                    // https://github.com/paritytech/bn/blob/63f8c587356a67b33c7396af98e065b66fca5dda/src/groups/mod.rs#L297
329                    #[allow(clippy::arithmetic_side_effects)]
330                    if let Some(sum) = AffineG1::from_jacobian(point1 + point2) {
331                        sum.x().to_big_endian(&mut output[..32]).unwrap();
332                        sum.y().to_big_endian(&mut output[32..]).unwrap();
333                    }
334                    memory.write_bytes(owner, dst, output)?;
335                }
336                // Scalar multiplication
337                1 => {
338                    let point = read_g1_point_alt_bn_128(memory, points_ptr)?;
339                    let scalar = Fr::from_slice(
340                        memory.read(
341                            points_ptr
342                                .checked_add(64)
343                                .ok_or(fuel_tx::PanicReason::MemoryOverflow)?,
344                            32u64,
345                        )?,
346                    )
347                    .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?;
348                    let mut output = [0u8; 64];
349                    // SAFETY: The library override the multiplication and is tested and
350                    // audited. Here is the code of the multiplication
351                    // : https://github.com/paritytech/bn/blob/63f8c587356a67b33c7396af98e065b66fca5dda/src/groups/mod.rs#L275
352                    #[allow(clippy::arithmetic_side_effects)]
353                    if let Some(product) = AffineG1::from_jacobian(point * scalar) {
354                        product.x().to_big_endian(&mut output[..32]).unwrap();
355                        product.y().to_big_endian(&mut output[32..]).unwrap();
356                    }
357                    memory.write_bytes(owner, dst, output)?;
358                }
359                _ => return Err(fuel_tx::PanicReason::UnsupportedOperationType.into()),
360            }
361        }
362        _ => return Err(fuel_tx::PanicReason::UnsupportedCurveId.into()),
363    }
364    inc_pc(pc);
365    Ok(())
366}
367
368pub(crate) fn ec_pairing(
369    memory: &mut MemoryInstance,
370    pc: RegMut<PC>,
371    success: &mut u64,
372    identifier: Word,
373    number_elements: Word,
374    elements_ptr: Word,
375) -> SimpleResult<()> {
376    match identifier {
377        // Optimal ate pairing / alt_bn128
378        0 => {
379            // Each element consists of an uncompressed G1 point (64 bytes) and an
380            // uncompressed G2 point (128 bytes).
381            let element_size = 128 + 64;
382            let mut elements = Vec::with_capacity(
383                usize::try_from(number_elements)
384                    .map_err(|_| fuel_tx::PanicReason::MemoryOverflow)?,
385            );
386            for idx in 0..number_elements {
387                let start_offset = elements_ptr
388                    .checked_add(
389                        idx.checked_mul(element_size)
390                            .ok_or(fuel_tx::PanicReason::MemoryOverflow)?,
391                    )
392                    .ok_or(fuel_tx::PanicReason::MemoryOverflow)?;
393                let a = read_g1_point_alt_bn_128(memory, start_offset)?;
394                let b = read_g2_point_alt_bn_128(
395                    memory,
396                    start_offset
397                        .checked_add(64)
398                        .ok_or(fuel_tx::PanicReason::MemoryOverflow)?,
399                )?;
400                elements.push((a, b));
401            }
402            *success = (bn::pairing_batch(&elements) == Gt::one()) as u64;
403        }
404        _ => return Err(fuel_tx::PanicReason::UnsupportedOperationType.into()),
405    }
406    inc_pc(pc);
407    Ok(())
408}