pallet_evm_precompile_ed25519/
lib.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19#![warn(unused_crate_dependencies)]
20
21extern crate alloc;
22
23use alloc::vec::Vec;
24
25use ed25519_dalek::{Signature, Verifier, VerifyingKey};
26use fp_evm::{ExitError, ExitSucceed, LinearCostPrecompile, PrecompileFailure};
27
28pub struct Ed25519Verify;
29
30impl LinearCostPrecompile for Ed25519Verify {
31	const BASE: u64 = 15;
32	const WORD: u64 = 3;
33
34	fn execute(input: &[u8], _: u64) -> Result<(ExitSucceed, Vec<u8>), PrecompileFailure> {
35		if input.len() < 128 {
36			return Err(PrecompileFailure::Error {
37				exit_status: ExitError::Other("input must contain 128 bytes".into()),
38			});
39		};
40
41		let mut i = [0u8; 128];
42		i[..128].copy_from_slice(&input[..128]);
43
44		let mut buf = [0u8; 4];
45
46		let msg = &i[0..32];
47		let pk = VerifyingKey::try_from(&i[32..64]).map_err(|_| PrecompileFailure::Error {
48			exit_status: ExitError::Other("Public key recover failed".into()),
49		})?;
50		let sig = Signature::try_from(&i[64..128]).map_err(|_| PrecompileFailure::Error {
51			exit_status: ExitError::Other("Signature recover failed".into()),
52		})?;
53
54		// https://docs.rs/rust-crypto/0.2.36/crypto/ed25519/fn.verify.html
55		if pk.verify(msg, &sig).is_ok() {
56			buf[3] = 0u8;
57		} else {
58			buf[3] = 1u8;
59		};
60
61		Ok((ExitSucceed::Returned, buf.to_vec()))
62	}
63}
64
65#[cfg(test)]
66mod tests {
67	use super::*;
68	use ed25519_dalek::{Signer, SigningKey};
69
70	#[test]
71	fn test_empty_input() -> Result<(), PrecompileFailure> {
72		let input: [u8; 0] = [];
73		let cost: u64 = 1;
74
75		match Ed25519Verify::execute(&input, cost) {
76			Ok((_, _)) => {
77				panic!("Test not expected to pass");
78			}
79			Err(e) => {
80				assert_eq!(
81					e,
82					PrecompileFailure::Error {
83						exit_status: ExitError::Other("input must contain 128 bytes".into())
84					}
85				);
86				Ok(())
87			}
88		}
89	}
90
91	#[test]
92	fn test_verify() -> Result<(), PrecompileFailure> {
93		#[allow(clippy::zero_prefixed_literal)]
94		let secret_key_bytes: [u8; ed25519_dalek::SECRET_KEY_LENGTH] = [
95			157, 097, 177, 157, 239, 253, 090, 096, 186, 132, 074, 244, 146, 236, 044, 196, 068,
96			073, 197, 105, 123, 050, 105, 025, 112, 059, 172, 003, 028, 174, 127, 096,
97		];
98
99		let keypair = SigningKey::from_bytes(&secret_key_bytes);
100		let public_key = keypair.verifying_key();
101
102		let msg: &[u8] = b"abcdefghijklmnopqrstuvwxyz123456";
103		assert_eq!(msg.len(), 32);
104		let signature = keypair.sign(msg);
105
106		// input is:
107		// 1) message (32 bytes)
108		// 2) pubkey (32 bytes)
109		// 3) signature (64 bytes)
110		let mut input: Vec<u8> = Vec::with_capacity(128);
111		input.extend_from_slice(msg);
112		input.extend_from_slice(&public_key.to_bytes());
113		input.extend_from_slice(&signature.to_bytes());
114		assert_eq!(input.len(), 128);
115
116		let cost: u64 = 1;
117
118		match Ed25519Verify::execute(&input, cost) {
119			Ok((_, output)) => {
120				assert_eq!(output.len(), 4);
121				assert_eq!(output[0], 0u8);
122				assert_eq!(output[1], 0u8);
123				assert_eq!(output[2], 0u8);
124				assert_eq!(output[3], 0u8);
125			}
126			Err(e) => {
127				return Err(e);
128			}
129		};
130
131		// try again with a different message
132		let msg: &[u8] = b"BAD_MESSAGE_mnopqrstuvwxyz123456";
133
134		let mut input: Vec<u8> = Vec::with_capacity(128);
135		input.extend_from_slice(msg);
136		input.extend_from_slice(&public_key.to_bytes());
137		input.extend_from_slice(&signature.to_bytes());
138		assert_eq!(input.len(), 128);
139
140		match Ed25519Verify::execute(&input, cost) {
141			Ok((_, output)) => {
142				assert_eq!(output.len(), 4);
143				assert_eq!(output[0], 0u8);
144				assert_eq!(output[1], 0u8);
145				assert_eq!(output[2], 0u8);
146				assert_eq!(output[3], 1u8); // non-zero indicates error (in our case, 1)
147			}
148			Err(e) => {
149				return Err(e);
150			}
151		};
152
153		Ok(())
154	}
155}