diff --git a/sdk/docs/source/examples.rst b/sdk/docs/source/examples.rst index 5fe011c..cd1c43d 100644 --- a/sdk/docs/source/examples.rst +++ b/sdk/docs/source/examples.rst @@ -44,6 +44,18 @@ Working with signatures >>> restored = aleo.Signature.from_string(serialized) >>> assert account.verify(restored, message) +Working with signatures using Aleo values +*********************** + +.. doctest:: + + >>> account = aleo.Account() + >>> message = '5field' + >>> signature = account.sign_value(message) + >>> serialized = str(signature) + >>> restored = aleo.Signature.from_string(serialized) + >>> assert account.verify_value(restored, message) + Calling a **transfer_public** function ************************************** diff --git a/sdk/python/test.py b/sdk/python/test.py index 8d22819..dfdb12a 100644 --- a/sdk/python/test.py +++ b/sdk/python/test.py @@ -49,6 +49,17 @@ def test_signature_verify(self): self.assertFalse(signature.verify(address, bad_message)) self.assertEqual(signature, aleo.Signature.from_string(c_signature)) + def test_signature_verify_value(self): + address = aleo.Address.from_string( + "aleo184vuwr5u7u0ha5f5k44067dd2uaqewxx6pe5ltha5pv99wvhfqxqv339h4") + c_signature = "sign1m9jrzpea7c8gdd0q7fp7pwszy6ar4du5p03aj8798c7pvwur9qqfhakcuf0xqelct6u8qylr0tkqwt46kngtg7capdlj6qeqkqevyqnavkjwgtm3t90lvxdrjjl07td0k4w5sysm7w22lfhfkqgdk690pcu5an22wssu4q6d3754cljxugdnrnccneldp79m3j5drzxs0s4sx2u5zze" + signature = aleo.Signature.from_string(c_signature) + message = "5field" + bad_message = "5u8" + self.assertTrue(signature.verify_value(address, message)) + self.assertFalse(signature.verify_value(address, bad_message)) + self.assertEqual(signature, aleo.Signature.from_string(c_signature)) + def test_account_sanity(self): private_key = aleo.PrivateKey.from_string( "APrivateKey1zkp3dQx4WASWYQVWKkq14v3RoQDfY2kbLssUj7iifi1VUQ6") @@ -65,6 +76,14 @@ def test_account_sanity(self): self.assertFalse(account.verify(signature, bad_message)) self.assertTrue(signature.verify(account.address(), message)) + message_value = "5field" + bad_message_value = "5u8" + signature_value = account.sign_value(message_value) + + self.assertTrue(account.verify_value(signature_value, message_value)) + self.assertFalse(account.verify_value(signature_value, bad_message_value)) + self.assertTrue(signature_value.verify_value(account.address(), message_value)) + def test_encrypt_decrypt_sk(self): private_key = aleo.PrivateKey.from_string( "APrivateKey1zkpJYx2NZeJYB74JHpzvQGpKneTP75Dk8dao6paugZXtCz3") diff --git a/sdk/src/account/mod.rs b/sdk/src/account/mod.rs index 63d2718..78d717d 100644 --- a/sdk/src/account/mod.rs +++ b/sdk/src/account/mod.rs @@ -92,11 +92,21 @@ impl Account { self.private_key.sign(message) } + /// Returns a signature for the given message (as an aleo value) + fn sign_value(&self, value: &str) -> anyhow::Result { + self.private_key.sign_value(value) + } + /// Verifies the signature of the given message. fn verify(&self, signature: &Signature, message: &[u8]) -> bool { signature.verify(&self.address, message) } + /// Verifies the signature of the given value. + fn verify_value(&self, signature: &Signature, value: &str) -> bool { + signature.verify_value(&self.address, value) + } + /// Decrypts a record ciphertext with a view key fn decrypt(&self, record_ciphertext: &RecordCiphertext) -> anyhow::Result { record_ciphertext.decrypt(&self.view_key) diff --git a/sdk/src/account/private_key.rs b/sdk/src/account/private_key.rs index 697f002..d3462ec 100644 --- a/sdk/src/account/private_key.rs +++ b/sdk/src/account/private_key.rs @@ -77,6 +77,11 @@ impl PrivateKey { Signature::sign(self, message) } + /// Returns a signature for the given message (as a field) using the private key. + pub fn sign_value(&self, value: &str) -> anyhow::Result { + Signature::sign_value(self, value) + } + /// Returns the signature secret key. fn sk_sig(&self) -> Scalar { self.0.sk_sig().into() diff --git a/sdk/src/account/signature.rs b/sdk/src/account/signature.rs index 5461415..13cfb3d 100644 --- a/sdk/src/account/signature.rs +++ b/sdk/src/account/signature.rs @@ -14,7 +14,11 @@ // You should have received a copy of the GNU General Public License // along with the Aleo SDK library. If not, see . -use crate::{types::SignatureNative, Address, ComputeKey, PrivateKey, Scalar}; +use crate::{ + types::{SignatureNative, ValueNative}, + Address, ComputeKey, PrivateKey, Scalar, +}; +use snarkvm::console::program::ToFields; use pyo3::prelude::*; use rand::{rngs::StdRng, SeedableRng}; @@ -62,12 +66,31 @@ impl Signature { .map(Self) } + /// Returns a signature for the given message (as bytes) using the private key. + #[staticmethod] + pub fn sign_value(private_key: &PrivateKey, value: &str) -> anyhow::Result { + let fields = ValueNative::from_str(value)?.to_fields()?; + private_key + .deref() + .sign(&fields, &mut StdRng::from_entropy()) + .map(Self) + } + /// Verifies (challenge == challenge') && (address == address') where: /// challenge' := HashToScalar(G^response pk_sig^challenge, pk_sig, pr_sig, address, message) pub fn verify(&self, address: &Address, message: &[u8]) -> bool { self.0.verify_bytes(address, message) } + /// Verifies (challenge == challenge') && (address == address') where: + /// challenge' := HashToScalar(G^response pk_sig^challenge, pk_sig, pr_sig, address, message) + pub fn verify_value(&self, address: &Address, value: &str) -> bool { + let Ok(fields) = ValueNative::from_str(value).and_then(|v| v.to_fields()) else { + return false; + }; + self.0.verify(address, &fields) + } + /// Returns a string representation of the signature. fn __str__(&self) -> String { self.0.to_string()