Skip to content

Commit b03c76e

Browse files
authored
Introduce crypto module and expand cryptographic functions (#6837)
## Description This PR replaces #5747 and intends to introduce the crypto module. > Note: #6832 will also use the crypto module. The std-lib currently contains the `ecr.sw` and `vm/evm/ecr.sw` files which have the following functions: - ec_recover() - ec_recover_r1() - ed_verify() - ec_recover_address() - ec_recover_address_r1() - ec_recover_evm_address() There are a number of issues with this including no type safety for signatures from different elliptic curves, functions split across multiple files, poor naming, and generic arguments. All of these are resolved by this PR which deprecates both `ecr.sw` files and replaces them with a crypto module which syntactically matches Rust. The following new types are introduced: * `PublicKey` - An Asymmetric public key, supporting both 64 and 32-byte public keys * `Message` - Hashed message authenticated by a signature type that handles variable lengths * `Secp256k1` - A secp256k1 signature * `Secp256r1` - A secp256r1 signature * `Ed25519` - An ed25519 signature * `Signature` - An ECDSA signature All original functionality is retained with the new module: * `Secp256k1::recover()` - Recovers a public key. * `Secp256r1::recover()` - Recovers a public key. * `Secp256k1::address()` - Recovers an address. * `Secp256r1::address()` - Recovers an address. * `Secp256k1::evm_address()` - Recovers an EVM address. * `Ed25519::verify()` - Verify that a signature matches the given public key. The following new functionality has been added: * `Secp256k1::verify()` - Verify that a signature matches the given public key. * `Secp256r1::verify()` - Verify that a signature matches the given public key. * `Secp256k1::verify_address()` - Verify that a signature matches the given address. * `Secp256r1::verify_address()` - Verify that a signature matches the given address. * `Secp256k1::verify_evm_address()` - Verify that a signature matches the given EVM address. * `Secp256r1::verify_evm_address()` - Verify that a signature matches the given EVM address. * `Secp256r1::evm_address()` - Recovers an EVM address. The following functions have been deprecated: - `std::ecr::ec_recover()` - `std::ecr::ec_recover_r1()` - `std::ecr::ed_verify()` - `std::ecr::ec_recover_address()` - `std::ecr::ec_recover_address_r1()` - `std::vm::evm::ecr::ec_recover_evm_address()` Example of changes for recovering a public key: ```sway // Before fn foo(signature: B512, message: b256) { let recovered_public_key: B512 = ec_recover(signature, message).unwrap(); } // After fn bar(signature: Signature, message: Message) { let recovered_public_key: PublicKey = signature.recover(message).unwrap(); } ``` Example of changes for recovering an Address: ```sway // Before fn foo(signature: B512, message: b256) { let recovered_address: Address = ec_recover_address(signature, message).unwrap(); } // After fn bar(signature: Signature, message: Message) { let recovered_address: Address = signature.address(message).unwrap(); } ``` Complete recovery example using the `Signature` type: ```sway use std::crypto::{Message, PublicKey, Secp256r1, Signature}; fn foo() { let secp256r1_signature = Signature::Secp256r1(Secp256r1::from(( 0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac, 0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d, ))); let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323); // A recovered public key pair. let secp256r1_public_key = secp256r1_signature.recover(signed_message); assert(secp256r1_public_key.is_ok()); assert( secp256r1_public_key .unwrap() == PublicKey::from(( 0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2, 0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452, )), ); } ``` ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] If my change requires substantial documentation changes, I have [requested support from the DevRel team](https://github.com/FuelLabs/devrel-requests/issues/new/choose) - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers.
1 parent e025355 commit b03c76e

File tree

26 files changed

+4266
-16
lines changed

26 files changed

+4266
-16
lines changed

docs/book/src/blockchain-development/hashing_and_cryptography.md

+47-3
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,54 @@ The Sway standard library provides easy access to a selection of cryptographic h
88
{{#include ../../../../examples/hashing/src/main.sw}}
99
```
1010

11-
## Signature Recovery
11+
## Cryptographic Signature Recovery and Verification
12+
13+
Fuel supports 3 asymmetric cryptographic signature schemes; `Secp256k1`, `Secp256r1`, and `Ed25519`.
14+
15+
### Public Key Recovery
16+
17+
Given a `Signature` and a sign `Message`, you can recover a `PublicKey`.
18+
19+
```sway
20+
{{#include ../../../../examples/signatures/src/main.sw:public_key_recovery}}
21+
```
22+
23+
### Signed Message Address Recovery
24+
25+
Given a `Signature` and signed `Message`, you can recover a Fuel `Address`.
1226

1327
```sway
14-
{{#include ../../../../examples/signatures/src/main.sw}}
28+
{{#include ../../../../examples/signatures/src/main.sw:address_recovery}}
1529
```
1630

17-
> **Note**: Recovery of EVM addresses is also supported via `std::vm::evm`.
31+
#### Signed Message EVM Address Recovery
32+
33+
Recovery of EVM addresses is also supported.
34+
35+
```sway
36+
{{#include ../../../../examples/signatures/src/main.sw:evm_address_recovery}}
37+
```
38+
39+
### Public Key Signature Verification
40+
41+
Given a `Signature`, `PublicKey`, and `Message`, you can verify that the message was signed using the public key.
42+
43+
```sway
44+
{{#include ../../../../examples/signatures/src/main.sw:signature_verification}}
45+
```
46+
47+
### Address Signature Verification
48+
49+
Given a `Signature`, `Address`, and `Message`, you can verify that the message was signed by the address.
50+
51+
```sway
52+
{{#include ../../../../examples/signatures/src/main.sw:address_verification}}
53+
```
54+
55+
#### EVM Address Signature Verification
56+
57+
Recovery of EVM addresses verification is also supported.
58+
59+
```sway
60+
{{#include ../../../../examples/signatures/src/main.sw:evm_address_verification}}
61+
```

examples/signatures/src/main.sw

+209-13
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,218 @@
11
script;
22

3-
use std::{b512::B512, ecr::{ec_recover, ec_recover_address, EcRecoverError}};
3+
fn main() {}
44

5-
const MSG_HASH = 0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323;
5+
use std::{
6+
crypto::{
7+
ed25519::*,
8+
message::*,
9+
public_key::*,
10+
secp256k1::*,
11+
secp256r1::*,
12+
signature::*,
13+
},
14+
hash::{
15+
Hash,
16+
sha256,
17+
},
18+
vm::evm::evm_address::EvmAddress,
19+
};
620

7-
fn main() {
8-
let hi = 0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c;
9-
let lo = 0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d;
10-
let signature: B512 = B512::from((hi, lo));
21+
fn public_key_recovery() {
22+
// ANCHOR: public_key_recovery
23+
// Secp256rk1 Public Key Recovery
24+
let secp256k1_signature: Signature = Signature::Secp256k1(Secp256k1::from((
25+
0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
26+
0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
27+
)));
28+
let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
29+
// A recovered public key pair.
30+
let secp256k1_public_key = secp256k1_signature.recover(signed_message);
31+
assert(secp256k1_public_key.is_ok());
32+
assert(
33+
secp256k1_public_key
34+
.unwrap() == PublicKey::from((
35+
0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
36+
0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
37+
)),
38+
);
1139

40+
// Secp256r1 Public Key Recovery
41+
let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
42+
0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
43+
0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
44+
)));
45+
let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
1246
// A recovered public key pair.
13-
let public_key = ec_recover(signature, MSG_HASH);
47+
let secp256r1_public_key = secp256r1_signature.recover(signed_message);
48+
assert(secp256r1_public_key.is_ok());
49+
assert(
50+
secp256r1_public_key
51+
.unwrap() == PublicKey::from((
52+
0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
53+
0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
54+
)),
55+
);
56+
// ANCHOR_END: public_key_recovery
57+
}
1458

59+
fn address_recovery() {
60+
// ANCHOR: address_recovery
61+
// Secp256k1 Address Recovery
62+
let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
63+
0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
64+
0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
65+
)));
66+
let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
1567
// A recovered Fuel address.
16-
let result_address: Result<Address, EcRecoverError> = ec_recover_address(signature, MSG_HASH);
17-
if let Ok(address) = result_address {
18-
log(address.bits());
19-
} else {
20-
revert(0);
21-
}
68+
let secp256k1_address = secp256k1_signature.address(signed_message);
69+
assert(secp256k1_address.is_ok());
70+
assert(
71+
secp256k1_address
72+
.unwrap() == Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881),
73+
);
74+
75+
// Secp256r1 Address Recovery
76+
let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
77+
0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
78+
0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
79+
)));
80+
let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
81+
// A recovered Fuel address.
82+
let secp256r1_address = secp256r1_signature.address(signed_message);
83+
assert(secp256r1_address.is_ok());
84+
assert(
85+
secp256r1_address
86+
.unwrap() == Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a),
87+
);
88+
89+
// ANCHOR_END: address_recovery
90+
}
91+
92+
fn evm_address_recovery() {
93+
// ANCHOR: evm_address_recovery
94+
// Secp256k1 EVM Address Recovery
95+
let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
96+
0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
97+
0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
98+
)));
99+
let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
100+
// A recovered EVM address.
101+
let secp256k1_evm_address = secp256k1_signature.evm_address(signed_message);
102+
assert(secp256k1_evm_address.is_ok());
103+
assert(
104+
secp256k1_evm_address
105+
.unwrap() == EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb),
106+
);
107+
108+
// Secp256r1 EVM Address Recovery
109+
let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
110+
0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
111+
0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
112+
)));
113+
let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
114+
// A recovered EVM address.
115+
let secp256r1_evm_address = secp256r1_signature.evm_address(signed_message);
116+
assert(secp256r1_evm_address.is_ok());
117+
assert(
118+
secp256r1_evm_address
119+
.unwrap() == EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a),
120+
);
121+
// ANCHOR_END: evm_address_recovery
122+
}
123+
124+
fn signature_verification() {
125+
// ANCHOR: signature_verification
126+
// Secp256k1 Signature Verification
127+
let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
128+
0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
129+
0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
130+
)));
131+
let secp256k1_public_key = PublicKey::from((
132+
0x41a55558a3486b6ee3878f55f16879c0798afd772c1506de44aba90d29b6e65c,
133+
0x341ca2e0a3d5827e78d838e35b29bebe2a39ac30b58999e1138c9467bf859965,
134+
));
135+
let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
136+
// A verified public key
137+
let secp256k1_verified = secp256k1_signature.verify(secp256k1_public_key, signed_message);
138+
assert(secp256k1_verified.is_ok());
139+
140+
// Secp256r1 Signature Verification
141+
let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
142+
0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac,
143+
0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d,
144+
)));
145+
let secp256r1_public_key = PublicKey::from((
146+
0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2,
147+
0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452,
148+
));
149+
let signed_message = Message::from(0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323);
150+
// A verified public key
151+
let secp256r1_verified = secp256r1_signature.verify(secp256r1_public_key, signed_message);
152+
assert(secp256r1_verified.is_ok());
153+
154+
// Ed25519 Signature Verification
155+
let ed25519_public_key = PublicKey::from(0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10);
156+
let ed25519_signature = Signature::Ed25519(Ed25519::from((
157+
0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545,
158+
0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00,
159+
)));
160+
let hashed_message = Message::from(sha256(b256::zero()));
161+
// A verified public key
162+
let ed25519_verified = ed25519_signature.verify(ed25519_public_key, hashed_message);
163+
assert(ed25519_verified.is_ok());
164+
// ANCHOR_END: signature_verification
165+
}
166+
167+
fn address_verification() {
168+
// ANCHOR: address_verification
169+
// Secp256k1 Address Verification
170+
let secp256k1_address = Address::from(0x02844f00cce0f608fa3f0f7408bec96bfd757891a6fda6e1fa0f510398304881);
171+
let secp256k1_signature = Secp256k1::from((
172+
0x61f3caf4c0912cec69ff0b226638d397115c623a7f057914d48a7e4daf1cf6d8,
173+
0x2555de81cd3a40382d3d64eb1c77e463eea5a76d65ec85f283e0b3d568352678,
174+
));
175+
let signed_message = Message::from(0xa13f4ab54057ce064d3dd97ac3ff30ed704e73956896c03650fe59b1a561fe15);
176+
// A verified address
177+
let secp256k1_verified = secp256k1_signature.verify_address(secp256k1_address, signed_message);
178+
assert(secp256k1_verified.is_ok());
179+
180+
// Secp256r1 Address Verification
181+
let secp256r1_address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a);
182+
let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
183+
0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c,
184+
0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
185+
)));
186+
let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
187+
// A verified address
188+
let secp256r1_verified = secp256r1_signature.verify_address(secp256r1_address, signed_message);
189+
assert(secp256r1_verified.is_ok());
190+
191+
// ANCHOR_END: address_verification
192+
}
193+
194+
fn evm_address_verification() {
195+
// ANCHOR: evm_address_verification
196+
// Secp256k1 Address Verification
197+
let secp256k1_evm_address = EvmAddress::from(0x0000000000000000000000000ec44cf95ce5051ef590e6d420f8e722dd160ecb);
198+
let secp256k1_signature = Signature::Secp256k1(Secp256k1::from((
199+
0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c,
200+
0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d,
201+
)));
202+
let signed_message = Message::from(0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323);
203+
// A recovered EVM address.
204+
let secp256k1_verified = secp256k1_signature.verify_evm_address(secp256k1_evm_address, signed_message);
205+
assert(secp256k1_verified.is_ok());
206+
207+
// Secp256r1 Address Verification
208+
let secp256r1_evm_address = EvmAddress::from(0x000000000000000000000000408eb2d97ef0beda0a33848d9e052066667cb00a);
209+
let secp256r1_signature = Signature::Secp256r1(Secp256r1::from((
210+
0x62CDC20C0AB6AA7B91E63DA9917792473F55A6F15006BC99DD4E29420084A3CC,
211+
0xF4D99AF28F9D6BD96BDAAB83BFED99212AC3C7D06810E33FBB14C4F29B635414,
212+
)));
213+
let signed_message = Message::from(0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563);
214+
// A recovered EVM address.
215+
let secp256r1_verified = secp256r1_signature.verify_evm_address(secp256r1_evm_address, signed_message);
216+
assert(secp256r1_verified.is_ok());
217+
// ANCHOR_END: evm_address_verification
22218
}

sway-lib-std/src/crypto.sw

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
library;
2+
3+
pub mod signature_error;
4+
pub mod message;
5+
pub mod public_key;
6+
pub mod ed25519;
7+
pub mod secp256k1;
8+
pub mod secp256r1;
9+
pub mod signature;

0 commit comments

Comments
 (0)