Skip to content

Commit 9d6bca6

Browse files
authored
feat: Add NEP-413 message verification (#98)
- Added NEP-413 verify method to verify payload and check that the account has a Full AccessKey - Made PublicKey copiable as it is at max 64 bytes, so it should be fine to copy - Accept as reference NEP413Payload for signing - Added tests for verify method and updated example
1 parent 070a3b6 commit 9d6bca6

File tree

14 files changed

+249
-68
lines changed

14 files changed

+249
-68
lines changed

api/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ keyring = { workspace = true, features = [
5050

5151
[features]
5252
default = []
53-
ledger = ["near-ledger"]
53+
ledger = ["near-ledger", "near-api-types/ledger"]
5454
keystore = ["dep:keyring"]
5555

5656
[dev-dependencies]
Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
use near_api::Signer;
1+
use near_api::{NetworkConfig, Signer};
22

3+
use near_sandbox::config::{DEFAULT_GENESIS_ACCOUNT, DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY};
34
use openssl::rand::rand_bytes;
45

56
#[tokio::main]
67
async fn main() -> testresult::TestResult {
7-
let signer = Signer::from_seed_phrase(
8-
"fatal edge jacket cash hard pass gallery fabric whisper size rain biology",
9-
None,
10-
)?;
8+
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
9+
let network = NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
10+
11+
let signer = Signer::from_secret_key(DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY.parse()?)?;
12+
let public_key = signer.get_public_key().await?;
1113

1214
let mut nonce = [0u8; 32];
1315
rand_bytes(&mut nonce)?;
@@ -20,14 +22,20 @@ async fn main() -> testresult::TestResult {
2022
};
2123

2224
let signature = signer
23-
.sign_message_nep413(
24-
"round-toad.testnet".parse()?,
25-
signer.get_public_key().await?,
26-
payload,
27-
)
25+
.sign_message_nep413(DEFAULT_GENESIS_ACCOUNT.into(), public_key, &payload)
2826
.await?;
2927

3028
println!("Signature: {signature}");
3129

30+
let result = payload
31+
.verify(
32+
&DEFAULT_GENESIS_ACCOUNT.into(),
33+
public_key,
34+
&signature,
35+
&network,
36+
)
37+
.await?;
38+
assert!(result);
39+
3240
Ok(())
3341
}

api/src/common/send.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ impl ExecuteSignedTransaction {
129129

130130
let signed_tr = self
131131
.signer
132-
.sign(transaction, public_key.clone(), nonce, block_hash)
132+
.sign(transaction, public_key, nonce, block_hash)
133133
.await?;
134134

135135
self.transaction =
@@ -161,7 +161,7 @@ impl ExecuteSignedTransaction {
161161
.map_err(SignerError::from)?;
162162
let (nonce, hash, _) = self
163163
.signer
164-
.fetch_tx_nonce(transaction.signer_id.clone(), signer_key.clone(), network)
164+
.fetch_tx_nonce(transaction.signer_id.clone(), signer_key, network)
165165
.await
166166
.map_err(MetaSignError::from)?;
167167
self.presign_offline(signer_key, hash, nonce).await
@@ -438,7 +438,7 @@ impl ExecuteMetaTransaction {
438438
.map_err(MetaSignError::from)?;
439439
let (nonce, block_hash, block_height) = self
440440
.signer
441-
.fetch_tx_nonce(transaction.signer_id.clone(), signer_key.clone(), network)
441+
.fetch_tx_nonce(transaction.signer_id.clone(), signer_key, network)
442442
.await
443443
.map_err(MetaSignError::from)?;
444444
self.presign_offline(signer_key, block_hash, nonce, block_height)

api/src/signer/keystore.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,24 @@ impl SignerTrait for KeystoreSigner {
2222
async fn get_secret_key(
2323
&self,
2424
signer_id: &AccountId,
25-
public_key: &PublicKey,
25+
public_key: PublicKey,
2626
) -> Result<SecretKey, SignerError> {
2727
debug!(target: KEYSTORE_SIGNER_TARGET, "Searching for matching public key");
2828
self.potential_pubkeys
2929
.iter()
30-
.find(|key| *key == public_key)
30+
.find(|key| **key == public_key)
3131
.ok_or(PublicKeyError::PublicKeyIsNotAvailable)?;
3232

3333
info!(target: KEYSTORE_SIGNER_TARGET, "Retrieving secret key");
3434
// TODO: fix this. Well the search is a bit suboptimal, but it's not a big deal for now
35-
let secret = if let Ok(secret) =
36-
Self::get_secret_key(signer_id, public_key.clone(), "mainnet").await
37-
{
38-
secret
39-
} else {
40-
Self::get_secret_key(signer_id, public_key.clone(), "testnet")
41-
.await
42-
.map_err(|_| SignerError::SecretKeyIsNotAvailable)?
43-
};
35+
let secret =
36+
if let Ok(secret) = Self::get_secret_key(signer_id, public_key, "mainnet").await {
37+
secret
38+
} else {
39+
Self::get_secret_key(signer_id, public_key, "testnet")
40+
.await
41+
.map_err(|_| SignerError::SecretKeyIsNotAvailable)?
42+
};
4443

4544
info!(target: KEYSTORE_SIGNER_TARGET, "Secret key prepared successfully");
4645
Ok(secret.private_key)
@@ -84,7 +83,7 @@ impl KeystoreSigner {
8483
.filter(|(_, access_key)| {
8584
matches!(access_key.permission, AccessKeyPermission::FullAccess)
8685
})
87-
.map(|(public_key, _)| public_key.clone())
86+
.map(|(public_key, _)| *public_key)
8887
.map(|key| Self::get_secret_key(&account_id, key, &network.network_name));
8988
let potential_pubkeys: Vec<PublicKey> = join_all(potential_pubkeys)
9089
.await

api/src/signer/ledger.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,11 @@ impl SignerTrait for LedgerSigner {
137137
&self,
138138
_signer_id: AccountId,
139139
_public_key: PublicKey,
140-
payload: NEP413Payload,
140+
payload: &NEP413Payload,
141141
) -> Result<Signature, SignerError> {
142142
info!(target: LEDGER_SIGNER_TARGET, "Signing NEP413 message with Ledger");
143143
let hd_path = self.hd_path.clone();
144-
let payload = payload.into();
144+
let payload = payload.to_owned().into();
145145

146146
let signature: Vec<u8> = tokio::task::spawn_blocking(move || {
147147
let signature =
@@ -164,7 +164,7 @@ impl SignerTrait for LedgerSigner {
164164
async fn get_secret_key(
165165
&self,
166166
_signer_id: &AccountId,
167-
_public_key: &PublicKey,
167+
_public_key: PublicKey,
168168
) -> Result<SecretKey, SignerError> {
169169
warn!(target: LEDGER_SIGNER_TARGET, "Attempted to access secret key, which is not available for Ledger signer");
170170
Err(SignerError::SecretKeyIsNotAvailable)
@@ -173,14 +173,14 @@ impl SignerTrait for LedgerSigner {
173173
#[instrument(skip(self))]
174174
fn get_public_key(&self) -> Result<PublicKey, PublicKeyError> {
175175
if let Some(public_key) = self.public_key.get() {
176-
Ok(public_key.clone())
176+
Ok(*public_key)
177177
} else {
178178
let public_key = near_ledger::get_wallet_id(self.hd_path.clone())
179179
.map_err(|_| PublicKeyError::PublicKeyIsNotAvailable)?;
180180
let public_key = PublicKey::ED25519(
181181
near_api_types::crypto::public_key::ED25519PublicKey(*public_key.as_bytes()),
182182
);
183-
self.public_key.set(public_key.clone())?;
183+
self.public_key.set(public_key)?;
184184
Ok(public_key)
185185
}
186186
}

0 commit comments

Comments
 (0)