From 5a0be430bece7d52495737746b42e65e5573ee6b Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Wed, 25 May 2022 20:10:52 -0600 Subject: [PATCH] split RingMLSAG out of transaction core (#2026) * WIP split RingMLSAG out of transaction core * Update crypto/ring-signature/src/domain_separators.rs Co-authored-by: Remoun Metyas * Update crypto/ring-signature/signer/src/no_keys_ring_signer.rs Co-authored-by: Remoun Metyas * Update crypto/ring-signature/signer/Cargo.toml Co-authored-by: Remoun Metyas * Update crypto/ring-signature/Cargo.toml Co-authored-by: Remoun Metyas * Update crypto/ring-signature/src/amount/commitment.rs Co-authored-by: Remoun Metyas * Update crypto/ring-signature/src/lib.rs Co-authored-by: Remoun Metyas * Update transaction/core/src/proptest_fixtures.rs Co-authored-by: Remoun Metyas * Update crypto/ring-signature/signer/src/traits.rs Co-authored-by: Remoun Metyas * Update crypto/ring-signature/src/amount/commitment.rs Co-authored-by: Remoun Metyas * rename MLSAGError to RingSignatureError everywhere * remove unnecesary dependencies in ring-signature crate * fix a remoun nit * fix previous, move `Amount` and `TokenId` to `mc-transaction-types` * fix tests * Update transaction/core/tests/validation.rs Co-authored-by: Remoun Metyas * Update transaction/core/src/signed_contingent_input.rs Co-authored-by: Remoun Metyas * remove some undesired re-exports * small reorg * review comments, fixups * fix missing README * fix unused imports, fix review comments * add missing README * add more text to crypto-ring-signature readme Co-authored-by: Remoun Metyas --- Cargo.lock | 77 ++++- Cargo.toml | 3 + android-bindings/Cargo.toml | 1 + android-bindings/src/bindings.rs | 3 +- api/Cargo.toml | 1 + api/src/convert/signature_rct_bulletproofs.rs | 5 +- api/src/convert/tx.rs | 8 +- api/tests/prost.rs | 5 +- consensus/api/src/conversions.rs | 4 +- consensus/enclave/trusted/Cargo.lock | 55 ++++ consensus/service/Cargo.toml | 1 + consensus/service/src/validators.rs | 2 +- crypto/ring-signature/Cargo.toml | 40 +++ crypto/ring-signature/README.md | 24 ++ crypto/ring-signature/signer/Cargo.toml | 39 +++ .../ring-signature}/signer/README.md | 0 .../ring-signature/signer/src/lib.rs | 5 + .../signer/src}/local_signer.rs | 7 +- .../signer/src}/no_keys_ring_signer.rs | 2 +- .../ring-signature/signer/src}/traits.rs | 18 +- .../ring-signature}/src/amount/commitment.rs | 44 ++- .../src/amount/compressed_commitment.rs | 29 +- crypto/ring-signature/src/amount/mod.rs | 13 + .../ring-signature/src/domain_separators.rs | 25 ++ crypto/ring-signature/src/lib.rs | 38 +++ .../ring-signature}/src/onetime_keys.rs | 264 +++++++++--------- .../ring-signature/src/proptest_fixtures.rs | 27 ++ .../src/ring_signature/curve_scalar.rs | 0 .../src/ring_signature/error.rs | 37 +++ .../src/ring_signature/generator_cache.rs | 2 +- .../src/ring_signature/key_image.rs | 0 .../src/ring_signature/mlsag.rs | 112 +++----- .../ring-signature}/src/ring_signature/mod.rs | 28 +- fog/distribution/Cargo.toml | 1 + fog/distribution/src/main.rs | 2 +- fog/ingest/enclave/trusted/Cargo.lock | 55 ++++ fog/ledger/enclave/trusted/Cargo.lock | 55 ++++ fog/sample-paykit/Cargo.toml | 2 + fog/sample-paykit/src/client.rs | 2 +- fog/view/enclave/trusted/Cargo.lock | 55 ++++ libmobilecoin/Cargo.toml | 1 + libmobilecoin/src/transaction.rs | 3 +- mobilecoind/Cargo.toml | 1 + mobilecoind/src/payments.rs | 2 +- transaction/core/Cargo.toml | 14 +- transaction/core/src/amount/mod.rs | 26 +- transaction/core/src/blockchain/block.rs | 19 +- .../core/src/blockchain/block_contents.rs | 2 +- transaction/core/src/constants.rs | 2 +- transaction/core/src/domain_separators.rs | 9 - transaction/core/src/lib.rs | 27 +- transaction/core/src/memo.rs | 5 +- .../core/src/mint/validation/config.rs | 16 +- transaction/core/src/mint/validation/tx.rs | 16 +- transaction/core/src/proptest_fixtures.rs | 28 +- transaction/core/src/range_proofs/mod.rs | 41 ++- .../src/{ring_signature => ring_ct}/error.rs | 19 +- transaction/core/src/ring_ct/mod.rs | 9 + .../rct_bulletproofs.rs | 58 ++-- .../core/src/signed_contingent_input.rs | 23 +- transaction/core/src/token.rs | 93 +----- transaction/core/src/tx.rs | 17 +- transaction/core/src/validation/error.rs | 2 +- transaction/core/test-utils/Cargo.toml | 1 + transaction/core/test-utils/src/lib.rs | 5 +- transaction/core/test-utils/src/mint.rs | 2 +- transaction/core/tests/digest-test-vectors.rs | 5 +- transaction/core/tests/util/mod.rs | 8 +- transaction/core/tests/validation.rs | 20 +- transaction/std/Cargo.toml | 1 + transaction/std/src/error.rs | 10 +- transaction/std/src/input_credentials.rs | 2 +- transaction/std/src/input_materials.rs | 4 +- .../src/signed_contingent_input_builder.rs | 24 +- transaction/std/src/test_utils.rs | 2 +- transaction/std/src/transaction_builder.rs | 6 +- transaction/types/Cargo.toml | 15 + transaction/types/README.md | 8 + transaction/types/src/amount.rs | 24 ++ transaction/types/src/lib.rs | 14 + transaction/types/src/token.rs | 94 +++++++ 81 files changed, 1187 insertions(+), 582 deletions(-) create mode 100644 crypto/ring-signature/Cargo.toml create mode 100644 crypto/ring-signature/README.md create mode 100644 crypto/ring-signature/signer/Cargo.toml rename {transaction/core/src => crypto/ring-signature}/signer/README.md (100%) rename transaction/core/src/signer/mod.rs => crypto/ring-signature/signer/src/lib.rs (85%) rename {transaction/core/src/signer => crypto/ring-signature/signer/src}/local_signer.rs (94%) rename {transaction/core/src/signer => crypto/ring-signature/signer/src}/no_keys_ring_signer.rs (96%) rename {transaction/core/src/signer => crypto/ring-signature/signer/src}/traits.rs (93%) rename {transaction/core => crypto/ring-signature}/src/amount/commitment.rs (73%) rename {transaction/core => crypto/ring-signature}/src/amount/compressed_commitment.rs (80%) create mode 100644 crypto/ring-signature/src/amount/mod.rs create mode 100644 crypto/ring-signature/src/domain_separators.rs create mode 100644 crypto/ring-signature/src/lib.rs rename {transaction/core => crypto/ring-signature}/src/onetime_keys.rs (61%) create mode 100644 crypto/ring-signature/src/proptest_fixtures.rs rename {transaction/core => crypto/ring-signature}/src/ring_signature/curve_scalar.rs (100%) create mode 100644 crypto/ring-signature/src/ring_signature/error.rs rename {transaction/core => crypto/ring-signature}/src/ring_signature/generator_cache.rs (95%) rename {transaction/core => crypto/ring-signature}/src/ring_signature/key_image.rs (100%) rename {transaction/core => crypto/ring-signature}/src/ring_signature/mlsag.rs (91%) rename {transaction/core => crypto/ring-signature}/src/ring_signature/mod.rs (75%) rename transaction/core/src/{ring_signature => ring_ct}/error.rs (86%) create mode 100644 transaction/core/src/ring_ct/mod.rs rename transaction/core/src/{ring_signature => ring_ct}/rct_bulletproofs.rs (97%) create mode 100644 transaction/types/Cargo.toml create mode 100644 transaction/types/README.md create mode 100644 transaction/types/src/amount.rs create mode 100644 transaction/types/src/lib.rs create mode 100644 transaction/types/src/token.rs diff --git a/Cargo.lock b/Cargo.lock index 1b671916ce..3e39becad3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2076,6 +2076,7 @@ dependencies = [ "mc-crypto-keys", "mc-crypto-noise", "mc-crypto-rand", + "mc-crypto-ring-signature-signer", "mc-crypto-sig", "mc-fog-kex-rng", "mc-fog-report-validation", @@ -2315,6 +2316,7 @@ dependencies = [ "mc-crypto-keys", "mc-crypto-noise", "mc-crypto-rand", + "mc-crypto-ring-signature-signer", "mc-fog-kex-rng", "mc-fog-report-types", "mc-fog-report-validation", @@ -2347,6 +2349,7 @@ dependencies = [ "mc-attest-core", "mc-crypto-keys", "mc-crypto-multisig", + "mc-crypto-ring-signature-signer", "mc-crypto-x509-test-vectors", "mc-fog-report-validation-test-utils", "mc-test-vectors-b58-encodings", @@ -2840,6 +2843,7 @@ dependencies = [ "mc-crypto-digestible", "mc-crypto-keys", "mc-crypto-multisig", + "mc-crypto-ring-signature-signer", "mc-ledger-db", "mc-ledger-sync", "mc-peers", @@ -3082,6 +3086,58 @@ dependencies = [ "rand_hc 0.3.1", ] +[[package]] +name = "mc-crypto-ring-signature" +version = "1.3.0-pre0" +dependencies = [ + "curve25519-dalek", + "displaydoc", + "hex_fmt", + "mc-account-keys", + "mc-crypto-digestible", + "mc-crypto-digestible-test-utils", + "mc-crypto-hashes", + "mc-crypto-keys", + "mc-transaction-types", + "mc-util-from-random", + "mc-util-repr-bytes", + "mc-util-serial", + "mc-util-test-helper", + "proptest", + "prost", + "rand_core 0.6.3", + "serde", + "subtle", + "tempdir", + "zeroize", +] + +[[package]] +name = "mc-crypto-ring-signature-signer" +version = "1.3.0-pre0" +dependencies = [ + "curve25519-dalek", + "displaydoc", + "generic-array", + "hex_fmt", + "mc-account-keys", + "mc-crypto-digestible-test-utils", + "mc-crypto-keys", + "mc-crypto-rand", + "mc-crypto-ring-signature", + "mc-transaction-types", + "mc-util-serial", + "proptest", + "prost", + "rand 0.8.5", + "rand_core 0.6.3", + "rand_hc 0.3.1", + "serde", + "subtle", + "tempdir", + "zeroize", +] + [[package]] name = "mc-crypto-sig" version = "1.3.0-pre0" @@ -3177,6 +3233,7 @@ dependencies = [ "mc-connection", "mc-consensus-enclave-measurement", "mc-crypto-keys", + "mc-crypto-ring-signature-signer", "mc-fog-ingest-enclave-measurement", "mc-fog-report-connection", "mc-fog-report-validation", @@ -3925,6 +3982,8 @@ dependencies = [ "mc-consensus-enclave-measurement", "mc-crypto-keys", "mc-crypto-rand", + "mc-crypto-ring-signature", + "mc-crypto-ring-signature-signer", "mc-fog-api", "mc-fog-enclave-connection", "mc-fog-ingest-enclave-measurement", @@ -4510,6 +4569,7 @@ dependencies = [ "mc-crypto-hashes", "mc-crypto-keys", "mc-crypto-rand", + "mc-crypto-ring-signature-signer", "mc-fog-report-connection", "mc-fog-report-validation", "mc-fog-report-validation-test-utils", @@ -4903,10 +4963,12 @@ dependencies = [ "mc-crypto-hashes", "mc-crypto-keys", "mc-crypto-multisig", - "mc-crypto-rand", + "mc-crypto-ring-signature", + "mc-crypto-ring-signature-signer", "mc-ledger-db", "mc-transaction-core-test-utils", "mc-transaction-std", + "mc-transaction-types", "mc-util-from-random", "mc-util-repr-bytes", "mc-util-serial", @@ -4917,7 +4979,6 @@ dependencies = [ "prost", "rand 0.8.5", "rand_core 0.6.3", - "rand_hc 0.3.1", "serde", "sha2 0.10.2", "subtle", @@ -4933,6 +4994,7 @@ dependencies = [ "mc-crypto-keys", "mc-crypto-multisig", "mc-crypto-rand", + "mc-crypto-ring-signature-signer", "mc-fog-report-validation-test-utils", "mc-ledger-db", "mc-transaction-core", @@ -4955,6 +5017,7 @@ dependencies = [ "mc-account-keys", "mc-crypto-hashes", "mc-crypto-keys", + "mc-crypto-ring-signature-signer", "mc-fog-report-validation", "mc-fog-report-validation-test-utils", "mc-transaction-core", @@ -4970,6 +5033,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mc-transaction-types" +version = "1.3.0-pre0" +dependencies = [ + "mc-crypto-digestible", + "serde", + "subtle", + "zeroize", +] + [[package]] name = "mc-util-b58-decoder" version = "1.3.0-pre0" diff --git a/Cargo.toml b/Cargo.toml index 46b2f41a0c..1d356a5e63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,8 @@ members = [ "crypto/multisig", "crypto/noise", "crypto/rand", + "crypto/ring-signature", + "crypto/ring-signature/signer", "crypto/x509/test-vectors", "crypto/x509/utils", "enclave-boundary", @@ -121,6 +123,7 @@ members = [ "transaction/core", "transaction/core/test-utils", "transaction/std", + "transaction/types", "util/b58-decoder", "util/build/enclave", "util/build/grpc", diff --git a/android-bindings/Cargo.toml b/android-bindings/Cargo.toml index 0a34ee1f54..6287271903 100644 --- a/android-bindings/Cargo.toml +++ b/android-bindings/Cargo.toml @@ -25,6 +25,7 @@ mc-crypto-box = { path = "../crypto/box" } mc-crypto-keys = { path = "../crypto/keys" } mc-crypto-noise = { path = "../crypto/noise" } mc-crypto-rand = { path = "../crypto/rand", features = ["std"] } +mc-crypto-ring-signature-signer = { path = "../crypto/ring-signature/signer", default-features = false } mc-fog-report-types = { path = "../fog/report/types" } mc-fog-report-validation = { path = "../fog/report/validation" } mc-transaction-core = { path = "../transaction/core" } diff --git a/android-bindings/src/bindings.rs b/android-bindings/src/bindings.rs index 8022747a88..fe73350da8 100644 --- a/android-bindings/src/bindings.rs +++ b/android-bindings/src/bindings.rs @@ -37,6 +37,7 @@ use mc_common::ResponderId; use mc_crypto_box::{CryptoBox, VersionedCryptoBox}; use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic, X25519}; use mc_crypto_rand::McRng; +use mc_crypto_ring_signature_signer::NoKeysRingSigner; use mc_fog_kex_rng::{BufferedRng, KexRngPubkey, NewFromKex, StoredRng, VersionedKexRng}; use mc_fog_report_types::{Report, ReportResponse}; use mc_fog_report_validation::{FogReportResponses, FogResolver}; @@ -46,12 +47,10 @@ use mc_transaction_core::{ create_shared_secret, recover_onetime_private_key, recover_public_subaddress_spend_key, }, ring_signature::KeyImage, - signer::NoKeysRingSigner, tokens::Mob, tx::{Tx, TxOut, TxOutConfirmationNumber, TxOutMembershipProof}, Amount, BlockVersion, CompressedCommitment, MaskedAmount, Token, }; - use mc_transaction_std::{ AuthenticatedSenderMemo, AuthenticatedSenderWithPaymentRequestIdMemo, DestinationMemo, InputCredentials, MemoBuilder, MemoPayload, RTHMemoBuilder, ReservedSubaddresses, diff --git a/api/Cargo.toml b/api/Cargo.toml index d36a80a41a..9c73697b41 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -34,6 +34,7 @@ mc-util-build-script = { path = "../util/build/script" } cargo-emit = "0.2.1" [dev-dependencies] +mc-crypto-ring-signature-signer = { path = "../crypto/ring-signature/signer" } mc-crypto-x509-test-vectors = { path = "../crypto/x509/test-vectors" } mc-fog-report-validation-test-utils = { path = "../fog/report/validation/test-utils" } mc-test-vectors-b58-encodings = { path = "../test-vectors/b58-encodings" } diff --git a/api/src/convert/signature_rct_bulletproofs.rs b/api/src/convert/signature_rct_bulletproofs.rs index 2c7db3dd6d..05d70ea58f 100644 --- a/api/src/convert/signature_rct_bulletproofs.rs +++ b/api/src/convert/signature_rct_bulletproofs.rs @@ -1,9 +1,10 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + //! Convert to/from external::SignatureRctBulletproofs use crate::{convert::ConversionError, external}; use mc_transaction_core::{ - ring_signature::{RingMLSAG, SignatureRctBulletproofs}, - CompressedCommitment, + ring_ct::SignatureRctBulletproofs, ring_signature::RingMLSAG, CompressedCommitment, }; use protobuf::RepeatedField; use std::convert::TryFrom; diff --git a/api/src/convert/tx.rs b/api/src/convert/tx.rs index 687ea0301b..6c7df8f2c0 100644 --- a/api/src/convert/tx.rs +++ b/api/src/convert/tx.rs @@ -1,7 +1,9 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + //! Convert to/from external::Tx. use crate::{convert::ConversionError, external}; -use mc_transaction_core::{ring_signature::SignatureRctBulletproofs, tx}; +use mc_transaction_core::{ring_ct::SignatureRctBulletproofs, tx}; use std::convert::TryFrom; /// Convert mc_transaction_core::tx::Tx --> external::Tx. @@ -29,10 +31,10 @@ impl TryFrom<&external::Tx> for tx::Tx { mod tests { use super::*; use mc_account_keys::AccountKey; + use mc_crypto_ring_signature_signer::NoKeysRingSigner; use mc_fog_report_validation_test_utils::MockFogResolver; use mc_transaction_core::{ - constants::MILLIMOB_TO_PICOMOB, signer::NoKeysRingSigner, tokens::Mob, tx::Tx, Amount, - BlockVersion, Token, TokenId, + constants::MILLIMOB_TO_PICOMOB, tokens::Mob, tx::Tx, Amount, BlockVersion, Token, TokenId, }; use mc_transaction_std::{ test_utils::get_input_credentials, EmptyMemoBuilder, ReservedSubaddresses, diff --git a/api/tests/prost.rs b/api/tests/prost.rs index 787bfaf743..0de4e84835 100644 --- a/api/tests/prost.rs +++ b/api/tests/prost.rs @@ -1,11 +1,14 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + //! Tests that prost-versions of structures round-trip with the versions //! generated from external.proto use maplit::btreemap; use mc_account_keys::{AccountKey, PublicAddress, RootIdentity}; use mc_api::external; +use mc_crypto_ring_signature_signer::NoKeysRingSigner; use mc_fog_report_validation_test_utils::{FullyValidatedFogPubkey, MockFogResolver}; -use mc_transaction_core::{signer::NoKeysRingSigner, Amount, BlockVersion, SignedContingentInput}; +use mc_transaction_core::{Amount, BlockVersion, SignedContingentInput}; use mc_transaction_std::{ test_utils::get_input_credentials, EmptyMemoBuilder, ReservedSubaddresses, SignedContingentInputBuilder, diff --git a/consensus/api/src/conversions.rs b/consensus/api/src/conversions.rs index 864ee3b49e..a280d92bd2 100644 --- a/consensus/api/src/conversions.rs +++ b/consensus/api/src/conversions.rs @@ -15,7 +15,7 @@ use crate::{ }; use mc_api::ConversionError; use mc_transaction_core::{ - mint::MintValidationError, ring_signature, ring_signature::MLSAGError, + mint::MintValidationError, ring_ct, ring_signature::Error as RingSignatureError, validation::TransactionValidationError as Error, BlockVersion, InputRuleError, TokenId, }; use std::convert::{From, TryFrom, TryInto}; @@ -84,7 +84,7 @@ impl TryInto for ProposeTxResult { Self::InsufficientInputSignatures => Ok(Error::InsufficientInputSignatures), Self::InvalidInputSignature => Ok(Error::InvalidInputSignature), Self::InvalidTransactionSignature => Ok(Error::InvalidTransactionSignature( - ring_signature::Error::MLSAG(MLSAGError::InvalidSignature), + ring_ct::Error::RingSignature(RingSignatureError::InvalidSignature), )), Self::InvalidRangeProof => Ok(Error::InvalidRangeProof), Self::InsufficientRingSize => Ok(Error::InsufficientRingSize), diff --git a/consensus/enclave/trusted/Cargo.lock b/consensus/enclave/trusted/Cargo.lock index aefc4bae84..543739ef31 100644 --- a/consensus/enclave/trusted/Cargo.lock +++ b/consensus/enclave/trusted/Cargo.lock @@ -1025,6 +1025,48 @@ dependencies = [ "rand_hc", ] +[[package]] +name = "mc-crypto-ring-signature" +version = "1.3.0-pre0" +dependencies = [ + "curve25519-dalek", + "displaydoc", + "hex_fmt", + "mc-account-keys", + "mc-crypto-digestible", + "mc-crypto-hashes", + "mc-crypto-keys", + "mc-transaction-types", + "mc-util-from-random", + "mc-util-repr-bytes", + "mc-util-serial", + "prost", + "rand_core", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "mc-crypto-ring-signature-signer" +version = "1.3.0-pre0" +dependencies = [ + "curve25519-dalek", + "displaydoc", + "generic-array", + "hex_fmt", + "mc-account-keys", + "mc-crypto-keys", + "mc-crypto-ring-signature", + "mc-transaction-types", + "mc-util-serial", + "prost", + "rand_core", + "serde", + "subtle", + "zeroize", +] + [[package]] name = "mc-enclave-boundary" version = "1.3.0-pre0" @@ -1184,6 +1226,9 @@ dependencies = [ "mc-crypto-hashes", "mc-crypto-keys", "mc-crypto-multisig", + "mc-crypto-ring-signature", + "mc-crypto-ring-signature-signer", + "mc-transaction-types", "mc-util-from-random", "mc-util-repr-bytes", "mc-util-serial", @@ -1197,6 +1242,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mc-transaction-types" +version = "1.3.0-pre0" +dependencies = [ + "mc-crypto-digestible", + "serde", + "subtle", + "zeroize", +] + [[package]] name = "mc-util-build-script" version = "1.3.0-pre0" diff --git a/consensus/service/Cargo.toml b/consensus/service/Cargo.toml index 547541127a..2eefd23154 100644 --- a/consensus/service/Cargo.toml +++ b/consensus/service/Cargo.toml @@ -64,6 +64,7 @@ mc-sgx-build = { path = "../../sgx/build" } mc-account-keys = { path = "../../account-keys" } mc-common = { path = "../../common", features = ["loggers"] } mc-consensus-enclave-mock = { path = "../../consensus/enclave/mock" } +mc-crypto-ring-signature-signer = { path = "../../crypto/ring-signature/signer" } mc-ledger-db = { path = "../../ledger/db", features = ["test_utils"] } mc-peers-test-utils = { path = "../../peers/test-utils" } mc-transaction-core-test-utils = { path = "../../transaction/core/test-utils" } diff --git a/consensus/service/src/validators.rs b/consensus/service/src/validators.rs index 039d8ac111..6b191f0b79 100644 --- a/consensus/service/src/validators.rs +++ b/consensus/service/src/validators.rs @@ -491,10 +491,10 @@ mod is_valid_tests { mod combine_tests { use super::*; use mc_crypto_keys::{RistrettoPrivate, RistrettoPublic}; + use mc_crypto_ring_signature_signer::NoKeysRingSigner; use mc_ledger_db::test_utils::get_mock_ledger; use mc_transaction_core::{ onetime_keys::recover_onetime_private_key, - signer::NoKeysRingSigner, tokens::Mob, tx::{TxOut, TxOutMembershipProof}, Amount, BlockVersion, Token, diff --git a/crypto/ring-signature/Cargo.toml b/crypto/ring-signature/Cargo.toml new file mode 100644 index 0000000000..b1c9e4f9ac --- /dev/null +++ b/crypto/ring-signature/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "mc-crypto-ring-signature" +version = "1.3.0-pre0" +authors = ["MobileCoin"] +edition = "2021" +readme = "README.md" + +[dependencies] +# External dependencies +displaydoc = { version = "0.2", default-features = false } +hex_fmt = "0.3" +# Enable all default features not known to break code coverage builds +proptest = { version = "1.0", default-features = false, features = ["default-code-coverage"], optional = true } +prost = { version = "0.10", default-features = false, features = ["prost-derive"] } +rand_core = { version = "0.6", default-features = false } +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +subtle = { version = "2.4.1", default-features = false, features = ["i128"] } +zeroize = { version = "1", default-features = false } + +# MobileCoin dependencies +mc-account-keys = { path = "../../account-keys", default-features = false } +mc-crypto-digestible = { path = "../../crypto/digestible", features = ["dalek", "derive"] } +mc-crypto-hashes = { path = "../../crypto/hashes" } +mc-crypto-keys = { path = "../../crypto/keys", default-features = false } +mc-transaction-types = { path = "../../transaction/types" } +mc-util-from-random = { path = "../../util/from-random" } +mc-util-repr-bytes = { path = "../../util/repr-bytes" } +mc-util-serial = { path = "../../util/serial" } + +[target.'cfg(any(target_feature = "avx2", target_feature = "avx"))'.dependencies] +curve25519-dalek = { version = "4.0.0-pre.2", default-features = false, features = ["simd_backend", "nightly", "serde"] } +[target.'cfg(not(any(target_feature = "avx2", target_feature = "avx")))'.dependencies] +curve25519-dalek = { version = "4.0.0-pre.2", default-features = false, features = ["nightly", "u64_backend", "serde"] } + +[dev-dependencies] +tempdir = "0.3" + +mc-crypto-digestible-test-utils = { path = "../../crypto/digestible/test-utils" } +mc-util-serial = { path = "../../util/serial", features = ["std"] } +mc-util-test-helper = { path = "../../util/test-helper" } diff --git a/crypto/ring-signature/README.md b/crypto/ring-signature/README.md new file mode 100644 index 0000000000..c9975d5cea --- /dev/null +++ b/crypto/ring-signature/README.md @@ -0,0 +1,24 @@ +mc-crypto-ring-signature +======================== + +This crate implements a few fundamental parts of MobileCoin transactions: + +* MobileCoin amount commitments. These are Pedersen commitments to numbers which + represent an amount of some token on the blockchain, in the smallest representable units. +* MobileCoin ring signatures. These are MLSAG ring signatures (see Ring CT) which sign over + several amount commitments together with their "address". The signer knows the "one-time private key" + of only one of these, but the signature does not reveal which one it is. This signature + confers spend authority for a value in the blockchain. However, there may be many of these in a + single MobileCoin transaction. The entire assembly is defined in the `ring_ct` module in + `mc-transaction-core`. +* Definitions for "one-time keys" and their connection to transaction outputs and subaddresses. + +This crate implicitly defines relationships between a bunch of key components: +* One-time private keys and TxOut's +* One-time private keys and Key Images +* One-time private keys of TxOut's and subaddresses +* Subaddreses and Ring signatures + +However, most things having to do with the TxOut Public key and the TxOut shared secret +live in the `mc-transaction-core` crate, one level higher. Most of the actual blockchain +data structures are defined there. diff --git a/crypto/ring-signature/signer/Cargo.toml b/crypto/ring-signature/signer/Cargo.toml new file mode 100644 index 0000000000..627e43c2bf --- /dev/null +++ b/crypto/ring-signature/signer/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "mc-crypto-ring-signature-signer" +version = "1.3.0-pre0" +authors = ["MobileCoin"] +edition = "2021" +readme = "README.md" + +[dependencies] +# External dependencies +displaydoc = { version = "0.2", default-features = false } +generic-array = { version = "0.14", features = ["serde", "more_lengths"] } +hex_fmt = "0.3" +prost = { version = "0.10", default-features = false, features = ["prost-derive"] } +rand_core = { version = "0.6", default-features = false } +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +subtle = { version = "2.4.1", default-features = false, features = ["i128"] } +zeroize = { version = "1", default-features = false } + +# MobileCoin dependencies +mc-account-keys = { path = "../../../account-keys", default-features = false } +mc-crypto-keys = { path = "../../keys", default-features = false } +mc-crypto-ring-signature = { path = "..", default-features = false } +mc-transaction-types = { path = "../../../transaction/types" } +mc-util-serial = { path = "../../../util/serial" } + +[target.'cfg(any(target_feature = "avx2", target_feature = "avx"))'.dependencies] +curve25519-dalek = { version = "4.0.0-pre.2", default-features = false, features = ["simd_backend", "nightly"] } + +[target.'cfg(not(any(target_feature = "avx2", target_feature = "avx")))'.dependencies] +curve25519-dalek = { version = "4.0.0-pre.2", default-features = false, features = ["nightly", "u64_backend"] } + +[dev-dependencies] +mc-crypto-digestible-test-utils = { path = "../../digestible/test-utils" } +mc-crypto-rand = { path = "../../rand" } +# Enable all default features not known to break code coverage builds +proptest = { version = "1.0", default-features = false, features = ["default-code-coverage"] } +rand = "0.8" +rand_hc = "0.3" +tempdir = "0.3" diff --git a/transaction/core/src/signer/README.md b/crypto/ring-signature/signer/README.md similarity index 100% rename from transaction/core/src/signer/README.md rename to crypto/ring-signature/signer/README.md diff --git a/transaction/core/src/signer/mod.rs b/crypto/ring-signature/signer/src/lib.rs similarity index 85% rename from transaction/core/src/signer/mod.rs rename to crypto/ring-signature/signer/src/lib.rs index 3c48adbe9b..a82bd61917 100644 --- a/transaction/core/src/signer/mod.rs +++ b/crypto/ring-signature/signer/src/lib.rs @@ -2,6 +2,11 @@ //! Interfaces for signing transactions +#![no_std] +#![deny(missing_docs)] + +extern crate alloc; + mod no_keys_ring_signer; pub use no_keys_ring_signer::NoKeysRingSigner; diff --git a/transaction/core/src/signer/local_signer.rs b/crypto/ring-signature/signer/src/local_signer.rs similarity index 94% rename from transaction/core/src/signer/local_signer.rs rename to crypto/ring-signature/signer/src/local_signer.rs index 328838537f..e00eb54765 100644 --- a/transaction/core/src/signer/local_signer.rs +++ b/crypto/ring-signature/signer/src/local_signer.rs @@ -1,12 +1,11 @@ // Copyright (c) 2018-2022 The MobileCoin Foundation use super::{Error, OneTimeKeyDeriveData, RingSigner, SignableInputRing}; -use crate::{ - onetime_keys::recover_onetime_private_key, - ring_signature::{generators, CryptoRngCore, RingMLSAG, Scalar}, -}; use mc_account_keys::AccountKey; use mc_crypto_keys::RistrettoPublic; +use mc_crypto_ring_signature::{ + generators, onetime_keys::recover_onetime_private_key, CryptoRngCore, RingMLSAG, Scalar, +}; /// An implementation of RingSigner that holds private keys and derives one-time /// private keys diff --git a/transaction/core/src/signer/no_keys_ring_signer.rs b/crypto/ring-signature/signer/src/no_keys_ring_signer.rs similarity index 96% rename from transaction/core/src/signer/no_keys_ring_signer.rs rename to crypto/ring-signature/signer/src/no_keys_ring_signer.rs index 0ea8d02fb7..59ead0ed4f 100644 --- a/transaction/core/src/signer/no_keys_ring_signer.rs +++ b/crypto/ring-signature/signer/src/no_keys_ring_signer.rs @@ -1,8 +1,8 @@ // Copyright (c) 2018-2022 The MobileCoin Foundation use super::{Error, OneTimeKeyDeriveData, RingSigner, SignableInputRing}; -use crate::ring_signature::{generators, CryptoRngCore, RingMLSAG, Scalar}; use mc_crypto_keys::RistrettoPublic; +use mc_crypto_ring_signature::{generators, CryptoRngCore, RingMLSAG, Scalar}; /// An implementation of RingSigner that holds no keys, and doesn't do any /// non-trivial derivation of the one-time private key. diff --git a/transaction/core/src/signer/traits.rs b/crypto/ring-signature/signer/src/traits.rs similarity index 93% rename from transaction/core/src/signer/traits.rs rename to crypto/ring-signature/signer/src/traits.rs index 9b8baeb857..ff66034661 100644 --- a/transaction/core/src/signer/traits.rs +++ b/crypto/ring-signature/signer/src/traits.rs @@ -1,12 +1,12 @@ // Copyright (c) 2018-2022 The MobileCoin Foundation -use crate::{ - ring_signature::{CryptoRngCore, MLSAGError, ReducedTxOut, RingMLSAG, Scalar}, - Amount, -}; use alloc::{string::String, vec::Vec}; use displaydoc::Display; use mc_crypto_keys::{KeyError, RistrettoPrivate}; +use mc_crypto_ring_signature::{ + CryptoRngCore, Error as RingSignatureError, ReducedTxOut, RingMLSAG, Scalar, +}; +use mc_transaction_types::Amount; use serde::{Deserialize, Serialize}; use zeroize::Zeroize; @@ -129,8 +129,8 @@ pub enum Error { Keys(KeyError), /// Real input index out of bounds RealInputIndexOutOfBounds, - /// MLSAG: {0} - MLSAG(MLSAGError), + /// Ring Signature: {0} + RingSignature(RingSignatureError), /// No path to spend key (logic error) NoPathToSpendKey, } @@ -141,8 +141,8 @@ impl From for Error { } } -impl From for Error { - fn from(src: MLSAGError) -> Self { - Self::MLSAG(src) +impl From for Error { + fn from(src: RingSignatureError) -> Self { + Self::RingSignature(src) } } diff --git a/transaction/core/src/amount/commitment.rs b/crypto/ring-signature/src/amount/commitment.rs similarity index 73% rename from transaction/core/src/amount/commitment.rs rename to crypto/ring-signature/src/amount/commitment.rs index 5f8febf06f..11d30ff3b2 100644 --- a/transaction/core/src/amount/commitment.rs +++ b/crypto/ring-signature/src/amount/commitment.rs @@ -1,7 +1,6 @@ -use crate::{ - ring_signature::{MLSAGError, PedersenGens, Scalar}, - CompressedCommitment, -}; +// Copyright (c) 2018-2022 The MobileCoin Foundation + +use crate::{CompressedCommitment, Error, PedersenGens, Scalar}; use core::{convert::TryFrom, fmt}; use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use mc_crypto_digestible::Digestible; @@ -37,13 +36,10 @@ impl Commitment { } impl TryFrom<&CompressedCommitment> for Commitment { - type Error = crate::ring_signature::MLSAGError; + type Error = Error; fn try_from(src: &CompressedCommitment) -> Result { - let point = src - .point - .decompress() - .ok_or(MLSAGError::InvalidCurvePoint)?; + let point = src.point.decompress().ok_or(Error::InvalidCurvePoint)?; Ok(Self { point }) } } @@ -59,7 +55,7 @@ impl fmt::Debug for Commitment { } impl ReprBytes for Commitment { - type Error = MLSAGError; + type Error = Error; type Size = U32; fn to_bytes(&self) -> GenericArray { self.point.compress().to_bytes().into() @@ -67,7 +63,7 @@ impl ReprBytes for Commitment { fn from_bytes(src: &GenericArray) -> Result { let point = CompressedRistretto::from_slice(src.as_slice()) .decompress() - .ok_or(MLSAGError::InvalidCurvePoint)?; + .ok_or(Error::InvalidCurvePoint)?; Ok(Self { point }) } } @@ -83,24 +79,26 @@ mod commitment_tests { Commitment, }; use curve25519_dalek::ristretto::RistrettoPoint; - use rand::{rngs::StdRng, RngCore, SeedableRng}; + use mc_util_test_helper::run_with_several_seeds; + use rand_core::RngCore; #[test] // Commitment::new should create the correct RistrettoPoint. fn test_new() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); - let value = rng.next_u64(); - let blinding = Scalar::random(&mut rng); - let gens = generators(rng.next_u64()); + run_with_several_seeds(|mut rng| { + let value = rng.next_u64(); + let blinding = Scalar::random(&mut rng); + let gens = generators(rng.next_u64()); - let commitment = Commitment::new(value, blinding, &gens); + let commitment = Commitment::new(value, blinding, &gens); - let expected_point: RistrettoPoint = { - let H = gens.B; - let G = B_BLINDING; - Scalar::from(value) * H + blinding * G - }; + let expected_point: RistrettoPoint = { + let H = gens.B; + let G = B_BLINDING; + Scalar::from(value) * H + blinding * G + }; - assert_eq!(commitment.point, expected_point); + assert_eq!(commitment.point, expected_point); + }) } } diff --git a/transaction/core/src/amount/compressed_commitment.rs b/crypto/ring-signature/src/amount/compressed_commitment.rs similarity index 80% rename from transaction/core/src/amount/compressed_commitment.rs rename to crypto/ring-signature/src/amount/compressed_commitment.rs index a8ffb30066..a55c426a47 100644 --- a/transaction/core/src/amount/compressed_commitment.rs +++ b/crypto/ring-signature/src/amount/compressed_commitment.rs @@ -1,3 +1,5 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + use crate::{ ring_signature::{Error, PedersenGens, Scalar}, Commitment, @@ -92,25 +94,26 @@ mod compressed_commitment_tests { CompressedCommitment, }; use curve25519_dalek::ristretto::CompressedRistretto; - use rand::{rngs::StdRng, RngCore, SeedableRng}; + use mc_util_test_helper::{run_with_several_seeds, RngCore}; #[test] // Commitment::new should create the correct RistrettoPoint. fn test_new() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); - let value = rng.next_u64(); - let blinding = Scalar::random(&mut rng); - let generator = generators(0); + run_with_several_seeds(|mut rng| { + let value = rng.next_u64(); + let blinding = Scalar::random(&mut rng); + let generator = generators(0); - let commitment = CompressedCommitment::new(value, blinding, &generator); + let commitment = CompressedCommitment::new(value, blinding, &generator); - let expected_point: CompressedRistretto = { - let H = generator.B; - let G = generator.B_blinding; - let point = Scalar::from(value) * H + blinding * G; - point.compress() - }; + let expected_point: CompressedRistretto = { + let H = generator.B; + let G = generator.B_blinding; + let point = Scalar::from(value) * H + blinding * G; + point.compress() + }; - assert_eq!(commitment.point, expected_point); + assert_eq!(commitment.point, expected_point); + }) } } diff --git a/crypto/ring-signature/src/amount/mod.rs b/crypto/ring-signature/src/amount/mod.rs new file mode 100644 index 0000000000..ac0f5ff15d --- /dev/null +++ b/crypto/ring-signature/src/amount/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + +//! A commitment to an output's amount. This commitment is always specific to +//! a token id, and the value is in the smallest representable units. +//! +//! Amounts are implemented as Pedersen commitments. The associated private keys +//! are "masked" using a shared secret. + +mod commitment; +mod compressed_commitment; + +pub use commitment::Commitment; +pub use compressed_commitment::CompressedCommitment; diff --git a/crypto/ring-signature/src/domain_separators.rs b/crypto/ring-signature/src/domain_separators.rs new file mode 100644 index 0000000000..ac80959ec2 --- /dev/null +++ b/crypto/ring-signature/src/domain_separators.rs @@ -0,0 +1,25 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + +//! Domain separation tags for hash functions used in the MobileCoin amount and +//! MLSAG protocols. +//! +//! Domain separation allows multiple distinct hash functions to be derived from +//! a single base function: +//! Hash_1(X) = Hash("Hash_1" || X), +//! Hash_2(X) = Hash("Hash_2" || X), +//! etc. +//! +//! Here, "Hash_1" and "Hash_2" are called domain separation tags. Tags should +//! uniquely identify the hash function within the protocol and may include the +//! protocol's version so that each derived hash function is independent of +//! others within the protocol and independent of hash functions in other +//! versions of the protocol. + +/// Domain separator for onetime key "hash_to_point" function. +pub const HASH_TO_POINT_DOMAIN_TAG: &str = "mc_onetime_key_hash_to_point"; + +/// Domain separator for onetime key "hash_to_scalar" function. +pub const HASH_TO_SCALAR_DOMAIN_TAG: &str = "mc_onetime_key_hash_to_scalar"; + +/// Domain separator for RingMLSAG's challenges. +pub const RING_MLSAG_CHALLENGE_DOMAIN_TAG: &str = "mc_ring_mlsag_challenge"; diff --git a/crypto/ring-signature/src/lib.rs b/crypto/ring-signature/src/lib.rs new file mode 100644 index 0000000000..887dc3725d --- /dev/null +++ b/crypto/ring-signature/src/lib.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + +//! MobileCoin implementation of amount commitments and MLSAG ring signatures, +//! as well as some related functions (see one-time keys module) + +#![no_std] +#![deny(missing_docs)] + +extern crate alloc; + +use crate::onetime_keys::create_shared_secret; +use mc_crypto_keys::{RistrettoPrivate, RistrettoPublic}; + +mod amount; +mod domain_separators; +mod ring_signature; + +pub mod onetime_keys; +#[cfg(any(test, feature = "proptest"))] +pub mod proptest_fixtures; + +pub use amount::{Commitment, CompressedCommitment}; +pub use ring_signature::{ + generators, CryptoRngCore, CurveScalar, Error, GeneratorCache, KeyImage, PedersenGens, + ReducedTxOut, RingMLSAG, Scalar, +}; + +/// Get the shared secret for a transaction output. +/// +/// # Arguments +/// * `view_key` - The recipient's private View key. +/// * `tx_public_key` - The public key of the transaction. +pub fn get_tx_out_shared_secret( + view_key: &RistrettoPrivate, + tx_public_key: &RistrettoPublic, +) -> RistrettoPublic { + create_shared_secret(tx_public_key, view_key) +} diff --git a/transaction/core/src/onetime_keys.rs b/crypto/ring-signature/src/onetime_keys.rs similarity index 61% rename from transaction/core/src/onetime_keys.rs rename to crypto/ring-signature/src/onetime_keys.rs index f5c5d968d8..9104e21d9d 100644 --- a/transaction/core/src/onetime_keys.rs +++ b/crypto/ring-signature/src/onetime_keys.rs @@ -199,8 +199,8 @@ pub fn create_shared_secret( mod tests { use super::*; use mc_account_keys::AccountKey; - use mc_crypto_rand::McRng; use mc_util_from_random::FromRandom; + use mc_util_test_helper::run_with_several_seeds; // Returns (tx_target_key, tx_public_key) fn get_output_public_keys( @@ -235,188 +235,200 @@ mod tests { // `create_target_key` should produce a public key that agrees with the // recipient's view key. fn test_create_target_key() { - let mut rng = McRng::default(); - let account: AccountKey = AccountKey::random(&mut rng); - let recipient = account.default_subaddress(); - - let tx_private_key = RistrettoPrivate::from_random(&mut rng); - let (tx_target_key, tx_public_key) = get_output_public_keys(&tx_private_key, &recipient); - - assert_eq!( - recipient.spend_public_key(), - &recover_public_subaddress_spend_key( - account.view_private_key(), // (a, D_0) - &tx_target_key, - &tx_public_key - ) - ); - - let other_account = AccountKey::random(&mut rng); - assert_ne!( - other_account.default_subaddress().spend_public_key(), - &recover_public_subaddress_spend_key( - other_account.view_private_key(), - &tx_target_key, - &tx_public_key - ), - "The one-time public key should not match other view keys." - ); + run_with_several_seeds(|mut rng| { + let account: AccountKey = AccountKey::random(&mut rng); + let recipient = account.default_subaddress(); + + let tx_private_key = RistrettoPrivate::from_random(&mut rng); + let (tx_target_key, tx_public_key) = + get_output_public_keys(&tx_private_key, &recipient); + + assert_eq!( + recipient.spend_public_key(), + &recover_public_subaddress_spend_key( + account.view_private_key(), // (a, D_0) + &tx_target_key, + &tx_public_key + ) + ); + + let other_account = AccountKey::random(&mut rng); + assert_ne!( + other_account.default_subaddress().spend_public_key(), + &recover_public_subaddress_spend_key( + other_account.view_private_key(), + &tx_target_key, + &tx_public_key + ), + "The one-time public key should not match other view keys." + ); + }) } #[test] // Should return `r * D`. fn test_create_tx_public_key() { - let mut rng = McRng::default(); - let r = Scalar::random(&mut rng); - let D = RistrettoPoint::random(&mut rng); - - let expected = RistrettoPublic::from(r * D); - - let tx_private_key = RistrettoPrivate::from(r); - let recipient_spend_key = RistrettoPublic::from(D); - assert_eq!( - expected, - create_tx_out_public_key(&tx_private_key, &recipient_spend_key) - ); + run_with_several_seeds(|mut rng| { + let r = Scalar::random(&mut rng); + let D = RistrettoPoint::random(&mut rng); + + let expected = RistrettoPublic::from(r * D); + + let tx_private_key = RistrettoPrivate::from(r); + let recipient_spend_key = RistrettoPublic::from(D); + assert_eq!( + expected, + create_tx_out_public_key(&tx_private_key, &recipient_spend_key) + ); + }) } #[test] // Should recover the correct public subaddress spend key D_i when the output // belongs to the recipient. fn test_recover_public_subaddress_spend_key_ok() { - let mut rng = McRng::default(); - let account: AccountKey = AccountKey::random(&mut rng); - let (_c, _d, recipient) = get_subaddress(&account, 7); - - let tx_private_key = RistrettoPrivate::from_random(&mut rng); - let (tx_target_key, tx_public_key) = get_output_public_keys(&tx_private_key, &recipient); - - let D_prime = recover_public_subaddress_spend_key( - account.view_private_key(), - &tx_target_key, - &tx_public_key, - ); + run_with_several_seeds(|mut rng| { + let account: AccountKey = AccountKey::random(&mut rng); + let (_c, _d, recipient) = get_subaddress(&account, 7); - assert_eq!(D_prime, *recipient.spend_public_key()); // D_7 + let tx_private_key = RistrettoPrivate::from_random(&mut rng); + let (tx_target_key, tx_public_key) = + get_output_public_keys(&tx_private_key, &recipient); - assert_eq!( - recipient.spend_public_key(), - &recover_public_subaddress_spend_key( + let D_prime = recover_public_subaddress_spend_key( account.view_private_key(), &tx_target_key, - &tx_public_key - ) - ); + &tx_public_key, + ); + + assert_eq!(D_prime, *recipient.spend_public_key()); // D_7 + + assert_eq!( + recipient.spend_public_key(), + &recover_public_subaddress_spend_key( + account.view_private_key(), + &tx_target_key, + &tx_public_key + ) + ); + }) } #[test] // Should not panic if the output contains the wrong tx_target_key. fn test_recover_public_subaddress_spend_key_wrong_tx_target_key() { - let mut rng = McRng::default(); - let account: AccountKey = AccountKey::random(&mut rng); - let (_c, _d, recipient) = get_subaddress(&account, 7); - - let tx_private_key = RistrettoPrivate::from_random(&mut rng); - let (_, tx_public_key) = get_output_public_keys(&tx_private_key, &recipient); - let wrong_tx_target_key = RistrettoPublic::from_random(&mut rng); - - // Should not panic. - let D_prime = recover_public_subaddress_spend_key( - account.view_private_key(), - &wrong_tx_target_key, - &tx_public_key, - ); + run_with_several_seeds(|mut rng| { + let account: AccountKey = AccountKey::random(&mut rng); + let (_c, _d, recipient) = get_subaddress(&account, 7); + + let tx_private_key = RistrettoPrivate::from_random(&mut rng); + let (_, tx_public_key) = get_output_public_keys(&tx_private_key, &recipient); + let wrong_tx_target_key = RistrettoPublic::from_random(&mut rng); - // Returns meaningless public key. - assert!(D_prime != *recipient.spend_public_key()); + // Should not panic. + let D_prime = recover_public_subaddress_spend_key( + account.view_private_key(), + &wrong_tx_target_key, + &tx_public_key, + ); + + // Returns meaningless public key. + assert!(D_prime != *recipient.spend_public_key()); + }) } #[test] // Should not panic if the output contains the wrong tx_public_key. fn test_recover_public_subaddress_spend_key_wrong_tx_public_key() { - let mut rng = McRng::default(); - let account: AccountKey = AccountKey::random(&mut rng); - let (_c, _d, recipient) = get_subaddress(&account, 7); - - let tx_private_key = RistrettoPrivate::from_random(&mut rng); - let (tx_target_key, _) = get_output_public_keys(&tx_private_key, &recipient); - let wrong_tx_public_key = RistrettoPublic::from_random(&mut rng); - - // Should not panic. - let D_prime = recover_public_subaddress_spend_key( - account.view_private_key(), - &tx_target_key, - &wrong_tx_public_key, - ); + run_with_several_seeds(|mut rng| { + let account: AccountKey = AccountKey::random(&mut rng); + let (_c, _d, recipient) = get_subaddress(&account, 7); - // Returns meaningless public key. - assert!(D_prime != *recipient.spend_public_key()); + let tx_private_key = RistrettoPrivate::from_random(&mut rng); + let (tx_target_key, _) = get_output_public_keys(&tx_private_key, &recipient); + let wrong_tx_public_key = RistrettoPublic::from_random(&mut rng); + + // Should not panic. + let D_prime = recover_public_subaddress_spend_key( + account.view_private_key(), + &tx_target_key, + &wrong_tx_public_key, + ); + + // Returns meaningless public key. + assert!(D_prime != *recipient.spend_public_key()); + }) } #[test] // Returns the private key corresponding to `tx_target_key`. fn test_recover_onetime_private_key_valid_keypair() { - let mut rng = McRng::default(); - let account = AccountKey::random(&mut rng); - let (_c, d, recipient) = get_subaddress(&account, 787); - - let tx_private_key = RistrettoPrivate::from_random(&mut rng); - let (tx_target_key, tx_public_key) = get_output_public_keys(&tx_private_key, &recipient); - - let onetime_private_key = - recover_onetime_private_key(&tx_public_key, account.view_private_key(), &d); - assert_eq!(tx_target_key, RistrettoPublic::from(&onetime_private_key)); + run_with_several_seeds(|mut rng| { + let account = AccountKey::random(&mut rng); + let (_c, d, recipient) = get_subaddress(&account, 787); + + let tx_private_key = RistrettoPrivate::from_random(&mut rng); + let (tx_target_key, tx_public_key) = + get_output_public_keys(&tx_private_key, &recipient); + + let onetime_private_key = + recover_onetime_private_key(&tx_public_key, account.view_private_key(), &d); + assert_eq!(tx_target_key, RistrettoPublic::from(&onetime_private_key)); + }) } #[test] // Returns meaningless data if the output contains the wrong tx_target_key. fn test_recover_onetime_private_key_wrong_tx_target_key() { - let mut rng = McRng::default(); - let account = AccountKey::random(&mut rng); - let (_c, d, recipient) = get_subaddress(&account, 787); + run_with_several_seeds(|mut rng| { + let account = AccountKey::random(&mut rng); + let (_c, d, recipient) = get_subaddress(&account, 787); - let tx_private_key = RistrettoPrivate::from_random(&mut rng); - let (_, tx_public_key) = get_output_public_keys(&tx_private_key, &recipient); + let tx_private_key = RistrettoPrivate::from_random(&mut rng); + let (_, tx_public_key) = get_output_public_keys(&tx_private_key, &recipient); - let wrong_tx_target_key = RistrettoPublic::from_random(&mut rng); + let wrong_tx_target_key = RistrettoPublic::from_random(&mut rng); - let onetime_private_key = - recover_onetime_private_key(&tx_public_key, account.view_private_key(), &d); + let onetime_private_key = + recover_onetime_private_key(&tx_public_key, account.view_private_key(), &d); - assert!(wrong_tx_target_key != RistrettoPublic::from(&onetime_private_key)); + assert!(wrong_tx_target_key != RistrettoPublic::from(&onetime_private_key)); + }) } #[test] // Returns meaningless data if the output contains the wrong tx_public_key. fn test_recover_onetime_private_key_wrong_tx_public_key() { - let mut rng = McRng::default(); - let account = AccountKey::random(&mut rng); - let (_c, d, recipient) = get_subaddress(&account, 787); + run_with_several_seeds(|mut rng| { + let account = AccountKey::random(&mut rng); + let (_c, d, recipient) = get_subaddress(&account, 787); - let tx_private_key = RistrettoPrivate::from_random(&mut rng); - let (tx_target_key, _) = get_output_public_keys(&tx_private_key, &recipient); + let tx_private_key = RistrettoPrivate::from_random(&mut rng); + let (tx_target_key, _) = get_output_public_keys(&tx_private_key, &recipient); - let wrong_tx_public_key = RistrettoPublic::from_random(&mut rng); + let wrong_tx_public_key = RistrettoPublic::from_random(&mut rng); - let onetime_private_key = - recover_onetime_private_key(&wrong_tx_public_key, account.view_private_key(), &d); + let onetime_private_key = + recover_onetime_private_key(&wrong_tx_public_key, account.view_private_key(), &d); - assert!(tx_target_key != RistrettoPublic::from(&onetime_private_key)); + assert!(tx_target_key != RistrettoPublic::from(&onetime_private_key)); + }) } #[test] // shared_secret(a,B) should equal shared_secret(b,A) fn test_create_shared_secret_is_symmetric() { - let mut rng = McRng::default(); - let a = RistrettoPrivate::from_random(&mut rng); - let A = RistrettoPublic::from(&a); + run_with_several_seeds(|mut rng| { + let a = RistrettoPrivate::from_random(&mut rng); + let A = RistrettoPublic::from(&a); - let b = RistrettoPrivate::from_random(&mut rng); - let B = RistrettoPublic::from(&b); + let b = RistrettoPrivate::from_random(&mut rng); + let B = RistrettoPublic::from(&b); - let aB = create_shared_secret(&B, &a); - let bA = create_shared_secret(&A, &b); + let aB = create_shared_secret(&B, &a); + let bA = create_shared_secret(&A, &b); - assert_eq!(aB, bA); + assert_eq!(aB, bA); + }) } } diff --git a/crypto/ring-signature/src/proptest_fixtures.rs b/crypto/ring-signature/src/proptest_fixtures.rs new file mode 100644 index 0000000000..407f1e76f8 --- /dev/null +++ b/crypto/ring-signature/src/proptest_fixtures.rs @@ -0,0 +1,27 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + +//! Fixtures for use with proptest + +use crate::{CurveScalar, Scalar}; +use mc_crypto_keys::{RistrettoPrivate, RistrettoPublic}; +use proptest::prelude::*; + +/// Generates an arbitrary Scalar. +pub fn arbitrary_scalar() -> impl Strategy { + any::<[u8; 32]>().prop_map(Scalar::from_bytes_mod_order) +} + +/// Generates an arbitrary CurveScalar. +pub fn arbitrary_curve_scalar() -> impl Strategy { + arbitrary_scalar().prop_map(CurveScalar::from) +} + +/// Generates an arbitrary RistrettoPrivate key. +pub fn arbitrary_ristretto_private() -> impl Strategy { + arbitrary_scalar().prop_map(RistrettoPrivate::from) +} + +/// Generates an arbitrary RistrettoPublic key. +pub fn arbitrary_ristretto_public() -> impl Strategy { + arbitrary_ristretto_private().prop_map(|private_key| RistrettoPublic::from(&private_key)) +} diff --git a/transaction/core/src/ring_signature/curve_scalar.rs b/crypto/ring-signature/src/ring_signature/curve_scalar.rs similarity index 100% rename from transaction/core/src/ring_signature/curve_scalar.rs rename to crypto/ring-signature/src/ring_signature/curve_scalar.rs diff --git a/crypto/ring-signature/src/ring_signature/error.rs b/crypto/ring-signature/src/ring_signature/error.rs new file mode 100644 index 0000000000..e5a2ebd90c --- /dev/null +++ b/crypto/ring-signature/src/ring_signature/error.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + +//! Errors which can occur in connection to RingMLSAG signatures + +use displaydoc::Display; +use serde::{Deserialize, Serialize}; + +/// An error which can occur when signing or verifying an MLSAG +#[derive(Clone, Debug, Deserialize, Display, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum Error { + /// Incorrect length for array copy, provided `{0}`, required `{1}`. + LengthMismatch(usize, usize), + + /// Index out of bounds + IndexOutOfBounds, + + /// Invalid curve point + InvalidCurvePoint, + + /// Invalid curve scalar + InvalidCurveScalar, + + /// The signature was not able to be validated + InvalidSignature, + + /// Failed to compress/decompress a KeyImage + InvalidKeyImage, + + /// Value not conserved + ValueNotConserved, +} + +impl From for Error { + fn from(src: mc_util_repr_bytes::LengthMismatch) -> Self { + Self::LengthMismatch(src.found, src.expected) + } +} diff --git a/transaction/core/src/ring_signature/generator_cache.rs b/crypto/ring-signature/src/ring_signature/generator_cache.rs similarity index 95% rename from transaction/core/src/ring_signature/generator_cache.rs rename to crypto/ring-signature/src/ring_signature/generator_cache.rs index e7162a25a0..2614561edd 100644 --- a/transaction/core/src/ring_signature/generator_cache.rs +++ b/crypto/ring-signature/src/ring_signature/generator_cache.rs @@ -3,8 +3,8 @@ //! A simple generator cache use super::{generators, PedersenGens}; -use crate::TokenId; use alloc::collections::BTreeMap; +use mc_transaction_types::TokenId; /// GeneratorCache is a simple object which caches computations of /// generator: TokenId -> PedersenGens diff --git a/transaction/core/src/ring_signature/key_image.rs b/crypto/ring-signature/src/ring_signature/key_image.rs similarity index 100% rename from transaction/core/src/ring_signature/key_image.rs rename to crypto/ring-signature/src/ring_signature/key_image.rs diff --git a/transaction/core/src/ring_signature/mlsag.rs b/crypto/ring-signature/src/ring_signature/mlsag.rs similarity index 91% rename from transaction/core/src/ring_signature/mlsag.rs rename to crypto/ring-signature/src/ring_signature/mlsag.rs index 25ebef9eb0..da1186fa71 100644 --- a/transaction/core/src/ring_signature/mlsag.rs +++ b/crypto/ring-signature/src/ring_signature/mlsag.rs @@ -6,7 +6,6 @@ use alloc::{vec, vec::Vec}; use core::convert::TryFrom; use curve25519_dalek::ristretto::RistrettoPoint; -use displaydoc::Display; use mc_crypto_digestible::Digestible; use mc_crypto_hashes::{Blake2b512, Digest}; use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; @@ -17,7 +16,9 @@ use zeroize::Zeroizing; use crate::{ domain_separators::RING_MLSAG_CHALLENGE_DOMAIN_TAG, - ring_signature::{hash_to_point, CurveScalar, KeyImage, PedersenGens, Scalar, B_BLINDING}, + ring_signature::{ + hash_to_point, CurveScalar, Error, KeyImage, PedersenGens, Scalar, B_BLINDING, + }, Commitment, CompressedCommitment, }; @@ -90,7 +91,7 @@ impl RingMLSAG { output_blinding: &Scalar, generator: &PedersenGens, rng: &mut dyn CryptoRngCore, - ) -> Result { + ) -> Result { RingMLSAG::sign_with_balance_check( message, ring, @@ -137,11 +138,11 @@ impl RingMLSAG { // Note: this `mut rng` can just be `rng` if this is merged upstream: // https://github.com/dalek-cryptography/curve25519-dalek/pull/394 mut rng: &mut dyn CryptoRngCore, - ) -> Result { + ) -> Result { let ring_size = ring.len(); if real_index >= ring_size { - return Err(MLSAGError::IndexOutOfBounds); + return Err(Error::IndexOutOfBounds); } let G = B_BLINDING; @@ -153,10 +154,7 @@ impl RingMLSAG { let key_image = KeyImage::from(onetime_private_key); // The uncompressed key_image. - let I: RistrettoPoint = key_image - .point - .decompress() - .ok_or(MLSAGError::InvalidKeyImage)?; + let I: RistrettoPoint = key_image.point.decompress().ok_or(Error::InvalidKeyImage)?; // Uncompressed output commitment. // This ensures that each address and commitment encodes a valid Ristretto @@ -236,7 +234,7 @@ impl RingMLSAG { let (_, input_commitment) = decompressed_ring[real_index]; let difference: RistrettoPoint = output_commitment.point - input_commitment.point; if difference != (z * G) { - return Err(MLSAGError::ValueNotConserved); + return Err(Error::ValueNotConserved); } } @@ -260,14 +258,11 @@ impl RingMLSAG { message: &[u8], ring: &[ReducedTxOut], output_commitment: &CompressedCommitment, - ) -> Result<(), MLSAGError> { + ) -> Result<(), Error> { let ring_size = ring.len(); // `responses` must contain `2 * ring_size` elements. if self.responses.len() != 2 * ring_size { - return Err(MLSAGError::LengthMismatch( - 2 * ring_size, - self.responses.len(), - )); + return Err(Error::LengthMismatch(2 * ring_size, self.responses.len())); } let G = B_BLINDING; @@ -278,7 +273,7 @@ impl RingMLSAG { .key_image .point .decompress() - .ok_or(MLSAGError::InvalidKeyImage)?; + .ok_or(Error::InvalidKeyImage)?; let r: Vec = self .responses @@ -296,13 +291,13 @@ impl RingMLSAG { // Scalars must be canonical. if !self.c_zero.scalar.is_canonical() { - return Err(MLSAGError::InvalidCurveScalar); + return Err(Error::InvalidCurveScalar); } // Scalars must be canonical. for response in &self.responses { if !response.scalar.is_canonical() { - return Err(MLSAGError::InvalidCurveScalar); + return Err(Error::InvalidCurveScalar); } } @@ -337,7 +332,7 @@ impl RingMLSAG { if self.c_zero.scalar == recomputed_c[0] { Ok(()) } else { - Err(MLSAGError::InvalidSignature) + Err(Error::InvalidSignature) } } } @@ -360,67 +355,28 @@ fn challenge( Scalar::from_hash(hasher) } -fn decompress_ring( - ring: &[ReducedTxOut], -) -> Result, MLSAGError> { +fn decompress_ring(ring: &[ReducedTxOut]) -> Result, Error> { // Ring must decompress. let mut decompressed_ring: Vec<(RistrettoPublic, Commitment)> = Vec::new(); for tx_out in ring { - let ristretto_public = RistrettoPublic::try_from(&tx_out.target_key) - .map_err(|_e| MLSAGError::InvalidCurvePoint)?; + let ristretto_public = + RistrettoPublic::try_from(&tx_out.target_key).map_err(|_e| Error::InvalidCurvePoint)?; let commitment = Commitment::try_from(&tx_out.commitment)?; decompressed_ring.push((ristretto_public, commitment)); } Ok(decompressed_ring) } -/// An error which can occur when signing or verifying -#[derive(Clone, Debug, Deserialize, Display, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub enum MLSAGError { - /// Incorrect length for array copy, provided `{0}`, required `{1}`. - LengthMismatch(usize, usize), - - /// Index out of bounds - IndexOutOfBounds, - - /// Invalid curve point - InvalidCurvePoint, - - /// Invalid curve scalar - InvalidCurveScalar, - - /// The signature was not able to be validated - InvalidSignature, - - /// Failed to compress/decompress a KeyImage - InvalidKeyImage, - - /// Value not conserved - ValueNotConserved, -} - -impl From for MLSAGError { - fn from(src: mc_util_repr_bytes::LengthMismatch) -> Self { - Self::LengthMismatch(src.found, src.expected) - } -} - #[cfg(test)] mod mlsag_tests { - use crate::{ - ring_signature::{ - generators, mlsag::RingMLSAG, CurveScalar, KeyImage, MLSAGError as Error, PedersenGens, - ReducedTxOut, Scalar, - }, - CompressedCommitment, - }; + use super::*; + use crate::generators; use alloc::vec::Vec; use curve25519_dalek::ristretto::CompressedRistretto; use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; use mc_util_from_random::FromRandom; + use mc_util_test_helper::{RngCore, RngType, SeedableRng}; use proptest::prelude::*; - use rand::{rngs::StdRng, CryptoRng, SeedableRng}; - use rand_core::RngCore; extern crate std; @@ -536,7 +492,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let params = @@ -559,7 +515,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); @@ -575,7 +531,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let mut params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); let wrong_value = rng.next_u64(); @@ -594,7 +550,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let mut params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); // The ring contains num_mixins + 1 elements, with indices 0..num_mixins. @@ -617,7 +573,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); @@ -636,7 +592,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let mut params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); let wrong_onetime_private_key = RistrettoPrivate::from_random(&mut rng); @@ -658,7 +614,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); @@ -709,7 +665,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); @@ -739,7 +695,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); @@ -763,7 +719,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); @@ -789,7 +745,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let mut params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); @@ -832,7 +788,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); @@ -855,7 +811,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); @@ -899,7 +855,7 @@ mod mlsag_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let pseudo_output_blinding = Scalar::random(&mut rng); let params = RingMLSAGParameters::random(num_mixins, pseudo_output_blinding, &mut rng); diff --git a/transaction/core/src/ring_signature/mod.rs b/crypto/ring-signature/src/ring_signature/mod.rs similarity index 75% rename from transaction/core/src/ring_signature/mod.rs rename to crypto/ring-signature/src/ring_signature/mod.rs index 2c2bf7d4b6..4d780e461f 100644 --- a/transaction/core/src/ring_signature/mod.rs +++ b/crypto/ring-signature/src/ring_signature/mod.rs @@ -4,7 +4,7 @@ #![allow(non_snake_case)] -pub use bulletproofs_og::{BulletproofGens, PedersenGens}; +use curve25519_dalek::traits::MultiscalarMul; pub use curve25519_dalek::{ristretto::RistrettoPoint, scalar::Scalar}; use crate::domain_separators::HASH_TO_POINT_DOMAIN_TAG; @@ -17,24 +17,34 @@ mod error; mod generator_cache; mod key_image; mod mlsag; -mod rct_bulletproofs; pub use curve_scalar::*; pub use error::Error; pub use generator_cache::*; pub use key_image::*; pub use mlsag::*; -pub use rct_bulletproofs::*; /// The base point for blinding factors used with all amount commitments pub const B_BLINDING: RistrettoPoint = RISTRETTO_BASEPOINT_POINT; -lazy_static! { - /// Generators (base points) for Bulletproofs. - /// The `party_capacity` is the maximum number of values in one proof. It should - /// be at least 2 * MAX_INPUTS + MAX_OUTPUTS, which allows for inputs, pseudo outputs, and outputs. - pub static ref BP_GENERATORS: BulletproofGens = - BulletproofGens::new(64, 64); +/// This is a structure which contains a pair of orthogonal generators for +/// Pedersen commitments. +/// This tracks `bulletproofs::PedersenGens`, but we do not import it, to avoid +/// creating a dependency on the `bulletproofs` crate. +#[derive(Clone, Copy, Debug)] +pub struct PedersenGens { + /// Base point corresponding to the value of a Pedersen commitment + pub B: RistrettoPoint, + /// Base point corresponding to the blinding factor of a Pedersen commitment + pub B_blinding: RistrettoPoint, +} + +impl PedersenGens { + /// Creates a Pedersen commitment using the value scalar and a blinding + /// factor. + pub fn commit(&self, value: Scalar, blinding: Scalar) -> RistrettoPoint { + RistrettoPoint::multiscalar_mul(&[value, blinding], &[self.B, self.B_blinding]) + } } /// Generators (base points) for Pedersen commitments to amounts. diff --git a/fog/distribution/Cargo.toml b/fog/distribution/Cargo.toml index 2783437192..f50b757cb2 100644 --- a/fog/distribution/Cargo.toml +++ b/fog/distribution/Cargo.toml @@ -17,6 +17,7 @@ mc-common = { path = "../../common", features = ["log"] } mc-connection = { path = "../../connection" } mc-consensus-enclave-measurement = { path = "../../consensus/enclave/measurement" } mc-crypto-keys = { path = "../../crypto/keys" } +mc-crypto-ring-signature-signer = { path = "../../crypto/ring-signature/signer" } mc-fog-ingest-enclave-measurement = { path = "../ingest/enclave/measurement" } mc-fog-report-connection = { path = "../../fog/report/connection" } mc-fog-report-validation = { path = "../../fog/report/validation" } diff --git a/fog/distribution/src/main.rs b/fog/distribution/src/main.rs index c88a99cb6f..6e1ca0593a 100755 --- a/fog/distribution/src/main.rs +++ b/fog/distribution/src/main.rs @@ -32,6 +32,7 @@ use mc_connection::{ RetryableBlockchainConnection, RetryableUserTxConnection, SyncConnection, ThickClient, }; use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPublic}; +use mc_crypto_ring_signature_signer::NoKeysRingSigner; use mc_fog_distribution::Config; use mc_fog_report_connection::{Error as ReportConnError, GrpcFogReportConnection}; use mc_fog_report_validation::FogResolver; @@ -40,7 +41,6 @@ use mc_transaction_core::{ get_tx_out_shared_secret, onetime_keys::recover_onetime_private_key, ring_signature::KeyImage, - signer::NoKeysRingSigner, tokens::Mob, tx::{Tx, TxOut, TxOutMembershipProof}, validation::TransactionValidationError, diff --git a/fog/ingest/enclave/trusted/Cargo.lock b/fog/ingest/enclave/trusted/Cargo.lock index 1e9b8960c5..ccb0e9bf9e 100644 --- a/fog/ingest/enclave/trusted/Cargo.lock +++ b/fog/ingest/enclave/trusted/Cargo.lock @@ -941,6 +941,48 @@ dependencies = [ "rand_hc", ] +[[package]] +name = "mc-crypto-ring-signature" +version = "1.3.0-pre0" +dependencies = [ + "curve25519-dalek", + "displaydoc", + "hex_fmt", + "mc-account-keys", + "mc-crypto-digestible", + "mc-crypto-hashes", + "mc-crypto-keys", + "mc-transaction-types", + "mc-util-from-random", + "mc-util-repr-bytes", + "mc-util-serial", + "prost", + "rand_core", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "mc-crypto-ring-signature-signer" +version = "1.3.0-pre0" +dependencies = [ + "curve25519-dalek", + "displaydoc", + "generic-array", + "hex_fmt", + "mc-account-keys", + "mc-crypto-keys", + "mc-crypto-ring-signature", + "mc-transaction-types", + "mc-util-serial", + "prost", + "rand_core", + "serde", + "subtle", + "zeroize", +] + [[package]] name = "mc-enclave-boundary" version = "1.3.0-pre0" @@ -1295,6 +1337,9 @@ dependencies = [ "mc-crypto-hashes", "mc-crypto-keys", "mc-crypto-multisig", + "mc-crypto-ring-signature", + "mc-crypto-ring-signature-signer", + "mc-transaction-types", "mc-util-from-random", "mc-util-repr-bytes", "mc-util-serial", @@ -1308,6 +1353,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mc-transaction-types" +version = "1.3.0-pre0" +dependencies = [ + "mc-crypto-digestible", + "serde", + "subtle", + "zeroize", +] + [[package]] name = "mc-util-build-script" version = "1.3.0-pre0" diff --git a/fog/ledger/enclave/trusted/Cargo.lock b/fog/ledger/enclave/trusted/Cargo.lock index de4a03452b..40c3a47e28 100644 --- a/fog/ledger/enclave/trusted/Cargo.lock +++ b/fog/ledger/enclave/trusted/Cargo.lock @@ -945,6 +945,48 @@ dependencies = [ "rand_hc", ] +[[package]] +name = "mc-crypto-ring-signature" +version = "1.3.0-pre0" +dependencies = [ + "curve25519-dalek", + "displaydoc", + "hex_fmt", + "mc-account-keys", + "mc-crypto-digestible", + "mc-crypto-hashes", + "mc-crypto-keys", + "mc-transaction-types", + "mc-util-from-random", + "mc-util-repr-bytes", + "mc-util-serial", + "prost", + "rand_core", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "mc-crypto-ring-signature-signer" +version = "1.3.0-pre0" +dependencies = [ + "curve25519-dalek", + "displaydoc", + "generic-array", + "hex_fmt", + "mc-account-keys", + "mc-crypto-keys", + "mc-crypto-ring-signature", + "mc-transaction-types", + "mc-util-serial", + "prost", + "rand_core", + "serde", + "subtle", + "zeroize", +] + [[package]] name = "mc-enclave-boundary" version = "1.3.0-pre0" @@ -1280,6 +1322,9 @@ dependencies = [ "mc-crypto-hashes", "mc-crypto-keys", "mc-crypto-multisig", + "mc-crypto-ring-signature", + "mc-crypto-ring-signature-signer", + "mc-transaction-types", "mc-util-from-random", "mc-util-repr-bytes", "mc-util-serial", @@ -1293,6 +1338,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mc-transaction-types" +version = "1.3.0-pre0" +dependencies = [ + "mc-crypto-digestible", + "serde", + "subtle", + "zeroize", +] + [[package]] name = "mc-util-build-script" version = "1.3.0-pre0" diff --git a/fog/sample-paykit/Cargo.toml b/fog/sample-paykit/Cargo.toml index 789d6f087f..c63c6cb25f 100644 --- a/fog/sample-paykit/Cargo.toml +++ b/fog/sample-paykit/Cargo.toml @@ -25,6 +25,8 @@ mc-consensus-api = { path = "../../consensus/api" } mc-consensus-enclave-measurement = { path = "../../consensus/enclave/measurement" } mc-crypto-keys = { path = "../../crypto/keys" } mc-crypto-rand = { path = "../../crypto/rand" } +mc-crypto-ring-signature = { path = "../../crypto/ring-signature" } +mc-crypto-ring-signature-signer = { path = "../../crypto/ring-signature/signer" } mc-sgx-css = { path = "../../sgx/css" } mc-transaction-core = { path = "../../transaction/core" } mc-transaction-std = { path = "../../transaction/std" } diff --git a/fog/sample-paykit/src/client.rs b/fog/sample-paykit/src/client.rs index 6a109987b2..015148498a 100644 --- a/fog/sample-paykit/src/client.rs +++ b/fog/sample-paykit/src/client.rs @@ -16,6 +16,7 @@ use mc_connection::{ }; use mc_crypto_keys::CompressedRistrettoPublic; use mc_crypto_rand::{CryptoRng, RngCore}; +use mc_crypto_ring_signature_signer::{LocalRingSigner, OneTimeKeyDeriveData, RingSigner}; use mc_fog_api::ledger::TxOutResultCode; use mc_fog_ledger_connection::{ FogBlockGrpcClient, FogKeyImageGrpcClient, FogMerkleProofGrpcClient, @@ -26,7 +27,6 @@ use mc_fog_report_validation::{FogPubkeyResolver, FogResolver}; use mc_fog_types::{ledger::KeyImageResultCode, BlockCount}; use mc_fog_view_connection::FogViewGrpcClient; use mc_transaction_core::{ - signer::{LocalRingSigner, OneTimeKeyDeriveData, RingSigner}, tx::{Tx, TxOut, TxOutMembershipProof}, Amount, BlockIndex, BlockVersion, SignedContingentInput, TokenId, }; diff --git a/fog/view/enclave/trusted/Cargo.lock b/fog/view/enclave/trusted/Cargo.lock index 2cb43cb71d..153a1cdcae 100644 --- a/fog/view/enclave/trusted/Cargo.lock +++ b/fog/view/enclave/trusted/Cargo.lock @@ -951,6 +951,48 @@ dependencies = [ "rand_hc", ] +[[package]] +name = "mc-crypto-ring-signature" +version = "1.3.0-pre0" +dependencies = [ + "curve25519-dalek", + "displaydoc", + "hex_fmt", + "mc-account-keys", + "mc-crypto-digestible", + "mc-crypto-hashes", + "mc-crypto-keys", + "mc-transaction-types", + "mc-util-from-random", + "mc-util-repr-bytes", + "mc-util-serial", + "prost", + "rand_core", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "mc-crypto-ring-signature-signer" +version = "1.3.0-pre0" +dependencies = [ + "curve25519-dalek", + "displaydoc", + "generic-array", + "hex_fmt", + "mc-account-keys", + "mc-crypto-keys", + "mc-crypto-ring-signature", + "mc-transaction-types", + "mc-util-serial", + "prost", + "rand_core", + "serde", + "subtle", + "zeroize", +] + [[package]] name = "mc-enclave-boundary" version = "1.3.0-pre0" @@ -1314,6 +1356,9 @@ dependencies = [ "mc-crypto-hashes", "mc-crypto-keys", "mc-crypto-multisig", + "mc-crypto-ring-signature", + "mc-crypto-ring-signature-signer", + "mc-transaction-types", "mc-util-from-random", "mc-util-repr-bytes", "mc-util-serial", @@ -1327,6 +1372,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mc-transaction-types" +version = "1.3.0-pre0" +dependencies = [ + "mc-crypto-digestible", + "serde", + "subtle", + "zeroize", +] + [[package]] name = "mc-util-build-script" version = "1.3.0-pre0" diff --git a/libmobilecoin/Cargo.toml b/libmobilecoin/Cargo.toml index 15b79d16d2..aae23f3109 100644 --- a/libmobilecoin/Cargo.toml +++ b/libmobilecoin/Cargo.toml @@ -40,6 +40,7 @@ mc-crypto-box = { path = "../crypto/box" } mc-crypto-keys = { path = "../crypto/keys" } mc-crypto-noise = { path = "../crypto/noise" } mc-crypto-rand = { path = "../crypto/rand", features = ["std"] } +mc-crypto-ring-signature-signer = { path = "../crypto/ring-signature/signer", default-features = false } mc-crypto-sig = { path = "../crypto/sig" } mc-fog-kex-rng = { path = "../fog/kex_rng" } mc-fog-report-validation = { path = "../fog/report/validation" } diff --git a/libmobilecoin/src/transaction.rs b/libmobilecoin/src/transaction.rs index 8c1954f202..778cc5e65f 100644 --- a/libmobilecoin/src/transaction.rs +++ b/libmobilecoin/src/transaction.rs @@ -12,17 +12,16 @@ use crc::Crc; use generic_array::{typenum::U66, GenericArray}; use mc_account_keys::{AccountKey, PublicAddress, ShortAddressHash}; use mc_crypto_keys::{CompressedRistrettoPublic, ReprBytes, RistrettoPrivate, RistrettoPublic}; +use mc_crypto_ring_signature_signer::NoKeysRingSigner; use mc_fog_report_validation::FogResolver; use mc_transaction_core::{ get_tx_out_shared_secret, onetime_keys::{recover_onetime_private_key, recover_public_subaddress_spend_key}, ring_signature::KeyImage, - signer::NoKeysRingSigner, tokens::Mob, tx::{TxOut, TxOutConfirmationNumber, TxOutMembershipProof}, Amount, BlockVersion, CompressedCommitment, EncryptedMemo, MaskedAmount, Token, }; - use mc_transaction_std::{ AuthenticatedSenderMemo, AuthenticatedSenderWithPaymentRequestIdMemo, DestinationMemo, InputCredentials, MemoBuilder, MemoPayload, RTHMemoBuilder, ReservedSubaddresses, diff --git a/mobilecoind/Cargo.toml b/mobilecoind/Cargo.toml index 19ac463d73..ba0618d028 100644 --- a/mobilecoind/Cargo.toml +++ b/mobilecoind/Cargo.toml @@ -27,6 +27,7 @@ mc-crypto-digestible = { path = "../crypto/digestible", features = ["derive"] } mc-crypto-hashes = { path = "../crypto/hashes" } mc-crypto-keys = { path = "../crypto/keys" } mc-crypto-rand = { path = "../crypto/rand" } +mc-crypto-ring-signature-signer = { path = "../crypto/ring-signature/signer" } mc-fog-report-connection = { path = "../fog/report/connection" } mc-fog-report-validation = { path = "../fog/report/validation" } mc-ledger-db = { path = "../ledger/db" } diff --git a/mobilecoind/src/payments.rs b/mobilecoind/src/payments.rs index 861489fa62..f747a9dc54 100644 --- a/mobilecoind/src/payments.rs +++ b/mobilecoind/src/payments.rs @@ -14,13 +14,13 @@ use mc_connection::{ }; use mc_crypto_keys::RistrettoPublic; use mc_crypto_rand::{CryptoRng, RngCore}; +use mc_crypto_ring_signature_signer::NoKeysRingSigner; use mc_fog_report_validation::FogPubkeyResolver; use mc_ledger_db::{Error as LedgerError, Ledger, LedgerDB}; use mc_transaction_core::{ constants::{MAX_INPUTS, MILLIMOB_TO_PICOMOB, RING_SIZE}, onetime_keys::recover_onetime_private_key, ring_signature::KeyImage, - signer::NoKeysRingSigner, tx::{Tx, TxOut, TxOutConfirmationNumber, TxOutMembershipProof}, Amount, BlockIndex, BlockVersion, TokenId, }; diff --git a/transaction/core/Cargo.toml b/transaction/core/Cargo.toml index 214479223d..282a00ca3e 100644 --- a/transaction/core/Cargo.toml +++ b/transaction/core/Cargo.toml @@ -3,6 +3,7 @@ name = "mc-transaction-core" version = "1.3.0-pre0" authors = ["MobileCoin"] edition = "2021" +readme = "README.md" [dependencies] # External dependencies @@ -29,6 +30,9 @@ mc-crypto-digestible = { path = "../../crypto/digestible", features = ["dalek", mc-crypto-hashes = { path = "../../crypto/hashes" } mc-crypto-keys = { path = "../../crypto/keys", default-features = false } mc-crypto-multisig = { path = "../../crypto/multisig", default-features = false } +mc-crypto-ring-signature = { path = "../../crypto/ring-signature", default-features = false } +mc-crypto-ring-signature-signer = { path = "../../crypto/ring-signature/signer", default-features = false } +mc-transaction-types = { path = "../types" } mc-util-from-random = { path = "../../util/from-random" } mc-util-repr-bytes = { path = "../../util/repr-bytes" } mc-util-serial = { path = "../../util/serial" } @@ -43,23 +47,17 @@ bulletproofs-og = { version = "3.0.0-pre.1", default-features = false } [target.'cfg(any(target_feature = "avx2", target_feature = "avx"))'.dependencies] curve25519-dalek = { version = "4.0.0-pre.2", default-features = false, features = ["simd_backend", "nightly"] } -[dev-dependencies.proptest] -version = "1.0" # Only works for 0.9.1 or newer -default-features = false -# Enable all default features not known to break code coverage builds -features = ["default-code-coverage"] - [target.'cfg(not(any(target_feature = "avx2", target_feature = "avx")))'.dependencies] curve25519-dalek = { version = "4.0.0-pre.2", default-features = false, features = ["nightly", "u64_backend"] } [dev-dependencies] assert_matches = "1.5" +proptest = { version = "1.0", default-features = false, features = ["default-code-coverage"] } rand = "0.8" -rand_hc = "0.3" tempdir = "0.3" mc-crypto-digestible-test-utils = { path = "../../crypto/digestible/test-utils" } -mc-crypto-rand = { path = "../../crypto/rand" } +mc-crypto-ring-signature = { path = "../../crypto/ring-signature", features = ["proptest"] } mc-ledger-db = { path = "../../ledger/db" } mc-transaction-core-test-utils = { path = "../../transaction/core/test-utils" } mc-transaction-std = { path = "../../transaction/std", features = ["test-only"] } diff --git a/transaction/core/src/amount/mod.rs b/transaction/core/src/amount/mod.rs index c5bb6e8dab..b7fec2e27c 100644 --- a/transaction/core/src/amount/mod.rs +++ b/transaction/core/src/amount/mod.rs @@ -11,44 +11,22 @@ use crate::{ domain_separators::{ AMOUNT_BLINDING_DOMAIN_TAG, AMOUNT_TOKEN_ID_DOMAIN_TAG, AMOUNT_VALUE_DOMAIN_TAG, }, - ring_signature::generators, - token::TokenId, + Amount, TokenId, }; use alloc::vec::Vec; use core::convert::TryInto; use crc::Crc; -use curve25519_dalek::scalar::Scalar; use mc_crypto_digestible::Digestible; use mc_crypto_hashes::{Blake2b512, Digest}; use mc_crypto_keys::RistrettoPublic; +use mc_crypto_ring_signature::{generators, CompressedCommitment, Scalar}; use prost::Message; use serde::{Deserialize, Serialize}; use zeroize::Zeroize; -mod commitment; -mod compressed_commitment; mod error; - -pub use commitment::Commitment; -pub use compressed_commitment::CompressedCommitment; pub use error::AmountError; -/// An amount of some token, in the "base" (u64) denomination. -#[derive(Clone, Copy, Debug, Digestible, Eq, PartialEq, Zeroize)] -pub struct Amount { - /// The "raw" value of this amount as a u64 - pub value: u64, - /// The token-id which is the denomination of this amount - pub token_id: TokenId, -} - -impl Amount { - /// Create a new amount - pub fn new(value: u64, token_id: TokenId) -> Self { - Self { value, token_id } - } -} - /// A commitment to an amount of MobileCoin or a related token, as it appears on /// the blockchain. This is a "blinded" commitment, and only the sender and /// receiver know the value and token id. diff --git a/transaction/core/src/blockchain/block.rs b/transaction/core/src/blockchain/block.rs index aa6825dc12..f90c51b6ee 100644 --- a/transaction/core/src/blockchain/block.rs +++ b/transaction/core/src/blockchain/block.rs @@ -215,7 +215,8 @@ mod block_tests { use mc_account_keys::AccountKey; use mc_crypto_keys::RistrettoPrivate; use mc_util_from_random::FromRandom; - use rand::{rngs::StdRng, CryptoRng, RngCore, SeedableRng}; + use mc_util_test_helper::{get_seeded_rng, CryptoRng, RngCore, SeedableRng}; + use rand::rngs::StdRng; // This is block version 1 to avoid messing with test vectors const BLOCK_VERSION: BlockVersion = BlockVersion::ONE; @@ -311,7 +312,7 @@ mod block_tests { #[test] /// The block returned by `get_block` should have a valid BlockID. fn test_get_block_has_valid_id() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let block = get_block(&mut rng); assert!(block.is_block_id_valid()); } @@ -319,7 +320,7 @@ mod block_tests { #[test] /// The block ID should depend on the block version. fn test_block_id_includes_version() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let mut block = get_block(&mut rng); block.version += 1; assert!(!block.is_block_id_valid()); @@ -328,7 +329,7 @@ mod block_tests { #[test] /// The block ID should depend on the parent_id. fn test_block_id_includes_parent_id() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let mut block = get_block(&mut rng); let mut bytes = [0u8; 32]; @@ -342,7 +343,7 @@ mod block_tests { #[test] /// The block ID should depend on the block's index. fn test_block_id_includes_block_index() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let mut block = get_block(&mut rng); block.index += 1; assert!(!block.is_block_id_valid()); @@ -351,7 +352,7 @@ mod block_tests { #[test] /// The block ID should depend on the root element. fn test_block_id_includes_root_element() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let mut block = get_block(&mut rng); let wrong_root_element = TxOutMembershipElement { @@ -365,7 +366,7 @@ mod block_tests { #[test] /// The block ID should depend on the content_hash. fn test_block_id_includes_content_hash() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let mut block = get_block(&mut rng); let mut bytes = [0u8; 32]; @@ -390,6 +391,10 @@ mod block_tests { /// we add/change Block/BlockContents and accidentally break id /// calculation of old blocks. fn test_hashing_is_consistent_block_version_one() { + // FIXME (#2042): This shouuld not use StdRng, because the rust standard library + // sometimes updates what RNG algorithm this point to. We should + // rerun this with Hc128 RNG (from mc_util_test_helper) and update the + // test vectors. let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); //Check hash with memo diff --git a/transaction/core/src/blockchain/block_contents.rs b/transaction/core/src/blockchain/block_contents.rs index 82dbf5fb92..4c950011b9 100644 --- a/transaction/core/src/blockchain/block_contents.rs +++ b/transaction/core/src/blockchain/block_contents.rs @@ -2,13 +2,13 @@ use crate::{ mint::{MintTx, ValidatedMintConfigTx}, - ring_signature::KeyImage, tx::TxOut, ConvertError, }; use alloc::{vec, vec::Vec}; use core::{convert::TryFrom, fmt::Debug}; use mc_crypto_digestible::{Digestible, MerlinTranscript}; +use mc_crypto_ring_signature::KeyImage; use prost::{ bytes::{Buf, BufMut}, encoding::{bytes, skip_field, DecodeContext, WireType}, diff --git a/transaction/core/src/constants.rs b/transaction/core/src/constants.rs index f1432aee61..64227f76ad 100644 --- a/transaction/core/src/constants.rs +++ b/transaction/core/src/constants.rs @@ -2,7 +2,7 @@ //! MobileCoin Transaction Constants. -use crate::ring_signature::Scalar; +use mc_crypto_ring_signature::Scalar; /// Maximum number of transactions that may be included in a Block. pub const MAX_TRANSACTIONS_PER_BLOCK: usize = 5000; diff --git a/transaction/core/src/domain_separators.rs b/transaction/core/src/domain_separators.rs index f5e839a4b1..b4b9095664 100644 --- a/transaction/core/src/domain_separators.rs +++ b/transaction/core/src/domain_separators.rs @@ -26,12 +26,6 @@ pub const AMOUNT_BLINDING_DOMAIN_TAG: &str = "mc_amount_blinding"; /// Domain separator for Bulletproof transcript. pub const BULLETPROOF_DOMAIN_TAG: &str = "mc_bulletproof_transcript"; -/// Domain separator for onetime key "hash_to_point" function. -pub const HASH_TO_POINT_DOMAIN_TAG: &str = "mc_onetime_key_hash_to_point"; - -/// Domain separator for onetime key "hash_to_scalar" function. -pub const HASH_TO_SCALAR_DOMAIN_TAG: &str = "mc_onetime_key_hash_to_scalar"; - /// Domain separator for hashing a TxOut leaf node in a Merkle tree. pub const TXOUT_MERKLE_LEAF_DOMAIN_TAG: &str = "mc_tx_out_merkle_leaf"; @@ -41,9 +35,6 @@ pub const TXOUT_MERKLE_NODE_DOMAIN_TAG: &str = "mc_tx_out_merkle_node"; /// Domain separator for hashing the "nil" value in a Merkle tree. pub const TXOUT_MERKLE_NIL_DOMAIN_TAG: &str = "mc_tx_out_merkle_nil"; -/// Domain separator for RingMLSAG's challenges. -pub const RING_MLSAG_CHALLENGE_DOMAIN_TAG: &str = "mc_ring_mlsag_challenge"; - /// Domain separator for hashing the confirmation number pub const TXOUT_CONFIRMATION_NUMBER_DOMAIN_TAG: &str = "mc_tx_out_confirmation_number"; diff --git a/transaction/core/src/lib.rs b/transaction/core/src/lib.rs index b975b5a221..7eb2ebb221 100644 --- a/transaction/core/src/lib.rs +++ b/transaction/core/src/lib.rs @@ -15,9 +15,6 @@ extern crate std; #[macro_use] extern crate lazy_static; -use crate::onetime_keys::create_shared_secret; -use mc_crypto_keys::{KeyError, RistrettoPrivate, RistrettoPublic}; - mod amount; mod blockchain; mod domain_separators; @@ -33,31 +30,43 @@ pub mod encrypted_fog_hint; pub mod fog_hint; pub mod membership_proofs; pub mod mint; -pub mod onetime_keys; pub mod range_proofs; -pub mod ring_signature; -pub mod signer; +pub mod ring_ct; pub mod tx; pub mod validation; #[cfg(test)] pub mod proptest_fixtures; -pub use amount::{Amount, AmountError, Commitment, CompressedCommitment, MaskedAmount}; +pub use amount::{AmountError, MaskedAmount}; pub use blockchain::*; pub use input_rules::{InputRuleError, InputRules}; pub use memo::{EncryptedMemo, MemoError, MemoPayload}; pub use signed_contingent_input::{ SignedContingentInput, SignedContingentInputError, UnmaskedAmount, }; -pub use token::{tokens, Token, TokenId}; +pub use token::{tokens, Token}; pub use tx::MemoContext; pub use tx_error::{NewMemoError, NewTxError, ViewKeyMatchError}; pub use tx_out_gift_code::TxOutGiftCode; +// Re-export Amount and TokenId from transaction-types, and certain types from +// RingSignature crate +pub use mc_crypto_ring_signature::{Commitment, CompressedCommitment}; +pub use mc_transaction_types::{Amount, TokenId}; + +/// Re-export all of mc-crypto-ring-signature +pub mod ring_signature { + pub use mc_crypto_ring_signature::*; +} + +// Re-export the one-time keys module which historically lived in this crate +pub use mc_crypto_ring_signature::onetime_keys; + use core::convert::TryFrom; use mc_account_keys::AccountKey; -use onetime_keys::recover_public_subaddress_spend_key; +use mc_crypto_keys::{KeyError, RistrettoPrivate, RistrettoPublic}; +use onetime_keys::{create_shared_secret, recover_public_subaddress_spend_key}; use tx::TxOut; /// Get the shared secret for a transaction output. diff --git a/transaction/core/src/memo.rs b/transaction/core/src/memo.rs index 546e7c8fb9..45aaa4e55e 100644 --- a/transaction/core/src/memo.rs +++ b/transaction/core/src/memo.rs @@ -245,12 +245,11 @@ impl From for MemoError { mod tests { use super::*; use mc_util_from_random::FromRandom; - use rand_core::SeedableRng; - use rand_hc::Hc128Rng; + use mc_util_test_helper::{RngType, SeedableRng}; #[test] fn test_memo_payload_round_trip() { - let mut rng = Hc128Rng::seed_from_u64(37); + let mut rng = RngType::seed_from_u64(37); let key1 = RistrettoPublic::from_random(&mut rng); let key2 = RistrettoPublic::from_random(&mut rng); diff --git a/transaction/core/src/mint/validation/config.rs b/transaction/core/src/mint/validation/config.rs index 1eb459c81d..cee1ab2ac7 100644 --- a/transaction/core/src/mint/validation/config.rs +++ b/transaction/core/src/mint/validation/config.rs @@ -102,11 +102,11 @@ mod tests { use mc_crypto_keys::{Ed25519Pair, Signer}; use mc_crypto_multisig::MultiSig; use mc_util_from_random::FromRandom; - use rand::{rngs::StdRng, SeedableRng}; + use mc_util_test_helper::get_seeded_rng; #[test] fn validate_configs_accepts_valid_mint_configs() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let token_id = TokenId::from(123); let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); @@ -149,7 +149,7 @@ mod tests { #[test] fn validate_configs_rejects_mismatching_token_ids() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); @@ -178,7 +178,7 @@ mod tests { #[test] fn validate_configs_rejects_invalid_signer_sets() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let signer_1 = Ed25519Pair::from_random(&mut rng); let token_id = TokenId::from(123); @@ -207,7 +207,7 @@ mod tests { #[test] fn validate_signature_accepts_valid_signature() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); let token_id = TokenId::from(123); @@ -323,7 +323,7 @@ mod tests { #[test] fn validate_signature_rejects_tampered_messages() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); @@ -398,7 +398,7 @@ mod tests { #[test] fn validate_signature_rejects_signers_mismatch() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); @@ -481,7 +481,7 @@ mod tests { #[test] fn validate_signature_rejects_duplicate_signer() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); diff --git a/transaction/core/src/mint/validation/tx.rs b/transaction/core/src/mint/validation/tx.rs index 9a63260c63..fa9d663312 100644 --- a/transaction/core/src/mint/validation/tx.rs +++ b/transaction/core/src/mint/validation/tx.rs @@ -92,11 +92,11 @@ mod tests { use mc_crypto_keys::{Ed25519Pair, RistrettoPublic, Signer}; use mc_crypto_multisig::MultiSig; use mc_util_from_random::FromRandom; - use rand::{rngs::StdRng, SeedableRng}; + use mc_util_test_helper::get_seeded_rng; #[test] fn validate_against_mint_config_accepts_valid_config() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let token_id = 123; let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); @@ -135,7 +135,7 @@ mod tests { #[test] fn validate_against_mint_config_rejects_token_id_mismatch() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let token_id = 123; let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); @@ -177,7 +177,7 @@ mod tests { #[test] fn validate_against_mint_config_rejects_amount_over_limit() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let token_id = 123; let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); @@ -219,7 +219,7 @@ mod tests { #[test] fn validate_against_mint_config_rejects_signature_mismatch() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let token_id = 123; let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); @@ -260,7 +260,7 @@ mod tests { #[test] fn validate_signature_accepts_valid_signature() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let token_id = 123; let signer_1 = Ed25519Pair::from_random(&mut rng); @@ -283,7 +283,7 @@ mod tests { #[test] fn validate_signature_rejects_signers_mismatch() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let token_id = 123; let signer_1 = Ed25519Pair::from_random(&mut rng); let signer_2 = Ed25519Pair::from_random(&mut rng); @@ -310,7 +310,7 @@ mod tests { #[test] fn validate_signature_rejects_tampered_messages() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let token_id = 123; let signer_1 = Ed25519Pair::from_random(&mut rng); diff --git a/transaction/core/src/proptest_fixtures.rs b/transaction/core/src/proptest_fixtures.rs index 1f150edfcc..9cfead6cf0 100644 --- a/transaction/core/src/proptest_fixtures.rs +++ b/transaction/core/src/proptest_fixtures.rs @@ -1,29 +1,11 @@ // Copyright (c) 2018-2022 The MobileCoin Foundation -use crate::{ring_signature::CurveScalar, tokens::Mob, Amount, MaskedAmount, Token}; -use curve25519_dalek::scalar::Scalar; -use mc_crypto_keys::{RistrettoPrivate, RistrettoPublic}; -use proptest::prelude::*; - -/// Generates an arbitrary Scalar. -pub fn arbitrary_scalar() -> impl Strategy { - any::<[u8; 32]>().prop_map(Scalar::from_bytes_mod_order) -} - -/// Generates an arbitrary CurveScalar. -pub fn arbitrary_curve_scalar() -> impl Strategy { - arbitrary_scalar().prop_map(CurveScalar::from) -} +pub use mc_crypto_ring_signature::{proptest_fixtures::*, CurveScalar, Scalar}; +pub use mc_transaction_types::Amount; -/// Generates an arbitrary RistrettoPrivate key. -pub fn arbitrary_ristretto_private() -> impl Strategy { - arbitrary_scalar().prop_map(RistrettoPrivate::from) -} - -/// Generates an arbitrary RistrettoPublic key. -pub fn arbitrary_ristretto_public() -> impl Strategy { - arbitrary_ristretto_private().prop_map(|private_key| RistrettoPublic::from(&private_key)) -} +use crate::{tokens::Mob, MaskedAmount, Token}; +use mc_crypto_keys::RistrettoPublic; +use proptest::prelude::*; prop_compose! { /// Generates an arbitrary masked_amount with value in [0,max_value]. diff --git a/transaction/core/src/range_proofs/mod.rs b/transaction/core/src/range_proofs/mod.rs index 4e012597bc..9027a0f86e 100644 --- a/transaction/core/src/range_proofs/mod.rs +++ b/transaction/core/src/range_proofs/mod.rs @@ -9,18 +9,24 @@ extern crate alloc; use alloc::vec::Vec; -use bulletproofs_og::RangeProof; +use bulletproofs_og::{BulletproofGens, PedersenGens as BPPedersenGens, RangeProof}; use curve25519_dalek::{ristretto::CompressedRistretto, scalar::Scalar}; +use mc_crypto_ring_signature::PedersenGens; use merlin::Transcript; use rand_core::{CryptoRng, RngCore}; pub mod error; -use crate::{ - domain_separators::BULLETPROOF_DOMAIN_TAG, - ring_signature::{PedersenGens, BP_GENERATORS}, -}; +use crate::domain_separators::BULLETPROOF_DOMAIN_TAG; use error::Error; +lazy_static! { + /// Generators (base points) for Bulletproofs. + /// The `party_capacity` is the maximum number of values in one proof. It should + /// be at least 2 * MAX_INPUTS + MAX_OUTPUTS, which allows for inputs, pseudo outputs, and outputs. + pub static ref BP_GENERATORS: BulletproofGens = + BulletproofGens::new(64, 64); +} + /// Create an aggregated 64-bit rangeproof for a set of values. /// /// Creates a proof that each secret value is in the range [0,2^64). @@ -51,7 +57,7 @@ pub fn generate_range_proofs( // Create a 64-bit RangeProof and corresponding commitments. RangeProof::prove_multiple_with_rng( &BP_GENERATORS, - pedersen_generators, + &convert_gens(pedersen_generators), &mut Transcript::new(BULLETPROOF_DOMAIN_TAG.as_ref()), &values_padded, &blindings_padded, @@ -81,7 +87,7 @@ pub fn check_range_proofs( range_proof .verify_multiple_with_rng( &BP_GENERATORS, - pedersen_generators, + &convert_gens(pedersen_generators), &mut Transcript::new(BULLETPROOF_DOMAIN_TAG.as_ref()), &resized_commitments, 64, @@ -112,16 +118,25 @@ fn resize_slice_to_pow2(slice: &[T]) -> Result, Error> { } } +/// Convert from the mc_crypto_ring_signature::PedersenGens to BPPedersenGens. +/// These types are identical, but we need a version of it in the lower-level +/// crate to break dependency on the bulletproofs crate. +fn convert_gens(src: &PedersenGens) -> BPPedersenGens { + BPPedersenGens { + B: src.B, + B_blinding: src.B_blinding, + } +} + #[cfg(test)] pub mod tests { use super::*; use crate::ring_signature::generators; use curve25519_dalek::ristretto::RistrettoPoint; - use rand::{rngs::StdRng, SeedableRng}; - use rand_core::RngCore; + use mc_util_test_helper::{get_seeded_rng, RngCore}; fn generate_and_check(values: Vec, blindings: Vec) { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let (proof, commitments) = generate_range_proofs(&values, &blindings, &generators(0), &mut rng).unwrap(); @@ -133,7 +148,7 @@ pub mod tests { #[test] fn test_pow2_number_of_inputs() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let vals: Vec = (0..2).map(|_| rng.next_u64()).collect(); let blindings: Vec = vals.iter().map(|_| Scalar::random(&mut rng)).collect(); generate_and_check(vals, blindings); @@ -141,7 +156,7 @@ pub mod tests { #[test] fn test_not_pow2_number_of_inputs() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let vals: Vec = (0..9).map(|_| rng.next_u64()).collect(); let blindings: Vec = vals.iter().map(|_| Scalar::random(&mut rng)).collect(); generate_and_check(vals, blindings); @@ -151,7 +166,7 @@ pub mod tests { // `check_range_proofs` should return an error if the commitments do not agree // with the proof. fn test_check_range_proofs_rejects_wrong_commitments() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let num_values: usize = 4; let values: Vec = (0..num_values).map(|_| rng.next_u64()).collect(); diff --git a/transaction/core/src/ring_signature/error.rs b/transaction/core/src/ring_ct/error.rs similarity index 86% rename from transaction/core/src/ring_signature/error.rs rename to transaction/core/src/ring_ct/error.rs index 07e521aa44..767d038d8c 100644 --- a/transaction/core/src/ring_signature/error.rs +++ b/transaction/core/src/ring_ct/error.rs @@ -1,13 +1,12 @@ // Copyright (c) 2018-2022 The MobileCoin Foundation -//! Errors which can occur in connection to ring signatures +//! Errors which can occur in connection to ring_ct signatures -use crate::{ - range_proofs::error::Error as RangeProofError, ring_signature::MLSAGError, - signer::Error as SignerError, TokenId, -}; +use crate::{range_proofs::error::Error as RangeProofError, TokenId}; use alloc::string::{String, ToString}; use displaydoc::Display; +use mc_crypto_ring_signature::Error as RingSignatureError; +use mc_crypto_ring_signature_signer::Error as SignerError; use mc_util_zip_exact::ZipExactError; use serde::{Deserialize, Serialize}; @@ -83,8 +82,8 @@ pub enum Error { /// Zip Exact: {0} ZipExact(ZipExactError), - /// MLSAG: {0} - MLSAG(MLSAGError), + /// Ring signature: {0} + RingSignature(RingSignatureError), /// Signer: {0} Signer(SignerError), @@ -108,9 +107,9 @@ impl From for Error { } } -impl From for Error { - fn from(src: MLSAGError) -> Self { - Self::MLSAG(src) +impl From for Error { + fn from(src: RingSignatureError) -> Self { + Self::RingSignature(src) } } impl From for Error { diff --git a/transaction/core/src/ring_ct/mod.rs b/transaction/core/src/ring_ct/mod.rs new file mode 100644 index 0000000000..9e70499983 --- /dev/null +++ b/transaction/core/src/ring_ct/mod.rs @@ -0,0 +1,9 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + +//! MobileCoin RingCT implementation + +mod error; +mod rct_bulletproofs; + +pub use error::Error; +pub use rct_bulletproofs::*; diff --git a/transaction/core/src/ring_signature/rct_bulletproofs.rs b/transaction/core/src/ring_ct/rct_bulletproofs.rs similarity index 97% rename from transaction/core/src/ring_signature/rct_bulletproofs.rs rename to transaction/core/src/ring_ct/rct_bulletproofs.rs index 395cc17147..e6dd9d6ddd 100644 --- a/transaction/core/src/ring_signature/rct_bulletproofs.rs +++ b/transaction/core/src/ring_ct/rct_bulletproofs.rs @@ -17,6 +17,10 @@ use curve25519_dalek::{ }; use mc_common::HashSet; use mc_crypto_digestible::{DigestTranscript, Digestible, MerlinTranscript}; +use mc_crypto_ring_signature::{ + Commitment, CompressedCommitment, GeneratorCache, KeyImage, ReducedTxOut, RingMLSAG, Scalar, +}; +use mc_crypto_ring_signature_signer::{RingSigner, SignableInputRing}; use mc_util_serial::prost::Message; use mc_util_zip_exact::zip_exact; use rand_core::{CryptoRng, RngCore}; @@ -27,9 +31,8 @@ use crate::{ constants::FEE_BLINDING, domain_separators::EXTENDED_MESSAGE_DOMAIN_TAG, range_proofs::{check_range_proofs, generate_range_proofs}, - ring_signature::{mlsag::RingMLSAG, Error, GeneratorCache, KeyImage, ReducedTxOut, Scalar}, - signer::{RingSigner, SignableInputRing}, - Amount, BlockVersion, Commitment, CompressedCommitment, + ring_ct::Error, + Amount, BlockVersion, }; /// A presigned RingMLSAG and ancillary data needed to incorporate it into a @@ -852,8 +855,9 @@ mod rct_bulletproofs_tests { use super::*; use crate::{ range_proofs::generate_range_proofs, - ring_signature::{generators, Error, KeyImage, MLSAGError, PedersenGens, ReducedTxOut}, - signer::{InputSecret, NoKeysRingSigner, OneTimeKeyDeriveData, SignableInputRing}, + ring_signature::{ + generators, Error as RingSignatureError, KeyImage, PedersenGens, ReducedTxOut, + }, CompressedCommitment, TokenId, }; use alloc::vec::Vec; @@ -861,10 +865,12 @@ mod rct_bulletproofs_tests { use core::convert::TryInto; use curve25519_dalek::scalar::Scalar; use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; + use mc_crypto_ring_signature_signer::{ + InputSecret, NoKeysRingSigner, OneTimeKeyDeriveData, SignableInputRing, + }; use mc_util_from_random::FromRandom; + use mc_util_test_helper::{CryptoRng, RngCore, RngType, SeedableRng}; use proptest::prelude::*; - use rand::{rngs::StdRng, CryptoRng, SeedableRng}; - use rand_core::RngCore; extern crate std; @@ -1071,7 +1077,7 @@ mod rct_bulletproofs_tests { block_version in 1..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let mut params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); params.rings = Vec::new(); @@ -1093,7 +1099,7 @@ mod rct_bulletproofs_tests { block_version in 1..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let mut params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); params.rings[0].members = Vec::new(); @@ -1114,7 +1120,7 @@ mod rct_bulletproofs_tests { block_version in 1..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); let signature = params.sign(0, &mut rng).unwrap(); @@ -1139,7 +1145,7 @@ mod rct_bulletproofs_tests { block_version in 1..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); let fee = 0; let signature = params.sign(fee, &mut rng).unwrap(); @@ -1165,7 +1171,7 @@ mod rct_bulletproofs_tests { block_version in 3..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let params = SignatureParams::random_mixed(block_version, num_inputs, num_mixins, num_token_ids, &mut rng); let fee = 0; let signature = params.sign(fee, &mut rng).unwrap(); @@ -1190,7 +1196,7 @@ mod rct_bulletproofs_tests { block_version in 1..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); let fee = 0; @@ -1209,7 +1215,7 @@ mod rct_bulletproofs_tests { &mut rng, ); - assert_eq!(result, Err(Error::MLSAG(MLSAGError::InvalidSignature))); + assert_eq!(result, Err(Error::RingSignature(RingSignatureError::InvalidSignature))); } #[test] @@ -1221,7 +1227,7 @@ mod rct_bulletproofs_tests { block_version in 1..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let mut params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); let fee = 0; // Modify an output value @@ -1254,7 +1260,7 @@ mod rct_bulletproofs_tests { block_version in 1..=2u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); let fee = 0; let mut signature = params.sign(fee, &mut rng).unwrap(); @@ -1294,7 +1300,7 @@ mod rct_bulletproofs_tests { block_version in 3..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); let fee = 0; let mut signature = params.sign(fee, &mut rng).unwrap(); @@ -1335,7 +1341,7 @@ mod rct_bulletproofs_tests { block_version in 1..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let mut params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); let fee = 0; @@ -1367,7 +1373,7 @@ mod rct_bulletproofs_tests { block_version in 1..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); let fee = 0; let signature = params.sign(fee, &mut rng).unwrap(); @@ -1392,7 +1398,7 @@ mod rct_bulletproofs_tests { block_version in 1..=3u32, ) { let block_version: BlockVersion = block_version.try_into().unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let mut params = SignatureParams::random(block_version, num_inputs, num_mixins, &mut rng); // Remove one of the outputs, and use its value as the fee. This conserves value. let popped_secret = params.output_secrets.pop().unwrap(); @@ -1435,7 +1441,7 @@ mod rct_bulletproofs_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let mut params = SignatureParams::random(BlockVersion::ONE, num_inputs, num_mixins, &mut rng); // Remove one of the outputs, and use its value as the fee. This conserves value. let popped_secret = params.output_secrets.pop().unwrap(); @@ -1461,7 +1467,7 @@ mod rct_bulletproofs_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let mut params = SignatureParams::random(BlockVersion::TWO, num_inputs, num_mixins, &mut rng); // Remove one of the outputs, and use its value as the fee. This conserves value. let popped_secret = params.output_secrets.pop().unwrap(); @@ -1487,7 +1493,7 @@ mod rct_bulletproofs_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let mut params = SignatureParams::random(BlockVersion::TWO, num_inputs, num_mixins, &mut rng); // Remove one of the outputs, and use its value as the fee. This conserves value. let popped_secret = params.output_secrets.pop().unwrap(); @@ -1524,7 +1530,7 @@ mod rct_bulletproofs_tests { num_mixins in 1..17usize, seed in any::<[u8; 32]>(), ) { - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let mut params = SignatureParams::random(BlockVersion::ONE, num_inputs, num_mixins, &mut rng); // Remove one of the outputs, and use its value as the fee. This conserves value. let popped_secret = params.output_secrets.pop().unwrap(); @@ -1545,7 +1551,7 @@ mod rct_bulletproofs_tests { block_version in 3..=3u32, ) { let block_version = BlockVersion::try_from(block_version).unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let params = SignatureParams::random_mixed(block_version, num_inputs, num_mixins,num_token_ids, &mut rng); let fee = 0; @@ -1584,7 +1590,7 @@ mod rct_bulletproofs_tests { block_version in 3..=3u32, ) { let block_version = BlockVersion::try_from(block_version).unwrap(); - let mut rng: StdRng = SeedableRng::from_seed(seed); + let mut rng: RngType = SeedableRng::from_seed(seed); let params = SignatureParams::random_mixed(block_version, num_inputs, num_mixins, num_token_ids, &mut rng); let fee = 0; diff --git a/transaction/core/src/signed_contingent_input.rs b/transaction/core/src/signed_contingent_input.rs index 6a004b7dca..20ea47a0b3 100644 --- a/transaction/core/src/signed_contingent_input.rs +++ b/transaction/core/src/signed_contingent_input.rs @@ -1,17 +1,18 @@ -// Copyright (c) 2022 The MobileCoin Foundation +// Copyright (c) 2018-2022 The MobileCoin Foundation //! A signed contingent input as described in MCIP #31 use crate::{ - ring_signature::{ - CurveScalar, GeneratorCache, KeyImage, MLSAGError, OutputSecret, PresignedInputRing, - RingMLSAG, SignedInputRing, - }, + ring_ct::{OutputSecret, PresignedInputRing, SignedInputRing}, tx::TxIn, - Amount, Commitment, CompressedCommitment, TokenId, + Amount, TokenId, }; use alloc::vec::Vec; use displaydoc::Display; +use mc_crypto_ring_signature::{ + Commitment, CompressedCommitment, CurveScalar, Error as RingSignatureError, GeneratorCache, + KeyImage, RingMLSAG, +}; use prost::Message; /// The "unmasked" data of an amount commitment @@ -180,12 +181,12 @@ pub enum SignedContingentInputError { MissingRules, /// Proofs of membership are missing MissingProofs, - /// Invalid MLSAG: {0} - MLSAG(MLSAGError), + /// Invalid Ring signature: {0} + RingSignature(RingSignatureError), } -impl From for SignedContingentInputError { - fn from(src: MLSAGError) -> Self { - Self::MLSAG(src) +impl From for SignedContingentInputError { + fn from(src: RingSignatureError) -> Self { + Self::RingSignature(src) } } diff --git a/transaction/core/src/token.rs b/transaction/core/src/token.rs index 19e4cabf9c..c49d781388 100644 --- a/transaction/core/src/token.rs +++ b/transaction/core/src/token.rs @@ -1,97 +1,8 @@ // Copyright (c) 2018-2022 The MobileCoin Foundation -//! A new-type wrapper for representing TokenIds +//! A registry of tokens -use core::{fmt, hash::Hash, num::ParseIntError, ops::Deref, str::FromStr}; -use mc_crypto_digestible::Digestible; -use serde::{Deserialize, Serialize}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -use zeroize::Zeroize; - -/// Token Id, used to identify different assets on on the blockchain. -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Digestible, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - Zeroize, -)] -pub struct TokenId(u64); - -impl From for TokenId { - fn from(src: u64) -> Self { - Self(src) - } -} - -impl From<&u64> for TokenId { - fn from(src: &u64) -> Self { - Self(*src) - } -} - -impl fmt::Display for TokenId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl TokenId { - /// Represents the MobileCoin token id for MOB token - pub const MOB: Self = Self(0); - - /// Represents the number of bytes in a well-formed TokenId - pub const NUM_BYTES: usize = 8; -} - -impl Deref for TokenId { - type Target = u64; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl FromStr for TokenId { - type Err = ParseIntError; - fn from_str(src: &str) -> Result { - let src = u64::from_str(src)?; - Ok(TokenId(src)) - } -} - -impl PartialEq for TokenId { - fn eq(&self, other: &u64) -> bool { - self.0 == *other - } -} - -impl PartialEq for u64 { - fn eq(&self, other: &TokenId) -> bool { - *self == other.0 - } -} - -impl ConstantTimeEq for TokenId { - fn ct_eq(&self, other: &TokenId) -> Choice { - self.0.ct_eq(&other.0) - } -} - -impl ConditionallySelectable for TokenId { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Self(ConditionallySelectable::conditional_select( - &a.0, &b.0, choice, - )) - } -} +use mc_transaction_types::TokenId; /// A generic representation of a token. pub trait Token { diff --git a/transaction/core/src/tx.rs b/transaction/core/src/tx.rs index 89164c9319..626c184272 100644 --- a/transaction/core/src/tx.rs +++ b/transaction/core/src/tx.rs @@ -9,6 +9,7 @@ use mc_common::Hash; use mc_crypto_digestible::{Digestible, MerlinTranscript}; use mc_crypto_hashes::{Blake2b256, Digest}; use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; +use mc_crypto_ring_signature::{KeyImage, ReducedTxOut}; use mc_util_repr_bytes::{ derive_prost_message_from_repr_bytes, typenum::U32, GenericArray, ReprBytes, }; @@ -17,7 +18,7 @@ use serde::{Deserialize, Serialize}; use zeroize::Zeroize; use crate::{ - amount::{Amount, AmountError, MaskedAmount}, + amount::{AmountError, MaskedAmount}, domain_separators::TXOUT_CONFIRMATION_NUMBER_DOMAIN_TAG, encrypted_fog_hint::EncryptedFogHint, get_tx_out_shared_secret, @@ -25,8 +26,8 @@ use crate::{ membership_proofs::Range, memo::{EncryptedMemo, MemoPayload}, onetime_keys::{create_shared_secret, create_tx_out_public_key, create_tx_out_target_key}, - ring_signature::{KeyImage, ReducedTxOut, SignatureRctBulletproofs, SignedInputRing}, - CompressedCommitment, NewMemoError, NewTxError, ViewKeyMatchError, + ring_ct::{SignatureRctBulletproofs, SignedInputRing}, + Amount, CompressedCommitment, NewMemoError, NewTxError, ViewKeyMatchError, }; /// Transaction hash length, in bytes. @@ -657,7 +658,7 @@ mod tests { encrypted_fog_hint::{EncryptedFogHint, ENCRYPTED_FOG_HINT_LEN}, get_tx_out_shared_secret, memo::MemoPayload, - ring_signature::SignatureRctBulletproofs, + ring_ct::SignatureRctBulletproofs, subaddress_matches_tx_out, tokens::Mob, tx::{Tx, TxIn, TxOut, TxPrefix}, @@ -668,13 +669,13 @@ mod tests { use mc_account_keys::{AccountKey, CHANGE_SUBADDRESS_INDEX, DEFAULT_SUBADDRESS_INDEX}; use mc_crypto_keys::{RistrettoPrivate, RistrettoPublic}; use mc_util_from_random::FromRandom; + use mc_util_test_helper::get_seeded_rng; use prost::Message; - use rand::{rngs::StdRng, SeedableRng}; #[test] // `serialize_tx` should create a Tx, encode/decode it, and compare fn test_serialize_tx_no_memo() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let tx_out = { let shared_secret = RistrettoPublic::from_random(&mut rng); let target_key = RistrettoPublic::from_random(&mut rng).into(); @@ -738,7 +739,7 @@ mod tests { #[test] // `serialize_tx` should create a Tx, encode/decode it, and compare fn test_serialize_tx_with_memo() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let tx_out = { let shared_secret = RistrettoPublic::from_random(&mut rng); let target_key = RistrettoPublic::from_random(&mut rng).into(); @@ -802,7 +803,7 @@ mod tests { // round trip memos from `TxOut` constructors through `decrypt_memo()` #[test] fn test_decrypt_memo() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let bob = AccountKey::new( &RistrettoPrivate::from_random(&mut rng), diff --git a/transaction/core/src/validation/error.rs b/transaction/core/src/validation/error.rs index 8f0fcfa424..0f721d3659 100644 --- a/transaction/core/src/validation/error.rs +++ b/transaction/core/src/validation/error.rs @@ -32,7 +32,7 @@ pub enum TransactionValidationError { InvalidInputSignature, /// Invalid RingCT signature: `{0}` - InvalidTransactionSignature(crate::ring_signature::Error), + InvalidTransactionSignature(crate::ring_ct::Error), /// All Range Proofs in the transaction must be valid. InvalidRangeProof, diff --git a/transaction/core/test-utils/Cargo.toml b/transaction/core/test-utils/Cargo.toml index 194d2773ea..3ab1c86544 100644 --- a/transaction/core/test-utils/Cargo.toml +++ b/transaction/core/test-utils/Cargo.toml @@ -13,6 +13,7 @@ mc-account-keys = { path = "../../../account-keys" } mc-crypto-keys = { path = "../../../crypto/keys", default-features = false } mc-crypto-multisig = { path = "../../../crypto/multisig" } mc-crypto-rand = { path = "../../../crypto/rand" } +mc-crypto-ring-signature-signer = { path = "../../../crypto/ring-signature/signer" } mc-fog-report-validation-test-utils = { path = "../../../fog/report/validation/test-utils" } mc-ledger-db = { path = "../../../ledger/db" } mc-transaction-core = { path = "../../../transaction/core" } diff --git a/transaction/core/test-utils/src/lib.rs b/transaction/core/test-utils/src/lib.rs index c5ffd55cca..eb2d1653b0 100644 --- a/transaction/core/test-utils/src/lib.rs +++ b/transaction/core/test-utils/src/lib.rs @@ -3,12 +3,12 @@ mod mint; pub use mc_account_keys::{AccountKey, PublicAddress, DEFAULT_SUBADDRESS_INDEX}; +pub use mc_crypto_ring_signature_signer::NoKeysRingSigner; pub use mc_fog_report_validation_test_utils::MockFogResolver; pub use mc_transaction_core::{ get_tx_out_shared_secret, onetime_keys::recover_onetime_private_key, ring_signature::KeyImage, - signer::NoKeysRingSigner, tokens::Mob, tx::{Tx, TxOut, TxOutMembershipElement, TxOutMembershipHash}, Amount, Block, BlockID, BlockIndex, BlockVersion, Token, @@ -20,7 +20,6 @@ pub use mint::{ use core::convert::TryFrom; use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; -use mc_crypto_rand::{CryptoRng, RngCore}; use mc_ledger_db::{Ledger, LedgerDB}; use mc_transaction_core::{constants::RING_SIZE, membership_proofs::Range, BlockContents}; use mc_transaction_std::{ @@ -28,7 +27,7 @@ use mc_transaction_std::{ TxOutputsOrdering, }; use mc_util_from_random::FromRandom; -use rand::{seq::SliceRandom, Rng}; +use rand::{seq::SliceRandom, CryptoRng, Rng, RngCore}; use std::cmp::Ordering; use tempdir::TempDir; diff --git a/transaction/core/test-utils/src/mint.rs b/transaction/core/test-utils/src/mint.rs index e18b6db149..1a6a8c40f0 100644 --- a/transaction/core/test-utils/src/mint.rs +++ b/transaction/core/test-utils/src/mint.rs @@ -4,7 +4,6 @@ use mc_account_keys::PublicAddress; use mc_crypto_keys::{Ed25519Pair, RistrettoPublic, Signer}; use mc_crypto_multisig::{MultiSig, SignerSet}; -use mc_crypto_rand::{CryptoRng, RngCore}; use mc_transaction_core::{ mint::{ constants::NONCE_LENGTH, MintConfig, MintConfigTx, MintConfigTxPrefix, MintTx, @@ -13,6 +12,7 @@ use mc_transaction_core::{ TokenId, }; use mc_util_from_random::FromRandom; +use rand::{CryptoRng, RngCore}; /// Generate a valid MintConfigTx and return it together with the set of signing /// keys that are allowed to sign it. diff --git a/transaction/core/tests/digest-test-vectors.rs b/transaction/core/tests/digest-test-vectors.rs index a974778800..19a5c1ab54 100644 --- a/transaction/core/tests/digest-test-vectors.rs +++ b/transaction/core/tests/digest-test-vectors.rs @@ -1,3 +1,5 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + use mc_account_keys::AccountKey; use mc_crypto_digestible_test_utils::*; use mc_crypto_keys::RistrettoPrivate; @@ -6,8 +8,7 @@ use mc_transaction_core::{ BlockVersion, Token, }; use mc_util_from_random::FromRandom; -use rand_core::{RngCore, SeedableRng}; -use rand_hc::Hc128Rng as FixedRng; +use mc_util_test_helper::{RngCore, RngType as FixedRng, SeedableRng}; fn test_accounts() -> Vec { let mut rng: FixedRng = SeedableRng::from_seed([12u8; 32]); diff --git a/transaction/core/tests/util/mod.rs b/transaction/core/tests/util/mod.rs index b4b80863d3..5216b847a8 100644 --- a/transaction/core/tests/util/mod.rs +++ b/transaction/core/tests/util/mod.rs @@ -1,3 +1,5 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + use mc_ledger_db::{Ledger, LedgerDB}; use mc_transaction_core::{tx::Tx, BlockVersion}; use mc_transaction_core_test_utils::{ @@ -5,10 +7,10 @@ use mc_transaction_core_test_utils::{ initialize_ledger, AccountKey, }; use mc_transaction_std::{DefaultTxOutputsOrdering, TxOutputsOrdering}; -use rand::{rngs::StdRng, SeedableRng}; +use mc_util_test_helper::{RngType, SeedableRng}; pub fn create_test_tx(block_version: BlockVersion) -> (Tx, LedgerDB) { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng: RngType = SeedableRng::from_seed([1u8; 32]); let sender = AccountKey::random(&mut rng); let mut ledger = create_ledger(); let n_blocks = 1; @@ -47,7 +49,7 @@ pub fn create_test_tx_with_amount_and_comparer( amount: u64, fee: u64, ) -> (Tx, LedgerDB) { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng: RngType = SeedableRng::from_seed([1u8; 32]); let sender = AccountKey::random(&mut rng); let mut ledger = create_ledger(); let n_blocks = 1; diff --git a/transaction/core/tests/validation.rs b/transaction/core/tests/validation.rs index 90e6eae84b..484c3fab41 100644 --- a/transaction/core/tests/validation.rs +++ b/transaction/core/tests/validation.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 The MobileCoin Foundation +// Copyright (c) 2018-2022 The MobileCoin Foundation //! This module is meant to unit test all of the functionality in the validation //! module in mc-transaction-core. @@ -19,7 +19,7 @@ use mc_transaction_core::{ BlockVersion, InputRules, Token, }; use mc_transaction_core_test_utils::{InverseTxOutputsOrdering, INITIALIZE_LEDGER_AMOUNT}; -use rand::{rngs::StdRng, SeedableRng}; +use mc_util_test_helper::get_seeded_rng; use util::*; #[test] @@ -502,7 +502,7 @@ fn test_validate_output_public_keys_are_unique_ok() { #[test] // `validate_signature` return OK for a valid transaction. fn test_validate_signature_ok() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); for block_version in BlockVersion::iterator() { let (tx, _ledger) = create_test_tx(block_version); @@ -518,7 +518,7 @@ fn test_validate_signature_ok() { #[test] // Should return InvalidTransactionSignature if an input is modified. fn test_transaction_signature_err_modified_input() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); for block_version in BlockVersion::iterator() { let (mut tx, _ledger) = create_test_tx(block_version); @@ -539,7 +539,7 @@ fn test_transaction_signature_err_modified_input() { #[test] // Should return InvalidTransactionSignature if an output is modified. fn test_transaction_signature_err_modified_output() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); for block_version in BlockVersion::iterator() { let (mut tx, _ledger) = create_test_tx(block_version); @@ -561,7 +561,7 @@ fn test_transaction_signature_err_modified_output() { #[test] // Should return InvalidTransactionSignature if the fee is modified. fn test_transaction_signature_err_modified_fee() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); for block_version in BlockVersion::iterator() { let (mut tx, _ledger) = create_test_tx(block_version); @@ -581,7 +581,7 @@ fn test_transaction_signature_err_modified_fee() { #[test] // Should return InvalidTransactionSignature if the token_id is modified fn test_transaction_signature_err_modified_token_id() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); for _ in 0..3 { let (mut tx, _ledger) = create_test_tx(BlockVersion::TWO); @@ -601,7 +601,7 @@ fn test_transaction_signature_err_modified_token_id() { #[test] // Should return InvalidTransactionSignature if block v 1 is validated as 2 fn test_transaction_signature_err_version_one_as_two() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); for _ in 0..3 { let (tx, _ledger) = create_test_tx(BlockVersion::ONE); @@ -619,7 +619,7 @@ fn test_transaction_signature_err_version_one_as_two() { #[test] // Should return InvalidTransactionSignature if block v 2 is validated as 1 fn test_transaction_signature_err_version_two_as_one() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); for _ in 0..3 { let (tx, _ledger) = create_test_tx(BlockVersion::TWO); @@ -752,7 +752,7 @@ fn test_validate_tombstone_tombstone_block_too_far() { #[test] #[ignore] fn test_global_validate_for_blocks_with_sorted_outputs() { - let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let mut rng = get_seeded_rng(); let fee = Mob::MINIMUM_FEE + 1; for block_version in BlockVersion::iterator() { // for block version < 3 it doesn't matter diff --git a/transaction/std/Cargo.toml b/transaction/std/Cargo.toml index ab60a78b5b..810160b816 100644 --- a/transaction/std/Cargo.toml +++ b/transaction/std/Cargo.toml @@ -23,6 +23,7 @@ zeroize = "1" mc-account-keys = { path = "../../account-keys" } mc-crypto-hashes = { path = "../../crypto/hashes" } mc-crypto-keys = { path = "../../crypto/keys", default-features = false } +mc-crypto-ring-signature-signer = { path = "../../crypto/ring-signature/signer", default-features = false } mc-fog-report-validation = { path = "../../fog/report/validation" } mc-transaction-core = { path = "../../transaction/core" } mc-util-from-random = { path = "../../util/from-random" } diff --git a/transaction/std/src/error.rs b/transaction/std/src/error.rs index 843ae43164..02836356ab 100644 --- a/transaction/std/src/error.rs +++ b/transaction/std/src/error.rs @@ -1,17 +1,17 @@ // Copyright (c) 2018-2022 The MobileCoin Foundation use displaydoc::Display; +use mc_crypto_ring_signature_signer::Error as SignerError; use mc_fog_report_validation::FogPubkeyError; use mc_transaction_core::{ - ring_signature, ring_signature::Error, signer::Error as SignerError, AmountError, NewMemoError, - NewTxError, TokenId, + ring_ct::Error as RingCtError, AmountError, NewMemoError, NewTxError, TokenId, }; /// An error that can occur when using the TransactionBuilder #[derive(Debug, Display)] pub enum TxBuilderError { /// Ring Signature construction failed: {0} - RingSignatureFailed(ring_signature::Error), + RingSignatureFailed(RingCtError), /// Range proof construction failed RangeProofFailed, @@ -98,8 +98,8 @@ impl From for TxBuilderError { } } -impl From for TxBuilderError { - fn from(src: Error) -> Self { +impl From for TxBuilderError { + fn from(src: RingCtError) -> Self { TxBuilderError::RingSignatureFailed(src) } } diff --git a/transaction/std/src/input_credentials.rs b/transaction/std/src/input_credentials.rs index 1a4c6631b2..daf66d95ee 100644 --- a/transaction/std/src/input_credentials.rs +++ b/transaction/std/src/input_credentials.rs @@ -2,9 +2,9 @@ use crate::TxBuilderError; use mc_crypto_keys::{RistrettoPrivate, RistrettoPublic}; +use mc_crypto_ring_signature_signer::{InputSecret, OneTimeKeyDeriveData, SignableInputRing}; use mc_transaction_core::{ onetime_keys::create_shared_secret, - signer::{InputSecret, OneTimeKeyDeriveData, SignableInputRing}, tx::{TxIn, TxOut, TxOutMembershipProof}, }; use std::convert::TryFrom; diff --git a/transaction/std/src/input_materials.rs b/transaction/std/src/input_materials.rs index 6285b87418..1f02e9bacb 100644 --- a/transaction/std/src/input_materials.rs +++ b/transaction/std/src/input_materials.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 The MobileCoin Foundation +// Copyright (c) 2018-2022 The MobileCoin Foundation //! Input Materials is a helper struct for the transaction builder. //! The transaction builder can get inputs either from input credentials, @@ -11,7 +11,7 @@ use crate::InputCredentials; use mc_crypto_keys::CompressedRistrettoPublic; -use mc_transaction_core::{ring_signature::InputRing, tx::TxIn, Amount, SignedContingentInput}; +use mc_transaction_core::{ring_ct::InputRing, tx::TxIn, Amount, SignedContingentInput}; /// Material that can be used by the transaction builder to create an input to /// a transaction. diff --git a/transaction/std/src/signed_contingent_input_builder.rs b/transaction/std/src/signed_contingent_input_builder.rs index bd22ddef52..422947f000 100644 --- a/transaction/std/src/signed_contingent_input_builder.rs +++ b/transaction/std/src/signed_contingent_input_builder.rs @@ -1,4 +1,4 @@ -//// Copyright (c) 2022 The MobileCoin Foundation +// Copyright (c) 2018-2022 The MobileCoin Foundation //! A builder object for signed contingent inputs (see MCIP #31) //! This plays a similar role to the transaction builder. @@ -9,10 +9,11 @@ use crate::{ }; use core::cmp::min; use mc_account_keys::PublicAddress; +use mc_crypto_ring_signature_signer::{RingSigner, SignableInputRing}; use mc_fog_report_validation::FogPubkeyResolver; use mc_transaction_core::{ - ring_signature::{OutputSecret, Scalar}, - signer::{RingSigner, SignableInputRing}, + ring_ct::OutputSecret, + ring_signature::Scalar, tx::{TxIn, TxOut, TxOutConfirmationNumber}, Amount, BlockVersion, InputRules, MemoContext, MemoPayload, NewMemoError, SignedContingentInput, TokenId, UnmaskedAmount, @@ -392,13 +393,14 @@ pub mod tests { use core::convert::TryFrom; use mc_account_keys::{AccountKey, CHANGE_SUBADDRESS_INDEX, DEFAULT_SUBADDRESS_INDEX}; use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; + use mc_crypto_ring_signature_signer::NoKeysRingSigner; use mc_fog_report_validation_test_utils::{FullyValidatedFogPubkey, MockFogResolver}; use mc_transaction_core::{ constants::MILLIMOB_TO_PICOMOB, fog_hint::FogHint, get_tx_out_shared_secret, - ring_signature::{Error as RingSignatureError, KeyImage}, - signer::NoKeysRingSigner, + ring_ct::Error as RingCtError, + ring_signature::KeyImage, subaddress_matches_tx_out, tokens::Mob, validation::{ @@ -1499,7 +1501,7 @@ pub mod tests { assert_matches!( builder.build(&NoKeysRingSigner {}, &mut rng), Err(TxBuilderError::RingSignatureFailed( - RingSignatureError::AllRingsPresigned + RingCtError::AllRingsPresigned )) ); } @@ -1902,7 +1904,10 @@ pub mod tests { // (Sanity check: the sci fails its own validation now, because the signature is // invalid) - assert_matches!(sci.validate(), Err(SignedContingentInputError::MLSAG(_))); + assert_matches!( + sci.validate(), + Err(SignedContingentInputError::RingSignature(_)) + ); let mut builder = TransactionBuilder::new( block_version, @@ -2254,7 +2259,10 @@ pub mod tests { // (Sanity check: the sci fails its own validation now, because the signature is // invalid) - assert_matches!(sci.validate(), Err(SignedContingentInputError::MLSAG(_))); + assert_matches!( + sci.validate(), + Err(SignedContingentInputError::RingSignature(_)) + ); let mut builder = TransactionBuilder::new( block_version, diff --git a/transaction/std/src/test_utils.rs b/transaction/std/src/test_utils.rs index 7513dbf0c2..ba7aa0d917 100644 --- a/transaction/std/src/test_utils.rs +++ b/transaction/std/src/test_utils.rs @@ -9,10 +9,10 @@ use crate::{ use core::convert::TryFrom; use mc_account_keys::{AccountKey, PublicAddress, DEFAULT_SUBADDRESS_INDEX}; use mc_crypto_keys::RistrettoPublic; +use mc_crypto_ring_signature_signer::{NoKeysRingSigner, OneTimeKeyDeriveData}; use mc_fog_report_validation::FogPubkeyResolver; use mc_transaction_core::{ onetime_keys::*, - signer::{NoKeysRingSigner, OneTimeKeyDeriveData}, tokens::Mob, tx::{Tx, TxOut, TxOutMembershipProof}, Amount, BlockVersion, MemoContext, NewMemoError, Token, TokenId, diff --git a/transaction/std/src/transaction_builder.rs b/transaction/std/src/transaction_builder.rs index edb9c8c2d6..027e0bca53 100644 --- a/transaction/std/src/transaction_builder.rs +++ b/transaction/std/src/transaction_builder.rs @@ -11,13 +11,13 @@ use crate::{ use core::{cmp::min, fmt::Debug}; use mc_account_keys::PublicAddress; use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; +use mc_crypto_ring_signature_signer::RingSigner; use mc_fog_report_validation::FogPubkeyResolver; use mc_transaction_core::{ encrypted_fog_hint::EncryptedFogHint, fog_hint::FogHint, onetime_keys::create_shared_secret, - ring_signature::{InputRing, OutputSecret, SignatureRctBulletproofs}, - signer::RingSigner, + ring_ct::{InputRing, OutputSecret, SignatureRctBulletproofs}, tokens::Mob, tx::{Tx, TxIn, TxOut, TxOutConfirmationNumber, TxPrefix}, Amount, BlockVersion, MemoContext, MemoPayload, NewMemoError, SignedContingentInput, @@ -643,13 +643,13 @@ pub mod transaction_builder_tests { burn_address, burn_address_view_private, AccountKey, ShortAddressHash, CHANGE_SUBADDRESS_INDEX, DEFAULT_SUBADDRESS_INDEX, GIFT_CODE_SUBADDRESS_INDEX, }; + use mc_crypto_ring_signature_signer::{InputSecret, NoKeysRingSigner, OneTimeKeyDeriveData}; use mc_fog_report_validation_test_utils::{FullyValidatedFogPubkey, MockFogResolver}; use mc_transaction_core::{ constants::{MAX_INPUTS, MAX_OUTPUTS, MILLIMOB_TO_PICOMOB}, get_tx_out_shared_secret, onetime_keys::*, ring_signature::KeyImage, - signer::{InputSecret, NoKeysRingSigner, OneTimeKeyDeriveData}, subaddress_matches_tx_out, tx::TxOutMembershipProof, validation::{validate_signature, validate_tx_out}, diff --git a/transaction/types/Cargo.toml b/transaction/types/Cargo.toml new file mode 100644 index 0000000000..a63aacc186 --- /dev/null +++ b/transaction/types/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "mc-transaction-types" +version = "1.3.0-pre0" +authors = ["MobileCoin"] +edition = "2021" +readme = "README.md" + +[dependencies] +# External dependencies +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +subtle = { version = "2.4.1", default-features = false, features = ["i128"] } +zeroize = { version = "1", default-features = false } + +# MobileCoin dependencies +mc-crypto-digestible = { path = "../../crypto/digestible", features = ["dalek", "derive"] } diff --git a/transaction/types/README.md b/transaction/types/README.md new file mode 100644 index 0000000000..211a3804ad --- /dev/null +++ b/transaction/types/README.md @@ -0,0 +1,8 @@ +mc-transaction-types +==================== + +This crate defines several small wrapper types around integers and such according +to their purpose, to try to promote self-describing and type-safe APIs. + +It includes some other low-level structures related to transactions and amounts, +that are used often in APIs. diff --git a/transaction/types/src/amount.rs b/transaction/types/src/amount.rs new file mode 100644 index 0000000000..61acbb5434 --- /dev/null +++ b/transaction/types/src/amount.rs @@ -0,0 +1,24 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + +//! An aggregate which represents an amount of some token in the MobileCoin +//! blockchain. + +use crate::token::TokenId; +use mc_crypto_digestible::Digestible; +use zeroize::Zeroize; + +/// An amount of some token, in the "base" (u64) denomination. +#[derive(Clone, Copy, Debug, Digestible, Eq, PartialEq, Zeroize)] +pub struct Amount { + /// The "raw" value of this amount as a u64 + pub value: u64, + /// The token-id which is the denomination of this amount + pub token_id: TokenId, +} + +impl Amount { + /// Create a new amount + pub fn new(value: u64, token_id: TokenId) -> Self { + Self { value, token_id } + } +} diff --git a/transaction/types/src/lib.rs b/transaction/types/src/lib.rs new file mode 100644 index 0000000000..1f79e6023a --- /dev/null +++ b/transaction/types/src/lib.rs @@ -0,0 +1,14 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + +//! Type-safe wrappers for integers used in our transactions, and other +//! low-level types. This crate is intended to have a small footprint +//! and be maximally portable. + +#![no_std] +#![deny(missing_docs)] + +mod amount; +mod token; + +pub use amount::Amount; +pub use token::TokenId; diff --git a/transaction/types/src/token.rs b/transaction/types/src/token.rs new file mode 100644 index 0000000000..da9990eedc --- /dev/null +++ b/transaction/types/src/token.rs @@ -0,0 +1,94 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + +//! A new-type wrapper for representing TokenIds + +use core::{fmt, hash::Hash, num::ParseIntError, ops::Deref, str::FromStr}; +use mc_crypto_digestible::Digestible; +use serde::{Deserialize, Serialize}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use zeroize::Zeroize; + +/// Token Id, used to identify different assets on on the blockchain. +#[derive( + Clone, + Copy, + Debug, + Deserialize, + Digestible, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize, + Zeroize, +)] +pub struct TokenId(u64); + +impl From for TokenId { + fn from(src: u64) -> Self { + Self(src) + } +} + +impl From<&u64> for TokenId { + fn from(src: &u64) -> Self { + Self(*src) + } +} + +impl fmt::Display for TokenId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl TokenId { + /// Represents the MobileCoin token id for MOB token + pub const MOB: Self = Self(0); + + /// Represents the number of bytes in a well-formed TokenId + pub const NUM_BYTES: usize = 8; +} + +impl Deref for TokenId { + type Target = u64; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl FromStr for TokenId { + type Err = ParseIntError; + fn from_str(src: &str) -> Result { + let src = u64::from_str(src)?; + Ok(TokenId(src)) + } +} + +impl PartialEq for TokenId { + fn eq(&self, other: &u64) -> bool { + self.0 == *other + } +} + +impl PartialEq for u64 { + fn eq(&self, other: &TokenId) -> bool { + *self == other.0 + } +} + +impl ConstantTimeEq for TokenId { + fn ct_eq(&self, other: &TokenId) -> Choice { + self.0.ct_eq(&other.0) + } +} + +impl ConditionallySelectable for TokenId { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Self(ConditionallySelectable::conditional_select( + &a.0, &b.0, choice, + )) + } +}