Compare commits

..

93 Commits

Author SHA1 Message Date
serinko 267429abfb edit testing script 2023-12-26 14:33:33 +01:00
serinko 5f1e2c6cf7 update gdpr info 2023-12-26 13:07:00 +01:00
serinko 1d89bc8b23 add official nymvpn faq page 2023-12-26 13:00:52 +01:00
serinko 9bd9b3f8ab Merge branch 'develop' of github_serinko:nymtech/nym into serinko/ccc-event-guide 2023-12-26 12:45:50 +01:00
serinko 7e16932c4a [DOC]: CCC 23 event pages + NymVPN guide (#4261)
* initialise ccc event pages

* compose existing FAQs

* edit flow

* restructure faq

* add vpn install steps

* add testing section

* initialise troubleshooting

* note add warning

* add warning

* add run cli steps

* add args

* document running flow

* add mulwald info to args desc

* add consent warning

* add FAQ questions

* add FAQ questions

* syntax fix

* edit guide flow

* add error troubleshooting point

* add warning to use two gateways

* remove reduntant message

* add clarification on testing

* rename to 37c3 to respect convention

* delete mixnet overview, replace with a link

* delete redundant

* grammar tweaks

* add gui config steps

* syntax fix

* syntax fix

* add releases and simple command info

* syntax edits

* add clarification on test logs

* describe TUN and IP flag

* add embedded video

* spellcheck

---------

Co-authored-by: mfahampshire <mfahampshire@pm.me>
2023-12-22 14:37:28 +00:00
Zane Schepke a0a44509af Merge pull request #4271 from nymtech/feat/vpnapp_ui_zoom
feat(vpn-deskop): add ui zoom level setting
2023-12-22 07:43:35 -05:00
serinko 2a1fdb42c9 spellcheck 2023-12-21 22:40:33 +01:00
serinko d767f76b6d add embedded video 2023-12-21 22:36:03 +01:00
serinko 8a34af0a20 describe TUN and IP flag 2023-12-21 21:53:27 +01:00
serinko ee7e0ff1d3 add clarification on test logs 2023-12-21 20:33:35 +01:00
Zane Schepke 852ed78e5d update README 2023-12-21 12:33:58 -05:00
serinko d4e0f43a05 syntax edits 2023-12-21 18:25:48 +01:00
serinko 945282c624 add releases and simple command info 2023-12-21 18:21:20 +01:00
serinko 69f6c6e36e syntax fix 2023-12-21 18:02:43 +01:00
mx 0d52800569 dkg ceremony overview (#4269)
* added initial dkg ceremony info

* rebase with pwnfoo's changes from #4270 

* final tweaks

---------

Co-authored-by: mfahampshire <mfahampshire@pm.me>
2023-12-21 16:55:58 +00:00
serinko 86b8b39c03 syntax fix 2023-12-21 17:51:26 +01:00
serinko 676f2e7f48 add gui config steps 2023-12-21 17:46:46 +01:00
mfahampshire 144d15f554 grammar tweaks 2023-12-21 16:50:07 +01:00
serinko d58dfc4359 delete redundant 2023-12-21 16:04:26 +01:00
serinko 3052069e9f delete mixnet overview, replace with a link 2023-12-21 15:58:27 +01:00
serinko ce4c2d2670 rename to 37c3 to respect convention 2023-12-21 15:56:04 +01:00
serinko 0b60fed339 add clarification on testing 2023-12-21 15:48:39 +01:00
serinko 1d25cfbe33 remove reduntant message 2023-12-21 15:34:22 +01:00
serinko d7d82037a3 add warning to use two gateways 2023-12-21 15:32:23 +01:00
serinko ecfe8ab3d6 add error troubleshooting point 2023-12-21 15:29:21 +01:00
serinko fc455ff3ad edit guide flow 2023-12-21 15:23:08 +01:00
serinko 5002670916 syntax fix 2023-12-21 15:19:54 +01:00
serinko f96d0a3608 add FAQ questions 2023-12-21 15:16:12 +01:00
serinko 84888db5ee add FAQ questions 2023-12-21 15:14:15 +01:00
serinko 3a8eb40b06 add consent warning 2023-12-21 14:34:57 +01:00
serinko d3f115383d add mulwald info to args desc 2023-12-21 14:30:56 +01:00
Sachin Kamath a1dd9e656d Add info on running full node & general improvements (#4270)
* documentation: add info on nyx full node

* documentation: https info for nym-api
2023-12-21 13:29:03 +00:00
serinko 75bf4d8017 document running flow 2023-12-21 14:28:41 +01:00
pierre 1ea78e8e97 add ui zoom level setting 2023-12-21 14:25:15 +01:00
serinko 4c5606a214 add args 2023-12-21 12:48:33 +01:00
serinko 873465f9fa add run cli steps 2023-12-21 09:47:00 +01:00
serinko 75ea5afe34 add warning 2023-12-21 09:27:18 +01:00
serinko 0444094510 note add warning 2023-12-21 09:09:26 +01:00
serinko ce6a8708d0 initialise troubleshooting 2023-12-21 09:05:58 +01:00
serinko 839bc3271a add testing section 2023-12-21 08:45:13 +01:00
Zane Schepke 46d68e5448 Merge pull request #4268 from nymtech/refactor/vpn_desktop_minor
refactor(vpn-desktop): minor changes
2023-12-20 12:09:56 -05:00
pierre 2c820ca0ea update country list, remove app name from topbar 2023-12-20 17:40:53 +01:00
serinko b0668817ca add vpn install steps 2023-12-20 15:50:59 +01:00
Zane Schepke 2c731cf048 Merge pull request #4265 from nymtech/fix/vpn_desktop_style
refactor(vpn-desktop): minor style changes
2023-12-20 09:37:51 -05:00
pierre 9a1f28bd43 fix spacing issue 2023-12-20 15:27:56 +01:00
Zane Schepke b9493004aa Merge pull request #4264 from nymtech/refactor/vpn_desktop_style
refactor(vpn-desktop): fix style
2023-12-20 08:49:19 -05:00
pierre b27f806c40 fix spacing issue 2023-12-20 14:47:24 +01:00
Pierre Dommerc 08aaa8813e refactor(vpn-desktop): clean ui (#4263)
* set cursor default on disabled state

* fix long error messages overflow

* clean code

* fix hop select component style

* fix exit select button

* add nym vpn icon

* style refactoring

* style refactoring

* fix hover bg country list in light mode

* fix hover bg country list in light mode

---------

Co-authored-by: Zane Schepke <zanecschepke@gmail.com>
2023-12-20 14:13:48 +01:00
serinko c5f534c76d restructure faq 2023-12-20 13:01:42 +01:00
serinko a1f1fd9ce7 edit flow 2023-12-20 11:55:01 +01:00
serinko dc840d3988 compose existing FAQs 2023-12-20 11:49:33 +01:00
serinko f4784c5d53 initialise ccc event pages 2023-12-19 15:57:06 +01:00
Pierre Dommerc dd9f4f24f1 doc(vpn-desktop): update readme (#4260) 2023-12-19 11:33:21 +01:00
Zane Schepke f6c2cab531 feat(vpn-desktop): add exit node location (#4234)
* add entry node selection

* update quick prefix to fastest

* add entry node selection

* update quick prefix to fastest

* add country selection

* add country selection

* remove config properties

* disable entry location for now

* refactor: rename entry location selector command

* remove un-related comment

* refactor: renamed entry location selector

* use translation key for copy

* fix on connect error handling

* use a default entry location for now

* refactor(backend): move country list into module

* refactor(backend): init app state based on restored app data

* fix(backend): connect command

* add comments

* fix country select bug

* add entry_node_location to app config file

* add proper default location handling

* clean code

---------

Co-authored-by: pierre <dommerc.pierre@gmail.com>
2023-12-19 11:15:11 +01:00
Jon Häggblad f4dd9a915d Add some license entries to Cargo.toml files (#4250)
* Add Apache 2.0 to bunch of common crates

* Allow some basic licenses

* Add more licenses
2023-12-19 09:21:33 +01:00
Jędrzej Stuczyński dcfd46ecf0 Merge pull request #4257 from nymtech/chore/reduce-verloc-logs
reduced log severity when verloc connection fails
2023-12-18 12:42:40 +00:00
Jędrzej Stuczyński d331e75375 reduced log severity when verloc connection fails 2023-12-18 12:27:25 +00:00
Jędrzej Stuczyński bac0f24cf7 Feature/issued credentials api (#4207)
* split up coconut module a bit

* internal tool for watching dkg state and updating group contract

* debug dkg state

* display past dealer data

* improved EpochState Display impl

* display contract errors + advance epoch state

* check admin

* panic handler

* simplify app.rs

* split action enum

* added new tab with logger information

* new dealing display

* sort by index

* [fixedup] wip: updating epoch issued credentials - OG 92ade10384a6d7b6c6c222d2e29d69d3b3446a4c

* storing and signing partial blinded credentials

* starting cleanup

* fixed coconut tests + clippy

* fixed nym-api tests

* removed dkg-manager tool

it was moved to a different branch

* implemented remaining endpoints

* unit tests + bug fixes

* clippy

* added persistent identity keys to nym-api

theyre not yet announced - this will be in another PR

* cargo fmt

* clippy

* fixed loading of old configs without storage paths set

* added additional logs for blind-sign endpoint

* fixed up licenses

* lowercasing error variants

* changed 'issued_credentials' to a post

* added minimal client support

* fixed the unit test
2023-12-18 12:51:34 +01:00
Tommy Verrall 6bba371c90 Merge pull request #4236 from nymtech/feature/update-sandbox-validator
update sandbox references
2023-12-18 09:55:45 +00:00
Jon Häggblad 404b043591 Add ci-cargo-deny (#4254) 2023-12-16 22:16:29 +01:00
Pierre Dommerc e09b33baff fix(vpn-desktop): typo (#4252) 2023-12-15 14:17:12 +01:00
Pierre Dommerc 82bfab48a5 feat(vpn-desktop): integrate nym-vpn-lib (#4244)
* fix initial selected vpn mode

* wip

* Set gateway config

* Init procedure and reading config

* Update two comments

* add nym-api field to app config

* Remove hardcoded RUST_LOG from package.json

* Use scope instead of explicit drop

* Spawn vpn client in separate thread and separate runtime

* Re-set nym-vpn-lib in Cargo.toml

* add vpn handle to app state

* add vpn client call to disconnect cmd

* wip

* Setup listener tasks

* Read entire env after all

* add env config file to app config

* doc: add notes on config

* refactor env config file as optional

* add logic to connection status changes

* refactor disconnect command

* fix handle click connect button

* update doc

* add some fake delay to establish connection

* localize backend messages

* refactor extract registering listeners into modules

* add more tracing logs

* refactor clean code

* refactor clean code

* refactor vpn config creation

* fix connect app_config read

* refactor rename listener functions

* add backend support for twohop mode

* copy change

* base connected status on Ready message

Ready message sent from vpn client

* filter out specific error

* add logs

* use exported receiver types from nym_vpn_lib

* Handle exit message

* Change to nym-vpn for consistency

* prefix comment with TODO

* update doc

* remove nym_api config property

use the one provided in the env config file

* fix css compile error

* log received backend error (frontend)

---------

Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
2023-12-15 13:25:49 +01:00
mx e8956603d7 tweak (#4251) 2023-12-14 15:34:48 +00:00
Jon Häggblad a81e7e6c53 Tweak task dropped log verbosity (#4249) 2023-12-14 10:26:09 +01:00
Jon Häggblad 723e30fb1d Replace panic with error for inserting duplicate pending ack (#4248) 2023-12-14 10:13:59 +01:00
mx 951f8e7a74 Max/hide nymvisor intros cleanup (#4246)
* * removed shipyard info for moment
* removed nymvisor page until next release

* removed 'new' from operator and tssdk docs

* removed nymvisor for moment
2023-12-13 12:07:37 +00:00
mx 4462dae45c nymvisor and nym-api docs (#4224)
* added nymvisor and nymapi to build output

* added new pages to summary

* added todo for nymvisor systemd

* typo fix

* extracted old upgrade info out of maintance page to its own

* stub page for api

* halfway done on nymvisor page

* small typo fix

* first pass complete

* removed wip note

* updated path to upgrade doc

* structure + first pass

* nymapi first pass

* finished maintenance + api update

* review fixes

* fixed command for --help cmdrun rendering
2023-12-13 08:50:50 +00:00
Jon Häggblad 8bfe670c9d Add some log statements (#4245) 2023-12-12 23:05:26 +01:00
Jędrzej Stuczyński 7ca801fff3 Merge pull request #4241 from nymtech/bugfix/concurrent-proposals
Make sure proposals are locking other requests
2023-12-12 10:58:05 +00:00
Jędrzej Stuczyński 8a92cca448 Make sure proposals are locking other requests 2023-12-12 10:40:28 +00:00
Tommy Verrall 4308f602ea Merge pull request #4231 from nymtech/jon/add-ipr-to-self-described
Add IPR to nym-node http endpoint
2023-12-09 06:33:13 +00:00
Tommy Verrall 9ea2eafb2c Update documentation/operators/src/nodes/validator-setup.md
Co-authored-by: Sachin Kamath <github@skamath.me>
2023-12-08 15:25:26 +01:00
Tommy Verrall 061aa6b7bd update sandbox references 2023-12-08 15:10:26 +01:00
Tommy Verrall 3ee1e541ff Merge pull request #4235 from nymtech/simulate-with-memo
Propagating memo field when simulating token transfer
2023-12-08 13:50:59 +00:00
Tommy Verrall 866309cedf fix linting 2023-12-08 14:19:58 +01:00
Tommy Verrall 2d57ed49e8 amend a few warnings 2023-12-08 14:08:59 +01:00
Jon Häggblad a08cc64fc7 typo 2023-12-08 12:28:03 +01:00
Jędrzej Stuczyński 23892fec8c Propagating memo field when simulating token transfer 2023-12-08 11:19:11 +00:00
Tommy Verrall d807f66944 Merge pull request #4232 from nymtech/jon/ipr-node-info
Add ip-packet-router info to node details printout in gateway
2023-12-08 10:53:53 +00:00
Jon Häggblad 0861304368 Add routes to openapi 2023-12-08 09:31:23 +01:00
Jon Häggblad 077ea25990 capitalization 2023-12-08 09:10:33 +01:00
Jon Häggblad 77679064de rustfmt 2023-12-08 09:00:21 +01:00
Jon Häggblad 2052577174 Add ip-packet-router info to node details printout in gateway 2023-12-08 08:57:28 +01:00
Jon Häggblad 24a859d03c Add to nym-api node-describe-cache 2023-12-07 23:57:17 +01:00
Jon Häggblad b898ad3e97 Remove unused 2023-12-07 23:44:00 +01:00
Jon Häggblad af3a216f71 formatting 2023-12-07 23:40:06 +01:00
Jon Häggblad 7866cb0ae8 Add IPR to nym-node http endpoint 2023-12-07 23:38:31 +01:00
Pierre Dommerc 40adedb5e1 fix initial selected vpn mode (#4230) 2023-12-07 19:09:30 +01:00
Tommy Verrall c1660c2b27 Merge pull request #4191 from nymtech/feature/update-pm-and-cost-params
Wallet - Update bonding PM and OC limits
2023-12-07 16:02:21 +00:00
fmtabbara 84b497ab20 fix conflicts 2023-12-04 22:57:30 +00:00
fmtabbara 14d5e112d0 update profit margin and operator cost params for bonding 2023-11-28 14:24:35 +00:00
fmtabbara 620c5e1188 refresh balance every 10 secs when balance page is open 2023-11-28 14:10:01 +00:00
fmtabbara 60c740f723 remove unnecessary wording from reward claim confirmation 2023-11-28 14:09:29 +00:00
280 changed files with 12163 additions and 3139 deletions
+21
View File
@@ -0,0 +1,21 @@
name: ci-cargo-deny
on: [workflow_dispatch]
jobs:
cargo-deny:
runs-on: ubuntu-22.04
strategy:
matrix:
checks:
# - advisories
- licenses
- bans sources
continue-on-error: ${{ matrix.checks == 'licenses' }}
steps:
- uses: actions/checkout@v3
- uses: EmbarkStudios/cargo-deny-action@v1
with:
log-level: warn
command: check ${{ matrix.checks }}
argument: --all-features
+1
View File
@@ -9,6 +9,7 @@
target
.env
.env.dev
envs/devnet.env
/.vscode/settings.json
validator/.vscode
sample-configs/validator-config.toml
Generated
+38 -89
View File
@@ -987,27 +987,13 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
[[package]]
name = "bls12_381"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54757888b09a69be70b5ec303e382a74227392086ba808cb01eeca29233a2397"
version = "0.8.0"
source = "git+https://github.com/jstuczyn/bls12_381?branch=feature/gt-serialization-0.8.0#c4543fde7d02efea6ecfcf22e14476ddb516b483"
dependencies = [
"digest 0.9.0",
"ff 0.10.1",
"group 0.10.0",
"pairing 0.20.0",
"rand_core 0.6.4",
"subtle 2.4.1",
]
[[package]]
name = "bls12_381"
version = "0.6.0"
source = "git+https://github.com/jstuczyn/bls12_381?branch=gt-serialisation#10fb6f700bfda17c8475af3bfd31e3fec15f2278"
dependencies = [
"digest 0.9.0",
"ff 0.11.1",
"group 0.11.0",
"pairing 0.21.0",
"ff 0.13.0",
"group 0.13.0",
"pairing",
"rand_core 0.6.4",
"subtle 2.4.1",
"zeroize",
@@ -3030,26 +3016,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "ff"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f"
dependencies = [
"rand_core 0.6.4",
"subtle 2.4.1",
]
[[package]]
name = "ff"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924"
dependencies = [
"rand_core 0.6.4",
"subtle 2.4.1",
]
[[package]]
name = "ff"
version = "0.12.1"
@@ -3066,6 +3032,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
dependencies = [
"bitvec",
"rand_core 0.6.4",
"subtle 2.4.1",
]
@@ -3519,30 +3486,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "group"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912"
dependencies = [
"byteorder",
"ff 0.10.1",
"rand_core 0.6.4",
"subtle 2.4.1",
]
[[package]]
name = "group"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89"
dependencies = [
"byteorder",
"ff 0.11.1",
"rand_core 0.6.4",
"subtle 2.4.1",
]
[[package]]
name = "group"
version = "0.12.1"
@@ -4286,6 +4229,15 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.9"
@@ -5937,6 +5889,7 @@ dependencies = [
"futures-util",
"getset",
"humantime-serde",
"itertools 0.12.0",
"lazy_static",
"log",
"nym-api-requests",
@@ -6001,10 +5954,12 @@ dependencies = [
"cosmwasm-std",
"getset",
"nym-coconut-interface",
"nym-crypto",
"nym-mixnet-contract-common",
"nym-node-requests",
"schemars",
"serde",
"tendermint",
"ts-rs",
]
@@ -6022,6 +5977,7 @@ dependencies = [
"rand 0.7.3",
"thiserror",
"url",
"zeroize",
]
[[package]]
@@ -6257,14 +6213,14 @@ dependencies = [
name = "nym-coconut"
version = "0.5.0"
dependencies = [
"bls12_381 0.6.0",
"bls12_381",
"bs58 0.4.0",
"criterion",
"digest 0.9.0",
"doc-comment",
"ff 0.11.1",
"ff 0.13.0",
"getrandom 0.2.10",
"group 0.11.0",
"group 0.13.0",
"itertools 0.10.5",
"nym-dkg",
"nym-pemstore",
@@ -6274,6 +6230,7 @@ dependencies = [
"serde_derive",
"sha2 0.9.9",
"thiserror",
"zeroize",
]
[[package]]
@@ -6364,7 +6321,7 @@ dependencies = [
name = "nym-credentials"
version = "0.1.0"
dependencies = [
"bls12_381 0.5.0",
"bls12_381",
"cosmrs",
"log",
"nym-api-requests",
@@ -6373,6 +6330,7 @@ dependencies = [
"nym-validator-client",
"rand 0.7.3",
"thiserror",
"zeroize",
]
[[package]]
@@ -6406,11 +6364,11 @@ name = "nym-dkg"
version = "0.1.0"
dependencies = [
"bitvec",
"bls12_381 0.6.0",
"bls12_381",
"bs58 0.4.0",
"criterion",
"ff 0.11.1",
"group 0.11.0",
"ff 0.13.0",
"group 0.13.0",
"lazy_static",
"nym-contracts-common",
"nym-pemstore",
@@ -6521,6 +6479,7 @@ dependencies = [
"nym-validator-client",
"nym-wireguard",
"once_cell",
"pretty_env_logger",
"rand 0.7.3",
"rand 0.8.5",
"serde",
@@ -6722,6 +6681,7 @@ dependencies = [
"nym-types",
"nym-validator-client",
"opentelemetry",
"pretty_env_logger",
"rand 0.7.3",
"serde",
"serde_json",
@@ -6746,7 +6706,6 @@ dependencies = [
"log",
"nym-bin-common",
"nym-crypto",
"nym-mixnet-contract-common",
"nym-network-defaults",
"nym-sphinx-acknowledgements",
"nym-sphinx-addressing",
@@ -6756,7 +6715,6 @@ dependencies = [
"nym-sphinx-types",
"nym-task",
"nym-validator-client",
"parking_lot 0.12.1",
"rand 0.8.5",
"serde",
"thiserror",
@@ -7882,20 +7840,11 @@ dependencies = [
[[package]]
name = "pairing"
version = "0.20.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7de9d09263c9966e8196fe0380c9dbbc7ea114b5cf371ba29004bc1f9c6db7f3"
checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f"
dependencies = [
"group 0.10.0",
]
[[package]]
name = "pairing"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2e415e349a3006dd7d9482cdab1c980a845bed1377777d768cb693a44540b42"
dependencies = [
"group 0.11.0",
"group 0.13.0",
]
[[package]]
@@ -9790,9 +9739,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.107"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
@@ -11014,16 +10963,16 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d"
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
dependencies = [
"bytes",
"futures-core",
"futures-io",
"futures-sink",
"futures-util",
"hashbrown 0.12.3",
"hashbrown 0.14.1",
"pin-project-lite 0.2.13",
"slab",
"tokio",
+10 -2
View File
@@ -163,9 +163,8 @@ tap = "1.0.1"
time = "0.3.30"
thiserror = "1.0.48"
tokio = "1.33.0"
tokio-stream = "0.1.14"
tokio-util = "0.7.10"
tokio-tungstenite = "0.20.1"
tokio-util = "0.7.9"
tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false }
ts-rs = "7.0.0"
@@ -174,6 +173,14 @@ utoipa-swagger-ui = "3.1.5"
url = "2.4"
zeroize = "1.6.0"
# coconut/DKG related
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="feature/gt-serialization-0.8.0" }
group = "0.13.0"
ff = "0.13.0"
# cosmwasm-related
cosmwasm-derive = "=1.3.0"
cosmwasm-schema = "=1.3.0"
@@ -194,6 +201,7 @@ cw-controllers = { version = "=1.1.0" }
bip32 = "0.5.1"
cosmrs = "=0.15.0"
tendermint-rpc = "0.34" # same version as used by cosmrs
tendermint = "0.34" # same version as used by cosmrs
prost = "0.12"
# wasm-related dependencies
+1
View File
@@ -5,6 +5,7 @@ authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej St
description = "Implementation of the Nym Client"
edition = "2021"
rust-version = "1.65"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -3,6 +3,7 @@ name = "nym-client-websocket-requests"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -5,6 +5,7 @@ authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
rust-version = "1.56"
license.workspace = true
[dependencies]
clap = { workspace = true, features = ["cargo", "derive"] }
+1
View File
@@ -2,6 +2,7 @@
name = "async-file-watcher"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+2
View File
@@ -2,6 +2,7 @@
name = "nym-bandwidth-controller"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -10,6 +11,7 @@ bip39 = { workspace = true }
rand = "0.7.3"
thiserror = { workspace = true }
url = { workspace = true }
zeroize = { workspace = true }
nym-coconut-interface = { path = "../coconut-interface" }
nym-credential-storage = { path = "../credential-storage" }
+12 -15
View File
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::BandwidthControllerError;
use nym_coconut_interface::{Base58, Parameters};
use nym_coconut_interface::Base58;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
use nym_credentials::coconut::utils::obtain_aggregate_signature;
use nym_crypto::asymmetric::{encryption, identity};
use nym_network_defaults::VOUCHER_INFO;
@@ -12,10 +12,8 @@ use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use nym_validator_client::nyxd::Coin;
use nym_validator_client::nyxd::Hash;
use rand::rngs::OsRng;
use state::{KeyPair, State};
use std::str::FromStr;
use state::State;
pub mod state;
@@ -24,30 +22,29 @@ where
C: CoconutBandwidthSigningClient + Sync,
{
let mut rng = OsRng;
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let signing_key = identity::PrivateKey::new(&mut rng);
let encryption_key = encryption::PrivateKey::new(&mut rng);
let params = BandwidthVoucher::default_parameters();
let voucher_value = amount.amount.to_string();
let tx_hash = client
.deposit(
amount,
String::from(VOUCHER_INFO),
signing_keypair.public_key.clone(),
encryption_keypair.public_key.clone(),
signing_key.public_key().to_base58_string(),
encryption_key.public_key().to_base58_string(),
None,
)
.await?
.transaction_hash
.to_string();
.transaction_hash;
let voucher = BandwidthVoucher::new(
&params,
voucher_value,
VOUCHER_INFO.to_string(),
Hash::from_str(&tx_hash).map_err(|_| BandwidthControllerError::InvalidTxHash)?,
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
tx_hash,
signing_key,
encryption_key,
);
let state = State { voucher, params };
@@ -2,32 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use nym_coconut_interface::Parameters;
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use nym_crypto::asymmetric::{encryption, identity};
pub(crate) struct KeyPair {
pub public_key: String,
pub private_key: String,
}
impl From<identity::KeyPair> for KeyPair {
fn from(kp: identity::KeyPair) -> Self {
Self {
public_key: kp.public_key().to_base58_string(),
private_key: kp.private_key().to_base58_string(),
}
}
}
impl From<encryption::KeyPair> for KeyPair {
fn from(kp: encryption::KeyPair) -> Self {
Self {
public_key: kp.public_key().to_base58_string(),
private_key: kp.private_key().to_base58_string(),
}
}
}
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
pub struct State {
pub voucher: BandwidthVoucher,
@@ -38,7 +13,7 @@ impl State {
pub fn new(voucher: BandwidthVoucher) -> Self {
State {
voucher,
params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(),
params: BandwidthVoucher::default_parameters(),
}
}
}
+9 -6
View File
@@ -7,6 +7,7 @@ use nym_credential_storage::storage::Storage;
use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::str::FromStr;
use zeroize::Zeroizing;
use {
nym_coconut_interface::Base58,
nym_credentials::coconut::{
@@ -46,10 +47,12 @@ impl<C, St: Storage> BandwidthController<C, St> {
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
.map_err(|_| StorageError::InconsistentData)?;
let voucher_info = bandwidth_credential.voucher_info.clone();
let serial_number =
nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.serial_number)?;
let binding_number =
nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.binding_number)?;
let serial_number = Zeroizing::new(nym_coconut_interface::Attribute::try_from_bs58(
bandwidth_credential.serial_number,
)?);
let binding_number = Zeroizing::new(nym_coconut_interface::Attribute::try_from_bs58(
bandwidth_credential.binding_number,
)?);
let signature =
nym_coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?;
let epoch_id = u64::from_str(&bandwidth_credential.epoch_id)
@@ -64,8 +67,8 @@ impl<C, St: Storage> BandwidthController<C, St> {
prepare_for_spending(
voucher_value,
voucher_info,
serial_number,
binding_number,
&serial_number,
&binding_number,
epoch_id,
&signature,
&verification_key,
+3 -2
View File
@@ -4,6 +4,7 @@ version = "1.1.15"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.66"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -47,7 +48,7 @@ nym-credential-storage = { path = "../credential-storage" }
nym-network-defaults = { path = "../network-defaults" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
workspace = true
version = "0.1.11"
features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
@@ -55,7 +56,7 @@ workspace = true
features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
workspace = true
version = "0.20.1"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
version = "0.6.2"
@@ -127,7 +127,9 @@ impl ActionController {
.insert(frag_id, (Arc::new(pending_ack), None))
.is_some()
{
panic!("Tried to insert duplicate pending ack")
// This used to be a panic, however since we've seen this actually happen in the
// wild, let's not take the whole client (and possibly gateway) down because of it.
error!("Tried to insert duplicate pending ack! This should not be possible!")
}
}
}
+1
View File
@@ -259,6 +259,7 @@ pub(super) fn get_specified_gateway(
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<gateway::Node, ClientCoreError> {
log::debug!("Requesting specified gateway: {}", gateway_identity);
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
+1 -1
View File
@@ -212,7 +212,7 @@ where
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
log::trace!("Setting up gateway");
log::debug!("Setting up gateway");
match setup {
GatewaySetup::MustLoad => use_loaded_gateway_details(key_store, details_store).await,
GatewaySetup::New {
+2 -1
View File
@@ -3,6 +3,7 @@ name = "nym-gateway-client"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -40,7 +41,7 @@ workspace = true
features = ["macros", "rt", "net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
workspace = true
version = "0.1.11"
features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
@@ -792,6 +792,7 @@ pub struct InitOnly;
impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
// for initialisation we do not need credential storage. Though it's still a bit weird we have to set the generic...
pub fn new_init(config: GatewayConfig, local_identity: Arc<identity::KeyPair>) -> Self {
log::trace!("Initialising gateway client");
use futures::channel::mpsc;
// note: this packet_router is completely invalid in normal circumstances, but "works"
+2 -1
View File
@@ -3,13 +3,14 @@ name = "nym-mixnet-client"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
futures = { workspace = true }
log = { workspace = true }
tokio = { version = "1.24.1", features = ["time", "net", "rt"] }
tokio = { workspace = true, features = ["time", "net", "rt"] }
tokio-util = { workspace = true, features = ["codec"] }
# internal
+11 -6
View File
@@ -123,10 +123,13 @@ impl Client {
// We could have as well used conn.send_all(receiver.map(Ok)), but considering we don't care
// about neither receiver nor the connection, it doesn't matter which one gets consumed
if let Err(err) = receiver.map(Ok).forward(conn).await {
warn!("Failed to forward packets to {address} - {err}");
warn!("Failed to forward packets to {} - {err}", address);
}
debug!("connection manager to {address} is finished. Either the connection failed or mixnet client got dropped");
debug!(
"connection manager to {} is finished. Either the connection failed or mixnet client got dropped",
address
);
}
/// If we're trying to reconnect, determine how long we should wait.
@@ -204,7 +207,7 @@ impl SendWithoutResponse for Client {
if let Some(sender) = self.conn_new.get_mut(&address) {
if let Err(err) = sender.channel.try_send(framed_packet) {
if err.is_full() {
debug!("Connection to {address} seems to not be able to handle all the traffic - dropping the current packet");
debug!("Connection to {} seems to not be able to handle all the traffic - dropping the current packet", address);
// it's not a 'big' error, but we did not manage to send the packet
// if the queue is full, we can't really do anything but to drop the packet
Err(io::Error::new(
@@ -212,8 +215,10 @@ impl SendWithoutResponse for Client {
"connection queue is full",
))
} else if err.is_disconnected() {
debug!("Connection to {address} seems to be dead. attempting to re-establish it...");
debug!(
"Connection to {} seems to be dead. attempting to re-establish it...",
address
);
// it's not a 'big' error, but we did not manage to send the packet, but queue
// it up to send it as soon as the connection is re-established
self.make_connection(address, err.into_inner());
@@ -233,7 +238,7 @@ impl SendWithoutResponse for Client {
}
} else {
// there was never a connection to begin with
debug!("establishing initial connection to {address}");
debug!("establishing initial connection to {}", address);
// it's not a 'big' error, but we did not manage to send the packet, but queue the packet
// for sending for as soon as the connection is created
self.make_connection(address, framed_packet);
@@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
rust-version = "1.56"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -5,8 +5,12 @@ use crate::nym_api::error::NymAPIError;
use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use async_trait::async_trait;
use http_api_client::{ApiClient, NO_PARAMS};
use nym_api_requests::coconut::models::{
EpochCredentialsResponse, IssuedCredentialResponse, IssuedCredentialsResponse,
};
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody, VerifyCredentialBody,
VerifyCredentialResponse,
};
use nym_api_requests::models::{
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
@@ -15,6 +19,7 @@ use nym_api_requests::models::{
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
StakeSaturationResponse, UptimeResponse,
};
use nym_coconut_dkg_common::types::EpochId;
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use nym_name_service_common::response::NamesListResponse;
@@ -399,6 +404,60 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn epoch_credentials(
&self,
dkg_epoch: EpochId,
) -> Result<EpochCredentialsResponse, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_EPOCH_CREDENTIALS,
&dkg_epoch.to_string(),
],
NO_PARAMS,
)
.await
}
async fn issued_credential(
&self,
credential_id: i64,
) -> Result<IssuedCredentialResponse, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_ISSUED_CREDENTIAL,
&credential_id.to_string(),
],
NO_PARAMS,
)
.await
}
async fn issued_credentials(
&self,
credential_ids: Vec<i64>,
) -> Result<IssuedCredentialsResponse, NymAPIError> {
self.post_json(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_ISSUED_CREDENTIALS,
],
NO_PARAMS,
&CredentialsRequestBody {
credential_ids,
pagination: None,
},
)
.await
}
async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
log::trace!("Getting service providers");
self.get_json(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
@@ -17,6 +17,9 @@ pub const BANDWIDTH: &str = "bandwidth";
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
pub const COCONUT_EPOCH_CREDENTIALS: &str = "epoch-credentials";
pub const COCONUT_ISSUED_CREDENTIAL: &str = "issued-credential";
pub const COCONUT_ISSUED_CREDENTIALS: &str = "issued-credentials";
pub const STATUS_ROUTES: &str = "status";
pub const MIXNODE: &str = "mixnode";
@@ -32,7 +32,7 @@ pub trait CoconutBandwidthSigningClient {
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = CoconutBandwidthExecuteMsg::DepositFunds {
data: DepositData::new(info.to_string(), verification_key, encryption_key),
data: DepositData::new(info, verification_key, encryption_key),
};
self.execute_coconut_bandwidth_contract(
fee,
@@ -6,8 +6,8 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::CosmWasmClient;
use async_trait::async_trait;
use cw3::{
ProposalListResponse, ProposalResponse, VoteListResponse, VoteResponse, VoterListResponse,
VoterResponse,
ProposalListResponse, ProposalResponse, VoteListResponse, VoteResponse, VoterDetail,
VoterListResponse, VoterResponse,
};
use cw_utils::ThresholdResponse;
use nym_multisig_contract_common::msg::QueryMsg as MultisigQueryMsg;
@@ -114,6 +114,26 @@ pub trait PagedMultisigQueryClient: MultisigQueryClient {
Ok(proposals)
}
async fn get_all_voters(&self) -> Result<Vec<VoterDetail>, NyxdError> {
let mut voters = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self.list_voters(start_after.take(), None).await?;
let last_voter = paged_response.voters.last().map(|prop| prop.addr.clone());
voters.append(&mut paged_response.voters);
if let Some(start_after_res) = last_voter {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(voters)
}
}
#[async_trait]
@@ -0,0 +1,13 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::TxResponse;
pub fn find_tx_attribute(tx: &TxResponse, event_type: &str, attribute_key: &str) -> Option<String> {
let event = tx.tx_result.events.iter().find(|e| e.kind == event_type)?;
let attribute = event
.attributes
.iter()
.find(|attr| attr.key == attribute_key)?;
Some(attribute.value.clone())
}
@@ -47,6 +47,10 @@ pub use cosmrs::Coin as CosmosCoin;
pub use cosmrs::Gas;
pub use cosmrs::{bip32, AccountId, Denom};
pub use cosmwasm_std::Coin as CosmWasmCoin;
pub use cw2;
pub use cw3;
pub use cw4;
pub use cw_controllers;
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
pub use tendermint_rpc::{
endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse},
@@ -55,9 +59,6 @@ pub use tendermint_rpc::{
};
pub use tendermint_rpc::{Request, Response, SimpleRequest};
#[cfg(feature = "http-client")]
pub use tendermint_rpc::Url as RpcUrl;
#[cfg(feature = "http-client")]
use crate::http_client;
#[cfg(feature = "http-client")]
@@ -70,6 +71,7 @@ pub mod contract_traits;
pub mod cosmwasm_client;
pub mod error;
pub mod fee;
pub mod helpers;
#[derive(Debug, Clone)]
pub struct Config {
@@ -379,7 +381,11 @@ where
})
}
pub async fn simulate<I, M>(&self, messages: I) -> Result<SimulateResponse, NyxdError>
pub async fn simulate<I, M>(
&self,
messages: I,
memo: impl Into<String> + Send + 'static,
) -> Result<SimulateResponse, NyxdError>
where
I: IntoIterator<Item = M> + Send,
M: Msg,
@@ -394,7 +400,7 @@ where
.map_err(|_| {
NyxdError::SerializationError("custom simulate messages".to_owned())
})?,
"simulating execution of transactions",
memo,
)
.await
}
+1
View File
@@ -3,6 +3,7 @@ name = "nym-coconut-interface"
version = "0.1.0"
edition = "2021"
description = "Crutch library until there is proper SerDe support for coconut structs"
license.workspace = true
[dependencies]
bs58 = "0.4.0"
+12 -10
View File
@@ -21,10 +21,14 @@ pub use nym_coconut::{
pub struct Credential {
#[getset(get = "pub")]
n_params: u32,
#[getset(get = "pub")]
theta: Theta,
voucher_value: u64,
voucher_info: String,
#[getset(get = "pub")]
epoch_id: u64,
}
@@ -64,14 +68,12 @@ impl Credential {
pub fn verify(&self, verification_key: &VerificationKey) -> bool {
let params = Parameters::new(self.n_params).unwrap();
let public_attributes = [
self.voucher_value.to_string().as_bytes(),
self.voucher_info.as_bytes(),
]
.iter()
.map(hash_to_scalar)
.collect::<Vec<Attribute>>();
nym_coconut::verify_credential(&params, verification_key, &self.theta, &public_attributes)
let hashed_value = hash_to_scalar(self.voucher_value.to_string());
let hashed_info = hash_to_scalar(&self.voucher_info);
let public_attributes = &[&hashed_value, &hashed_info];
nym_coconut::verify_credential(&params, verification_key, &self.theta, public_attributes)
}
pub fn as_bytes(&self) -> Vec<u8> {
@@ -180,8 +182,8 @@ mod tests {
&params,
&verification_key,
&signature,
serial_number,
binding_number,
&serial_number,
&binding_number,
)
.unwrap();
let credential = Credential::new(4, theta, voucher_value, voucher_info, 42);
+1
View File
@@ -3,6 +3,7 @@ name = "nym-cli-commands"
version = "1.0.0"
authors.workspace = true
edition = "2021"
license.workspace = true
[dependencies]
anyhow = { workspace = true }
@@ -26,6 +26,10 @@ pub struct Args {
}
pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
if args.amount == 0 {
bail!("did not specify credential amount")
}
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
+2 -1
View File
@@ -3,6 +3,7 @@ name = "nym-config"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,4 +18,4 @@ url = { workspace = true }
nym-network-defaults = { path = "../network-defaults" }
[features]
default = ["dirs"]
default = ["dirs"]
@@ -2,6 +2,7 @@
name = "nym-coconut-bandwidth-contract-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -12,4 +13,4 @@ cw2 = { workspace = true, optional = true }
nym-multisig-contract-common = { path = "../multisig-contract" }
[features]
schema = ["cw2"]
schema = ["cw2"]
@@ -4,6 +4,9 @@
// event types
pub const DEPOSITED_FUNDS_EVENT_TYPE: &str = "deposited-funds";
// a 'wasm-' prefix is added to all cosmwasm events
pub const COSMWASM_DEPOSITED_FUNDS_EVENT_TYPE: &str = "wasm-deposited-funds";
// attributes that are used in multiple places
pub const DEPOSIT_VALUE: &str = "deposit-value";
pub const DEPOSIT_INFO: &str = "deposit-info";
@@ -2,6 +2,7 @@
name = "nym-coconut-dkg-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -14,4 +15,4 @@ contracts-common = { path = "../contracts-common", package = "nym-contracts-comm
nym-multisig-contract-common = { path = "../multisig-contract" }
[features]
schema = []
schema = []
@@ -174,17 +174,19 @@ impl Display for EpochState {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
EpochState::PublicKeySubmission { resharing } => {
write!(f, "PublicKeySubmission with resharing {resharing}")
write!(f, "PublicKeySubmission (resharing: {resharing})")
}
EpochState::DealingExchange { resharing } => {
write!(f, "DealingExchange (resharing: {resharing})")
}
EpochState::DealingExchange { resharing } => write!(f, "DealingExchange {resharing}"),
EpochState::VerificationKeySubmission { resharing } => {
write!(f, "VerificationKeySubmission with resharing {resharing}")
write!(f, "VerificationKeySubmission (resharing: {resharing})")
}
EpochState::VerificationKeyValidation { resharing } => {
write!(f, "VerificationKeyValidation with resharing {resharing}")
write!(f, "VerificationKeyValidation (resharing: {resharing})")
}
EpochState::VerificationKeyFinalization { resharing } => {
write!(f, "VerificationKeyFinalization with resharing {resharing}")
write!(f, "VerificationKeyFinalization (resharing: {resharing})")
}
EpochState::InProgress => write!(f, "InProgress"),
}
@@ -2,6 +2,7 @@
name = "nym-ephemera-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -13,4 +14,4 @@ cw-utils = { workspace = true }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common" }
[features]
schema = []
schema = []
@@ -2,6 +2,7 @@
name = "nym-group-contract-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -623,24 +623,6 @@ pub enum Layer {
Three = 3,
}
impl Layer {
pub fn try_next(&self) -> Option<Self> {
match self {
Layer::One => Some(Layer::Two),
Layer::Two => Some(Layer::Three),
Layer::Three => None,
}
}
pub fn try_previous(&self) -> Option<Self> {
match self {
Layer::One => None,
Layer::Two => Some(Layer::One),
Layer::Three => Some(Layer::Two),
}
}
}
impl From<Layer> for String {
fn from(layer: Layer) -> Self {
(layer as u8).to_string()
@@ -2,6 +2,7 @@
name = "nym-multisig-contract-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -2,6 +2,7 @@
name = "nym-name-service-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -16,4 +17,4 @@ serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
[features]
schema = ["cw2"]
schema = ["cw2"]
@@ -2,6 +2,7 @@
name = "nym-service-provider-directory-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -15,4 +16,4 @@ nym-contracts-common = { path = "../contracts-common", version = "0.5.0" }
thiserror = { workspace = true }
[features]
schema = ["cw2"]
schema = ["cw2"]
+1
View File
@@ -2,6 +2,7 @@
name = "nym-credential-storage"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -2,6 +2,7 @@
name = "nym-credential-utils"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+4 -2
View File
@@ -2,18 +2,20 @@
name = "nym-credentials"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
bls12_381 = { workspace = true, default-features = false, features = ["pairings", "alloc", "experimental"] }
cosmrs = { workspace = true }
thiserror = { workspace = true }
log = { workspace = true }
zeroize = { workspace = true }
# I guess temporarily until we get serde support in coconut up and running
nym-coconut-interface = { path = "../coconut-interface" }
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "hashing"] }
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric"] }
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
+93 -51
View File
@@ -13,38 +13,60 @@ use nym_coconut_interface::{
PrivateAttribute, PublicAttribute, Signature, VerificationKey,
};
use nym_crypto::asymmetric::{encryption, identity};
use zeroize::{Zeroize, ZeroizeOnDrop};
use super::utils::prepare_credential_for_spending;
use crate::error::Error;
pub const PUBLIC_ATTRIBUTES: u32 = 2;
pub const PRIVATE_ATTRIBUTES: u32 = 2;
pub const TOTAL_ATTRIBUTES: u32 = PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES;
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct BandwidthVoucher {
// a random secret value generated by the client used for double-spending detection
// private attributes
/// a random secret value generated by the client used for double-spending detection
serial_number: PrivateAttribute,
// a random secret value generated by the client used to bind multiple credentials together
/// a random secret value generated by the client used to bind multiple credentials together
binding_number: PrivateAttribute,
// the value (e.g., bandwidth) encoded in this voucher
voucher_value: PublicAttribute,
// the plain text value (e.g., bandwidth) encoded in this voucher
// public atttributes:
/// the plain text value (e.g., bandwidth) encoded in this voucher
// TODO: in another PR change the value from `"1000"` to `"1000unym"`
voucher_value_plain: String,
// a field with public information, e.g., type of voucher, interval etc.
voucher_info: PublicAttribute,
// the plain text information
/// the plain text information
voucher_info_plain: String,
// the hash of the deposit transaction
/// the precomputed value (e.g., bandwidth) encoded in this voucher
_voucher_value_prehashed: PublicAttribute,
/// the precomputed field with public information, e.g., type of voucher, interval etc.
_voucher_info_prehashed: PublicAttribute,
/// the hash of the deposit transaction
#[zeroize(skip)]
tx_hash: Hash,
// base58 encoded private key ensuring the depositer requested these attributes
/// base58 encoded private key ensuring the depositer requested these attributes
signing_key: identity::PrivateKey,
// base58 encoded private key ensuring only this client receives the signature share
encryption_key: encryption::PrivateKey,
/// base58 encoded private key ensuring only this client receives the signature share
unused_ed25519: encryption::PrivateKey,
pedersen_commitments_openings: Vec<Attribute>,
#[zeroize(skip)]
blind_sign_request: BlindSignRequest,
}
impl BandwidthVoucher {
pub const PUBLIC_ATTRIBUTES: u32 = 2;
pub const PRIVATE_ATTRIBUTES: u32 = 2;
pub const ENCODED_ATTRIBUTES: u32 = 4;
pub fn default_parameters() -> Parameters {
// safety: the unwrap is fine here as Self::ENCODED_ATTRIBUTES is non-zero
Parameters::new(Self::ENCODED_ATTRIBUTES).unwrap()
}
pub fn new(
params: &Parameters,
voucher_value: String,
@@ -57,24 +79,26 @@ impl BandwidthVoucher {
let binding_number = params.random_scalar();
let voucher_value_plain = voucher_value.clone();
let voucher_info_plain = voucher_info.clone();
let voucher_value = hash_to_scalar(voucher_value.as_bytes());
let voucher_info = hash_to_scalar(voucher_info.as_bytes());
let _voucher_value_prehashed = hash_to_scalar(voucher_value);
let _voucher_info_prehashed = hash_to_scalar(voucher_info);
let (pedersen_commitments_openings, blind_sign_request) = prepare_blind_sign(
params,
&[serial_number, binding_number],
&[voucher_value, voucher_info],
&[&serial_number, &binding_number],
&[&_voucher_value_prehashed, &_voucher_info_prehashed],
)
.unwrap();
BandwidthVoucher {
serial_number,
binding_number,
voucher_value,
_voucher_value_prehashed,
voucher_value_plain,
voucher_info,
_voucher_info_prehashed,
voucher_info_plain,
tx_hash,
signing_key,
encryption_key,
unused_ed25519: encryption_key,
pedersen_commitments_openings,
blind_sign_request,
}
@@ -87,7 +111,7 @@ impl BandwidthVoucher {
let voucher_info_plain_b = self.voucher_info_plain.as_bytes();
let tx_hash_b = self.tx_hash.as_bytes();
let signing_key_b = self.signing_key.to_bytes();
let encryption_key_b = self.encryption_key.to_bytes();
let encryption_key_b = self.unused_ed25519.to_bytes();
let blind_sign_request_b = self.blind_sign_request.to_bytes();
let mut ret = Vec::new();
@@ -171,13 +195,13 @@ impl BandwidthVoucher {
bytes[var_length_pointer..var_length_pointer + voucher_value_plain_no].to_vec(),
)
.or_else(utf_err)?;
let voucher_value = hash_to_scalar(&voucher_value_plain);
let _voucher_value_prehashed = hash_to_scalar(&voucher_value_plain);
var_length_pointer += voucher_value_plain_no;
let voucher_info_plain = String::from_utf8(
bytes[var_length_pointer..var_length_pointer + voucher_info_plain_no].to_vec(),
)
.or_else(utf_err)?;
let voucher_info = hash_to_scalar(&voucher_info_plain);
let _voucher_info_prehashed = hash_to_scalar(&voucher_info_plain);
var_length_pointer += voucher_info_plain_no;
let blind_sign_request = BlindSignRequest::from_bytes(
&bytes[var_length_pointer..var_length_pointer + blind_sign_request_no],
@@ -196,36 +220,43 @@ impl BandwidthVoucher {
Ok(Self {
serial_number,
binding_number,
voucher_value,
_voucher_value_prehashed,
voucher_value_plain,
voucher_info,
_voucher_info_prehashed,
voucher_info_plain,
tx_hash,
signing_key,
encryption_key,
unused_ed25519: encryption_key,
pedersen_commitments_openings,
blind_sign_request,
})
}
/// Check if the plain values correspond to the PublicAttributes
pub fn verify_against_plain(values: &[PublicAttribute], plain_values: &[String]) -> bool {
pub fn verify_against_plain(values: &[&PublicAttribute], plain_values: &[String]) -> bool {
values.len() == 2
&& plain_values.len() == 2
&& values[0] == hash_to_scalar(&plain_values[0])
&& values[1] == hash_to_scalar(&plain_values[1])
&& values[0] == &hash_to_scalar(&plain_values[0])
&& values[1] == &hash_to_scalar(&plain_values[1])
}
pub fn tx_hash(&self) -> &Hash {
&self.tx_hash
pub fn tx_hash(&self) -> Hash {
self.tx_hash
}
pub fn get_public_attributes(&self) -> Vec<PublicAttribute> {
vec![self.voucher_value, self.voucher_info]
pub fn get_public_attributes(&self) -> Vec<&PublicAttribute> {
vec![
&self._voucher_value_prehashed,
&self._voucher_info_prehashed,
]
}
pub fn identity_key(&self) -> &identity::PrivateKey {
&self.signing_key
}
pub fn encryption_key(&self) -> &encryption::PrivateKey {
&self.encryption_key
&self.unused_ed25519
}
pub fn pedersen_commitments_openings(&self) -> &Vec<Attribute> {
@@ -247,27 +278,32 @@ impl BandwidthVoucher {
]
}
pub fn get_private_attributes(&self) -> Vec<PrivateAttribute> {
vec![self.serial_number, self.binding_number]
pub fn get_private_attributes(&self) -> Vec<&PrivateAttribute> {
vec![&self.serial_number, &self.binding_number]
}
pub fn sign(&self, request: &BlindSignRequest) -> identity::Signature {
pub fn signable_plaintext(request: &BlindSignRequest, tx_hash: Hash) -> Vec<u8> {
let mut message = request.to_bytes();
message.extend_from_slice(self.tx_hash.to_string().as_bytes());
self.signing_key.sign(&message)
message.extend_from_slice(tx_hash.as_bytes());
message
}
pub fn sign(&self) -> identity::Signature {
let message = Self::signable_plaintext(&self.blind_sign_request, self.tx_hash);
self.signing_key.sign(message)
}
}
pub fn prepare_for_spending(
voucher_value: u64,
voucher_info: String,
serial_number: PrivateAttribute,
binding_number: PrivateAttribute,
serial_number: &PrivateAttribute,
binding_number: &PrivateAttribute,
epoch_id: u64,
signature: &Signature,
verification_key: &VerificationKey,
) -> Result<Credential, Error> {
let params = Parameters::new(TOTAL_ATTRIBUTES)?;
let params = Parameters::new(BandwidthVoucher::ENCODED_ATTRIBUTES)?;
prepare_credential_for_spending(
&params,
@@ -316,24 +352,30 @@ mod test {
let deserialized_voucher = BandwidthVoucher::try_from_bytes(&bytes).unwrap();
assert_eq!(voucher.serial_number, deserialized_voucher.serial_number);
assert_eq!(voucher.binding_number, deserialized_voucher.binding_number);
assert_eq!(voucher.voucher_value, deserialized_voucher.voucher_value);
assert_eq!(
voucher.voucher_value_plain,
deserialized_voucher.voucher_value_plain
);
assert_eq!(voucher.voucher_info, deserialized_voucher.voucher_info);
assert_eq!(
voucher.voucher_info_plain,
deserialized_voucher.voucher_info_plain
);
assert_eq!(
voucher._voucher_value_prehashed,
deserialized_voucher._voucher_value_prehashed
);
assert_eq!(
voucher._voucher_info_prehashed,
deserialized_voucher._voucher_info_prehashed
);
assert_eq!(voucher.tx_hash, deserialized_voucher.tx_hash);
assert_eq!(
voucher.signing_key.to_string(),
deserialized_voucher.signing_key.to_string()
);
assert_eq!(
voucher.encryption_key.to_string(),
deserialized_voucher.encryption_key.to_string()
voucher.unused_ed25519.to_string(),
deserialized_voucher.unused_ed25519.to_string()
);
assert_eq!(
voucher.pedersen_commitments_openings,
@@ -371,11 +413,11 @@ mod test {
]
));
assert!(!BandwidthVoucher::verify_against_plain(
&[voucher.get_public_attributes()[0], Attribute::one()],
&[voucher.get_public_attributes()[0], &Attribute::one()],
&voucher.get_public_attributes_plain()
));
assert!(!BandwidthVoucher::verify_against_plain(
&[Attribute::one(), voucher.get_public_attributes()[1]],
&[&Attribute::one(), voucher.get_public_attributes()[1]],
&voucher.get_public_attributes_plain()
));
assert!(BandwidthVoucher::verify_against_plain(
-1
View File
@@ -2,5 +2,4 @@
// SPDX-License-Identifier: Apache-2.0
pub mod bandwidth;
pub mod params;
pub mod utils;
-15
View File
@@ -1,15 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_crypto::aes::Aes128;
use nym_crypto::blake3;
use nym_crypto::ctr;
type Aes128Ctr = ctr::Ctr64LE<Aes128>;
/// Hashing algorithm used during hkdf for ephemeral shared key generation per blinded signature
/// response encryption.
pub type NymApiCredentialHkdfAlgorithm = blake3::Hasher;
/// Encryption algorithm used for end-to-end encryption of blinded signature response
pub type NymApiCredentialEncryptionAlgorithm = Aes128Ctr;
+22 -39
View File
@@ -1,18 +1,14 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::coconut::bandwidth::{BandwidthVoucher, PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES};
use crate::coconut::params::{NymApiCredentialEncryptionAlgorithm, NymApiCredentialHkdfAlgorithm};
use crate::coconut::bandwidth::BandwidthVoucher;
use crate::error::Error;
use log::{debug, warn};
use nym_api_requests::coconut::BlindSignRequestBody;
use nym_coconut_interface::{
aggregate_signature_shares, aggregate_verification_keys, prove_bandwidth_credential, Attribute,
BlindedSignature, Credential, Parameters, Signature, SignatureShare, VerificationKey,
Credential, Parameters, Signature, SignatureShare, VerificationKey,
};
use nym_crypto::asymmetric::encryption::PublicKey;
use nym_crypto::shared_key::recompute_shared_key;
use nym_crypto::symmetric::stream_cipher;
use nym_validator_client::client::CoconutApiClient;
pub async fn obtain_aggregate_verification_key(
@@ -36,47 +32,34 @@ pub async fn obtain_aggregate_verification_key(
async fn obtain_partial_credential(
params: &Parameters,
attributes: &BandwidthVoucher,
voucher: &BandwidthVoucher,
client: &nym_validator_client::client::NymApiClient,
validator_vk: &VerificationKey,
) -> Result<Signature, Error> {
let public_attributes = attributes.get_public_attributes();
let public_attributes_plain = attributes.get_public_attributes_plain();
let private_attributes = attributes.get_private_attributes();
let blind_sign_request = attributes.blind_sign_request();
let public_attributes_plain = voucher.get_public_attributes_plain();
let blind_sign_request = voucher.blind_sign_request();
let request_signature = voucher.sign();
let blind_sign_request_body = BlindSignRequestBody::new(
blind_sign_request,
attributes.tx_hash().to_string(),
attributes.sign(blind_sign_request).to_base58_string(),
&public_attributes,
blind_sign_request.clone(),
voucher.tx_hash(),
request_signature,
public_attributes_plain,
(public_attributes.len() + private_attributes.len()) as u32,
);
let response = client.blind_sign(&blind_sign_request_body).await?;
let encrypted_signature = response.encrypted_signature;
let remote_key = PublicKey::from_bytes(&response.remote_key)?;
let encryption_key = recompute_shared_key::<
NymApiCredentialEncryptionAlgorithm,
NymApiCredentialHkdfAlgorithm,
>(&remote_key, attributes.encryption_key());
let zero_iv = stream_cipher::zero_iv::<NymApiCredentialEncryptionAlgorithm>();
let blinded_signature_bytes = stream_cipher::decrypt::<NymApiCredentialEncryptionAlgorithm>(
&encryption_key,
&zero_iv,
&encrypted_signature,
);
let blinded_signature = response.blinded_signature;
let blinded_signature = BlindedSignature::from_bytes(&blinded_signature_bytes)?;
let public_attributes = voucher.get_public_attributes();
let private_attributes = voucher.get_private_attributes();
let unblinded_signature = blinded_signature.unblind(
let unblinded_signature = blinded_signature.unblind_and_verify(
params,
validator_vk,
&private_attributes,
&public_attributes,
&blind_sign_request.get_commitment_hash(),
attributes.pedersen_commitments_openings(),
voucher.pedersen_commitments_openings(),
)?;
Ok(unblinded_signature)
@@ -84,16 +67,13 @@ async fn obtain_partial_credential(
pub async fn obtain_aggregate_signature(
params: &Parameters,
attributes: &BandwidthVoucher,
voucher: &BandwidthVoucher,
coconut_api_clients: &[CoconutApiClient],
threshold: u64,
) -> Result<Signature, Error> {
if coconut_api_clients.is_empty() {
return Err(Error::NoValidatorsAvailable);
}
let public_attributes = attributes.get_public_attributes();
let private_attributes = attributes.get_private_attributes();
let mut shares = Vec::with_capacity(coconut_api_clients.len());
let validators_partial_vks: Vec<_> = coconut_api_clients
.iter()
@@ -114,7 +94,7 @@ pub async fn obtain_aggregate_signature(
match obtain_partial_credential(
params,
attributes,
voucher,
&coconut_api_client.api_client,
&coconut_api_client.verification_key,
)
@@ -136,6 +116,9 @@ pub async fn obtain_aggregate_signature(
return Err(Error::NotEnoughShares);
}
let public_attributes = voucher.get_public_attributes();
let private_attributes = voucher.get_private_attributes();
let mut attributes = Vec::with_capacity(private_attributes.len() + public_attributes.len());
attributes.extend_from_slice(&private_attributes);
attributes.extend_from_slice(&public_attributes);
@@ -150,8 +133,8 @@ pub fn prepare_credential_for_spending(
params: &Parameters,
voucher_value: u64,
voucher_info: String,
serial_number: Attribute,
binding_number: Attribute,
serial_number: &Attribute,
binding_number: &Attribute,
epoch_id: u64,
signature: &Signature,
verification_key: &VerificationKey,
@@ -165,7 +148,7 @@ pub fn prepare_credential_for_spending(
)?;
Ok(Credential::new(
PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES,
BandwidthVoucher::ENCODED_ATTRIBUTES,
theta,
voucher_value,
voucher_info,
@@ -201,6 +201,17 @@ impl<'a> From<&'a PrivateKey> for PublicKey {
}
impl PrivateKey {
#[cfg(feature = "rand")]
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let x25519_secret = x25519_dalek::StaticSecret::new(rng);
PrivateKey(x25519_secret)
}
pub fn public_key(&self) -> PublicKey {
self.into()
}
pub fn to_bytes(&self) -> [u8; PRIVATE_KEY_SIZE] {
self.0.to_bytes()
}
+23 -3
View File
@@ -5,13 +5,14 @@ pub use ed25519_dalek::ed25519::signature::Signature as SignatureTrait;
pub use ed25519_dalek::SignatureError;
pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH};
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
#[cfg(feature = "sphinx")]
use nym_sphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH};
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use thiserror::Error;
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(feature = "sphinx")]
use nym_sphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH};
#[cfg(feature = "rand")]
use rand::{CryptoRng, RngCore};
#[cfg(feature = "serde")]
@@ -224,6 +225,17 @@ impl<'a> From<&'a PrivateKey> for PublicKey {
}
impl PrivateKey {
#[cfg(feature = "rand")]
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let ed25519_secret = ed25519_dalek::SecretKey::generate(rng);
PrivateKey(ed25519_secret)
}
pub fn public_key(&self) -> PublicKey {
self.into()
}
pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] {
self.0.to_bytes()
}
@@ -295,7 +307,7 @@ impl PemStorableKey for PrivateKey {
}
}
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Signature(ed25519_dalek::Signature);
impl Signature {
@@ -319,6 +331,14 @@ impl Signature {
}
}
impl FromStr for Signature {
type Err = Ed25519RecoveryError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Signature::from_base58_string(s)
}
}
#[cfg(feature = "serde")]
impl Serialize for Signature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+4 -3
View File
@@ -3,6 +3,7 @@ name = "nym-dkg"
version = "0.1.0"
edition = "2021"
resolver = "2"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -11,7 +12,7 @@ bitvec = "1.0.0"
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="gt-serialisation", default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] }
bls12_381 = { workspace = true, default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] }
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common", optional = true }
bs58 = "0.4"
@@ -29,11 +30,11 @@ zeroize = { workspace = true, features = ["zeroize_derive"] }
nym-pemstore = { path = "../pemstore" }
[dependencies.group]
version = "0.11"
workspace = true
default-features = false
[dependencies.ff]
version = "0.11"
workspace = true
default-features = false
[dev-dependencies]
+1
View File
@@ -2,6 +2,7 @@
name = "nym-execute"
version = "0.1.0"
edition = "2021"
license.workspace = true
[lib]
proc-macro = true
+2 -1
View File
@@ -2,6 +2,7 @@
name = "ledger"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -10,4 +11,4 @@ bip32 = "0.5.1"
k256 = { workspace = true }
ledger-transport = "0.10.0"
ledger-transport-hid = "0.10.0"
thiserror = { workspace = true }
thiserror = { workspace = true }
+4 -5
View File
@@ -3,6 +3,7 @@ name = "nym-mixnode-common"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -12,8 +13,8 @@ futures = { workspace = true }
humantime-serde = "1.0"
log = { workspace = true }
rand = "0.8"
serde = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = [
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.24.1", features = [
"time",
"macros",
"rt",
@@ -23,10 +24,9 @@ tokio = { workspace = true, features = [
tokio-util = { workspace = true, features = ["codec"] }
url = { workspace = true }
thiserror = { workspace = true }
parking_lot = { workspace = true }
## tracing
tracing = { workspace = true, optional = true }
tracing = { version = "0.1.37", optional = true }
nym-crypto = { path = "../crypto" }
nym-network-defaults = { path = "../network-defaults" }
@@ -38,7 +38,6 @@ nym-sphinx-params = { path = "../nymsphinx/params" }
nym-sphinx-types = { path = "../nymsphinx/types" }
nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
nym-bin-common = { path = "../bin-common" }
cfg-if = "1.0.0"
@@ -1,27 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_validator_client::nyxd::error::NyxdError;
use std::net::IpAddr;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ForwardTravelError {
#[error("received a connection request from a forbidden address: '{address}'")]
DisallowedIngressAddress { address: IpAddr },
#[error("received a request to open connection to a forbidden address: '{address}'")]
DisallowedEgressAddress { address: IpAddr },
#[error("no valid nyxd urls are available for topology queries")]
NoNyxdUrlsAvailable,
#[error("nyxd interaction failure: {source}")]
NyxdFailure {
#[from]
source: NyxdError,
},
#[error("the current epoch appears to be stuck")]
StuckEpoch,
}
@@ -1,356 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::forward_travel::error::ForwardTravelError;
use log::{debug, error, info, trace, warn};
use nym_mixnet_contract_common::{EpochId, GatewayBond, Layer, MixNodeBond};
use nym_network_defaults::NymNetworkDetails;
use nym_task::TaskClient;
use nym_validator_client::client::IdentityKey;
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, PagedMixnetQueryClient};
use nym_validator_client::{nyxd, QueryHttpRpcNyxdClient};
use parking_lot::RwLock;
use rand::seq::SliceRandom;
use rand::{thread_rng, Rng};
use std::collections::HashSet;
use std::mem;
use std::net::{IpAddr, ToSocketAddrs};
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use tokio::time::sleep;
use url::Url;
pub mod error;
// TODO: to allow for separate ingress/egress we have to change our layer selection algorithm, i.e. it has to be announced before epoch change
pub type AllowedIngress = AllowedPaths;
pub type AllowedEgress = AllowedPaths;
pub struct AllowedAddressesProvider {
current_epoch: EpochId,
identity: IdentityKey,
client_config: nyxd::Config,
/// URLs to the nyxd validators for obtaining unfiltered network topology.
nyxd_endpoints: Vec<Url>,
// to allow for separate ingress/egress we have to change our layer selection algorithm, i.e. it has to be announced before epoch change
// ingress: AllowedIngress,
// egress: AllowedEgress,
allowed: AllowedPaths,
}
#[allow(dead_code)]
impl AllowedAddressesProvider {
pub async fn new(
identity: IdentityKey,
nyxd_endpoints: Vec<Url>,
allow_all: bool,
network_details: Option<NymNetworkDetails>,
) -> Result<Self, ForwardTravelError> {
let network = network_details.unwrap_or(NymNetworkDetails::new_mainnet());
let mut provider = AllowedAddressesProvider {
current_epoch: 0,
identity,
client_config: nyxd::Config::try_from_nym_network_details(&network)?,
nyxd_endpoints,
allowed: AllowedPaths::new(allow_all),
};
if !allow_all {
// set initial values for ingress/egress
let client = provider.ephemeral_nyxd_client()?;
provider.update_state(client).await?;
}
Ok(provider)
}
fn ephemeral_nyxd_client(&self) -> Result<QueryHttpRpcNyxdClient, ForwardTravelError> {
let mut possible_nyxd_endpoints = self.nyxd_endpoints.clone();
possible_nyxd_endpoints.shuffle(&mut thread_rng());
let mut last_error = match QueryHttpRpcNyxdClient::connect(
self.client_config.clone(),
possible_nyxd_endpoints
.pop()
.ok_or(ForwardTravelError::NoNyxdUrlsAvailable)?
.as_str(),
) {
Ok(client) => return Ok(client),
Err(err) => err,
};
for url in possible_nyxd_endpoints {
match QueryHttpRpcNyxdClient::connect(self.client_config.clone(), url.as_str()) {
Ok(client) => return Ok(client),
Err(err) => last_error = err,
};
}
Err(last_error.into())
}
pub fn ingress(&self) -> AllowedIngress {
self.allowed.clone()
}
pub fn egress(&self) -> AllowedEgress {
self.allowed.clone()
}
fn add_node_ips(raw_host: &str, identity: &str, set: &mut HashSet<IpAddr>) {
if let Ok(ip) = IpAddr::from_str(raw_host) {
set.insert(ip);
} else {
// this might still be a valid hostname
//
// annoyingly there exists a method of looking up a socket address but not an ip address,
// so append any port and perform the lookup
let Ok(sockets) = format!("{raw_host}:1789").to_socket_addrs() else {
warn!("failed to resolve ip address of node '{identity}' (hostname: '{raw_host}')");
return;
};
for socket in sockets {
set.insert(socket.ip());
}
}
}
fn get_all_addresses(nodes: &[MixNodeBond], gateways: &[GatewayBond]) -> HashSet<IpAddr> {
let mut allowed = HashSet::new();
for node in nodes.iter() {
Self::add_node_ips(&node.mix_node.host, node.identity(), &mut allowed);
}
for gateway in gateways.iter() {
Self::add_node_ips(&gateway.gateway.host, gateway.identity(), &mut allowed);
}
allowed
}
/// Gets ip addresses of all mixnodes on given layer
fn get_addresses_on_layer(layer: Layer, nodes: &[MixNodeBond]) -> HashSet<IpAddr> {
let mut allowed = HashSet::new();
for node in nodes.iter().filter(|m| m.layer == layer) {
Self::add_node_ips(&node.mix_node.host, node.identity(), &mut allowed);
}
allowed
}
fn gateway_addresses(gateways: &[GatewayBond]) -> HashSet<IpAddr> {
let mut allowed = HashSet::new();
for gateway in gateways.iter() {
Self::add_node_ips(&gateway.gateway.host, gateway.identity(), &mut allowed);
}
allowed
}
fn locate_layer(&self, nodes: &[MixNodeBond]) -> Option<Layer> {
nodes
.iter()
.find(|m| m.identity() == self.identity)
.map(|m| m.layer)
}
fn is_gateway(&self, gateways: &[GatewayBond]) -> bool {
gateways
.iter()
.any(|g| g.gateway.identity_key == self.identity)
}
async fn update_state(
&mut self,
client: QueryHttpRpcNyxdClient,
) -> Result<(), ForwardTravelError> {
let current_interval = client.get_current_interval_details().await?;
let current_epoch = current_interval.interval.current_epoch_absolute_id();
if current_epoch == self.current_epoch {
error!("can't update the allowed ips list as the epoch appears to be stuck");
return Err(ForwardTravelError::StuckEpoch);
}
let has_epoch_deviated = current_epoch > self.current_epoch + 1;
let mixnodes = client.get_all_mixnode_bonds().await?;
let gateways = client.get_all_gateways().await?;
let new_allowed = Self::get_all_addresses(&mixnodes, &gateways);
// I'm leaving this code commented out to preserve this logic for when we need it
// to update to proper ingress/egress filtering
// let our_mix_layer = self.locate_layer(&mixnodes);
//
// let previous_mix_layer = our_mix_layer.and_then(|l| l.try_previous());
// let next_mix_layer = our_mix_layer.and_then(|l| l.try_next());
//
// let (allowed_ingress, allowed_egress) = match (previous_mix_layer, next_mix_layer) {
// // layer 1
// (None, Some(next)) => {
// let gateways = client.get_all_gateways().await?;
//
// (
// Self::gateway_addresses(&gateways),
// Self::get_addresses_on_layer(next, &mixnodes),
// )
// }
// // layer 2
// (Some(previous), Some(next)) => (
// Self::get_addresses_on_layer(previous, &mixnodes),
// Self::get_addresses_on_layer(next, &mixnodes),
// ),
// // layer 3
// (Some(previous), None) => {
// let gateways = client.get_all_gateways().await?;
// (
// Self::get_addresses_on_layer(previous, &mixnodes),
// Self::gateway_addresses(&gateways),
// )
// }
// // gateway (or not bonded)
// (None, None) => {
// let gateways = client.get_all_gateways().await?;
//
// if self.is_gateway(&gateways) {
// let mut base_ingress = Self::get_addresses_on_layer(Layer::Three, &mixnodes);
// let mut base_egress = Self::get_addresses_on_layer(Layer::One, &mixnodes);
//
// // TODO: this extension should be conditional on whether the node is running the vpn module
// let gw_extension = Self::gateway_addresses(&gateways);
//
// base_ingress.extend(gw_extension.clone());
// base_egress.extend(gw_extension.clone());
//
// (base_ingress, base_egress)
// } else {
// warn!("our node doesn't appear to be bonded - going to permit traffic from ALL mixnodes and gateways");
// let all = Self::get_all_addresses(&mixnodes, &gateways);
// (all.clone(), all)
// }
// }
// };
self.current_epoch = current_epoch;
self.allowed.advance_epoch(new_allowed, has_epoch_deviated);
Ok(())
}
async fn update_allowed_addresses(&mut self) -> Result<(), ForwardTravelError> {
// create new client every epoch because it results in different nyxd endpoint being used
// what may help in distributing the load
let client = self.ephemeral_nyxd_client()?;
self.wait_for_epoch_rollover(&client).await?;
self.update_state(client).await
}
async fn wait_for_epoch_rollover(
&self,
client: &QueryHttpRpcNyxdClient,
) -> Result<(), ForwardTravelError> {
let current_interval = client.get_current_interval_details().await?;
let current_epoch = current_interval.interval.current_epoch_absolute_id();
if current_epoch <= self.current_epoch {
let remaining = current_interval.time_until_current_epoch_end();
// add few more seconds to account for block time drift and to spread queries of all
// other nodes
let adjustment_secs = rand::thread_rng().gen_range(5..90);
sleep(remaining + Duration::from_secs(adjustment_secs)).await;
}
Ok(())
}
pub async fn run(&mut self, mut task_client: TaskClient) {
if self.allowed.allow_all {
// debug_assert!(self.egress.allow_all);
info!("the forward travel is currently disabled - there's no point in starting the route refresher");
task_client.mark_as_success();
return;
}
debug!("Started ValidAddressesProvider with graceful shutdown support");
while !task_client.is_shutdown() {
tokio::select! {
biased;
_ = task_client.recv() => {
trace!("ValidAddressesProvider: Received shutdown");
}
res = self.update_allowed_addresses() => {
if let Err(err) = res {
warn!("failed to update the allowed addresses: {err}");
// don't retry immediately in case it was a network failure, wait a bit instead.
task_client.wait(Duration::from_secs(5 * 60)).await
}
}
}
}
task_client.recv_timeout().await;
log::debug!("ValidAddressesProvider: Exiting");
}
}
#[derive(Clone)]
pub struct AllowedPaths {
// this is fine that this value is not wrapped in an Arc and is not atomic given
// it's not expected to be modified at runtime
allow_all: bool,
inner: Arc<RwLock<AllowedPathsInner>>,
}
impl AllowedPaths {
fn new(allow_all: bool) -> Self {
AllowedPaths {
allow_all,
inner: Arc::new(RwLock::new(AllowedPathsInner {
previous_epoch: HashSet::new(),
current_epoch: HashSet::new(),
})),
}
}
pub fn is_allowed(&self, address: IpAddr) -> bool {
if self.allow_all {
return true;
}
let guard = self.inner.read();
guard.current_epoch.contains(&address) || guard.previous_epoch.contains(&address)
}
fn advance_epoch(&self, current_epoch: HashSet<IpAddr>, reset_previous: bool) {
// if this is triggered, it's an implementation bug;
// we shouldn't be updating data if we're allowing everything regardless
debug_assert!(!self.allow_all);
let mut guard = self.inner.write();
let old_current = mem::replace(&mut guard.current_epoch, current_epoch);
if reset_previous {
guard.previous_epoch = HashSet::new()
} else {
guard.previous_epoch = old_current;
}
}
}
struct AllowedPathsInner {
previous_epoch: HashSet<IpAddr>,
current_epoch: HashSet<IpAddr>,
}
+1 -3
View File
@@ -1,7 +1,5 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod forward_travel;
pub mod packet_processor;
pub mod verloc;
+58 -27
View File
@@ -1,50 +1,81 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::{self, Display, Formatter};
use std::io;
use thiserror::Error;
#[derive(Debug, Error)]
#[derive(Debug)]
pub enum RttError {
#[error("the received echo packet had unexpected size")]
UnexpectedEchoPacketSize,
#[error("the received reply packet had unexpected size")]
UnexpectedReplyPacketSize,
#[error("the received echo packet had malformed sender")]
MalformedSenderIdentity,
#[error("the received echo packet had malformed signature")]
MalformedEchoSignature,
#[error("the received reply packet had malformed signature")]
MalformedReplySignature,
#[error("the received echo packet had invalid signature")]
InvalidEchoSignature,
#[error("the received reply packet had invalid signature")]
InvalidReplySignature,
#[error("could not establish connection to {0}: {1}")]
UnreachableNode(String, #[source] io::Error),
#[error("failed to write echo packet to {0}: {1}")]
UnexpectedConnectionFailureWrite(String, #[source] io::Error),
#[error("failed to read reply packet from {0}: {1}")]
UnexpectedConnectionFailureRead(String, #[source] io::Error),
#[error("timed out while trying to read reply packet from {0}")]
UnreachableNode(String, io::Error),
UnexpectedConnectionFailureWrite(String, io::Error),
UnexpectedConnectionFailureRead(String, io::Error),
ConnectionReadTimeout(String),
#[error("timed out while trying to write echo packet to {0}")]
ConnectionWriteTimeout(String),
#[error("the received reply packet had an unexpected sequence number")]
UnexpectedReplySequence,
#[error("shutdown signal received")]
ShutdownReceived,
}
impl Display for RttError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
RttError::UnexpectedEchoPacketSize => {
write!(f, "The received echo packet had unexpected size")
}
RttError::UnexpectedReplyPacketSize => {
write!(f, "The received reply packet had unexpected size")
}
RttError::MalformedSenderIdentity => {
write!(f, "The received echo packet had malformed sender")
}
RttError::MalformedEchoSignature => {
write!(f, "The received echo packet had malformed signature")
}
RttError::MalformedReplySignature => {
write!(f, "The received reply packet had malformed signature")
}
RttError::InvalidEchoSignature => {
write!(f, "The received echo packet had invalid signature")
}
RttError::InvalidReplySignature => {
write!(f, "The received reply packet had invalid signature")
}
RttError::UnreachableNode(id, err) => {
write!(f, "Could not establish connection to {id} - {err}")
}
RttError::UnexpectedConnectionFailureWrite(id, err) => {
write!(f, "Failed to write echo packet to {id} - {err}")
}
RttError::UnexpectedConnectionFailureRead(id, err) => {
write!(f, "Failed to read reply packet from {id} - {err}")
}
RttError::ConnectionReadTimeout(id) => {
write!(f, "Timed out while trying to read reply packet from {id}")
}
RttError::ConnectionWriteTimeout(id) => {
write!(f, "Timed out while trying to write echo packet to {id}")
}
RttError::UnexpectedReplySequence => write!(
f,
"The received reply packet had an unexpected sequence number"
),
RttError::ShutdownReceived => {
write!(f, "Shutdown signal received")
}
}
}
}
impl std::error::Error for RttError {}
+3 -3
View File
@@ -108,13 +108,13 @@ impl ConnectionHandler {
let reply_packet = match maybe_echo_packet {
Some(Ok(echo_packet)) => self.handle_echo_packet(echo_packet),
Some(Err(err)) => {
error!(
debug!(
"The socket connection got corrupted with error: {err}. Closing the socket",
);
return;
}
None => {
error!("The socket connection got terminated by the remote!");
debug!("The socket connection got terminated by the remote!");
return;
}
};
@@ -125,7 +125,7 @@ impl ConnectionHandler {
.write_all(reply_packet.to_bytes().as_ref())
.await
{
error!(
debug!(
"Failed to write reply packet back to the sender - {}. Closing the socket on our end",
err
);
+1
View File
@@ -2,6 +2,7 @@
name = "nym-node-tester-utils"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+3 -2
View File
@@ -3,17 +3,18 @@ name = "nym-nonexhaustive-delayqueue"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio-stream = { workspace = true } # this one seems to be a thing until `Stream` trait is stabilised in stdlib
tokio-stream = "0.1.11" # this one seems to be a thing until `Stream` trait is stabilised in stdlib
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
workspace = true
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-util]
workspace = true
version = "0.7.4"
features = ["time"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
+7 -3
View File
@@ -3,11 +3,12 @@ name = "nym-coconut"
version = "0.5.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>", "Ania Piotrowska <ania@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="gt-serialisation", default-features = false, features = ["pairings", "alloc", "experimental"] }
bls12_381 = { workspace = true, default-features = false, features = ["pairings", "alloc", "experimental"] }
itertools = "0.10"
digest = "0.9"
rand = "0.8"
@@ -16,16 +17,17 @@ serde = { workspace = true }
serde_derive = "1.0"
bs58 = "0.4.0"
sha2 = "0.9"
zeroize = { workspace = true, optional = true }
nym-dkg = { path = "../dkg" }
nym-pemstore = { path = "../pemstore" }
[dependencies.ff]
version = "0.11"
workspace = true
default-features = false
[dependencies.group]
version = "0.11"
workspace = true
default-features = false
[dev-dependencies]
@@ -38,7 +40,9 @@ name = "benchmarks"
harness = false
[features]
key-zeroize = ["zeroize", "bls12_381/zeroize"]
default = []
[target.'cfg(target_env = "wasm32-unknown-unknown")'.dependencies]
getrandom = { version="0.2", features=["js"] }
+13 -13
View File
@@ -7,7 +7,7 @@ use ff::Field;
use group::{Curve, Group};
use nym_coconut::{
aggregate_signature_shares, aggregate_verification_keys, blind_sign, prepare_blind_sign,
prove_bandwidth_credential, setup, ttp_keygen, verify_credential,
prove_bandwidth_credential, random_scalars_refs, setup, ttp_keygen, verify_credential,
verify_partial_blind_signature, Attribute, BlindedSignature, Parameters, Signature,
SignatureShare, VerificationKey,
};
@@ -66,8 +66,8 @@ fn unblind_and_aggregate(
params: &Parameters,
blinded_signatures: &[BlindedSignature],
partial_verification_keys: &[VerificationKey],
private_attributes: &[Attribute],
public_attributes: &[Attribute],
private_attributes: &[&Attribute],
public_attributes: &[&Attribute],
commitment_hash: &G1Projective,
pedersen_commitments_openings: &[Scalar],
verification_key: &VerificationKey,
@@ -78,7 +78,7 @@ fn unblind_and_aggregate(
.zip(partial_verification_keys.iter())
.map(|(signature, partial_verification_key)| {
signature
.unblind(
.unblind_and_verify(
params,
partial_verification_key,
private_attributes,
@@ -171,10 +171,10 @@ fn bench_coconut(c: &mut Criterion) {
let params = setup(case.num_public_attrs + case.num_private_attrs).unwrap();
let public_attributes = params.n_random_scalars(case.num_public_attrs as usize);
random_scalars_refs!(public_attributes, params, case.num_public_attrs as usize);
let serial_number = params.random_scalar();
let binding_number = params.random_scalar();
let private_attributes = vec![serial_number, binding_number];
let private_attributes = vec![&serial_number, &binding_number];
// The prepare blind sign is performed by the user
let (pedersen_commitments_openings, blind_sign_request) =
@@ -213,7 +213,7 @@ fn bench_coconut(c: &mut Criterion) {
b.iter(|| {
blind_sign(
&params,
&keypair.secret_key(),
keypair.secret_key(),
&blind_sign_request,
&public_attributes,
)
@@ -228,7 +228,7 @@ fn bench_coconut(c: &mut Criterion) {
for keypair in coconut_keypairs.iter() {
let blinded_signature = blind_sign(
&params,
&keypair.secret_key(),
keypair.secret_key(),
&blind_sign_request,
&public_attributes,
)
@@ -238,7 +238,7 @@ fn bench_coconut(c: &mut Criterion) {
let verification_keys: Vec<VerificationKey> = coconut_keypairs
.iter()
.map(|keypair| keypair.verification_key())
.map(|keypair| keypair.verification_key().clone())
.collect();
// verify a random partial blind signature
@@ -310,8 +310,8 @@ fn bench_coconut(c: &mut Criterion) {
&params,
&aggr_verification_key,
&aggregated_signature,
serial_number,
binding_number,
&serial_number,
&binding_number,
)
.unwrap();
@@ -329,8 +329,8 @@ fn bench_coconut(c: &mut Criterion) {
&params,
&aggr_verification_key,
&aggregated_signature,
serial_number,
binding_number,
&serial_number,
&binding_number,
)
.unwrap()
})
+34 -13
View File
@@ -34,7 +34,10 @@ impl TryFrom<&[u8]> for Ciphertext {
)));
}
// safety: we just checked for the length so the unwraps are fine
#[allow(clippy::unwrap_used)]
let c1_bytes: &[u8; 48] = &bytes[..48].try_into().unwrap();
#[allow(clippy::unwrap_used)]
let c2_bytes: &[u8; 48] = &bytes[48..].try_into().unwrap();
let c1 = try_deserialize_g1_projective(
@@ -112,7 +115,16 @@ impl Bytable for PrivateKey {
}
fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
PrivateKey::from_bytes(slice.try_into().unwrap())
let received = slice.len();
let Ok(arr) = slice.try_into() else {
return Err(CoconutError::UnexpectedArrayLength {
typ: "elgamal::PrivateKey".to_string(),
received,
expected: 32,
});
};
PrivateKey::from_bytes(arr)
}
}
@@ -145,21 +157,36 @@ impl PublicKey {
}
pub fn to_bytes(&self) -> [u8; 48] {
self.to_byte_vec().try_into().unwrap()
self.0.to_affine().to_compressed()
}
pub fn from_bytes(bytes: &[u8; 48]) -> Result<PublicKey> {
Ok(PublicKey::try_from(bytes.to_vec().as_slice()).unwrap())
try_deserialize_g1_projective(
bytes,
CoconutError::Deserialization(
"Failed to deserialize compressed ElGamal public key".to_string(),
),
)
.map(PublicKey)
}
}
impl Bytable for PublicKey {
fn to_byte_vec(&self) -> Vec<u8> {
self.0.to_affine().to_compressed().into()
self.to_bytes().into()
}
fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
Ok(PublicKey::from_bytes(slice.try_into().unwrap()).unwrap())
let received = slice.len();
let Ok(arr) = slice.try_into() else {
return Err(CoconutError::UnexpectedArrayLength {
typ: "elgamal::PublicKey".to_string(),
received,
expected: 48,
});
};
PublicKey::from_bytes(arr)
}
}
@@ -167,13 +194,7 @@ impl TryFrom<&[u8]> for PublicKey {
type Error = CoconutError;
fn try_from(slice: &[u8]) -> Result<PublicKey> {
try_deserialize_g1_projective(
slice.try_into().unwrap(),
CoconutError::Deserialization(
"Failed to deserialize compressed ElGamal public key".to_string(),
),
)
.map(PublicKey)
PublicKey::try_from_byte_slice(slice)
}
}
@@ -225,7 +246,7 @@ pub fn elgamal_keygen(params: &Parameters) -> ElGamalKeyPair {
pub fn compute_attribute_encryption(
params: &Parameters,
private_attributes: &[Attribute],
private_attributes: &[&Attribute],
pub_key: &PublicKey,
commitment_hash: G1Projective,
) -> (Vec<Ciphertext>, Vec<EphemeralKey>) {
+16
View File
@@ -50,4 +50,20 @@ pub enum CoconutError {
modulus: usize,
object: String,
},
#[error("received an array of unexpected size for deserialization of {typ}. got {received} but expected {expected}")]
UnexpectedArrayLength {
typ: String,
received: usize,
expected: usize,
},
#[error("failed to decode the base58 representation: {0}")]
Base58DecodingFailure(#[from] bs58::decode::Error),
#[error("failed to deserialize scalar from the received bytes - it might not have been canonically encoded")]
ScalarDeserializationFailure,
#[error("failed to deserialize G1Projective point from the received bytes - it might not have been canonically encoded")]
G1ProjectiveDeserializationFailure,
}
+4 -17
View File
@@ -1,11 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::convert::TryInto;
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
use bls12_381::Scalar;
pub use crate::traits::Bytable;
pub use elgamal::elgamal_keygen;
pub use elgamal::ElGamalKeyPair;
pub use elgamal::PublicKey;
@@ -30,6 +28,7 @@ pub use scheme::BlindedSignature;
pub use scheme::Signature;
pub use scheme::SignatureShare;
pub use traits::Base58;
pub use traits::Bytable;
pub use utils::hash_to_scalar;
pub mod elgamal;
@@ -41,18 +40,6 @@ pub mod tests;
mod traits;
mod utils;
pub type Attribute = Scalar;
pub type Attribute = bls12_381::Scalar;
pub type PrivateAttribute = Attribute;
pub type PublicAttribute = Attribute;
impl Bytable for Attribute {
fn to_byte_vec(&self) -> Vec<u8> {
self.to_bytes().to_vec()
}
fn try_from_byte_slice(slice: &[u8]) -> Result<Self, CoconutError> {
Ok(Attribute::from_bytes(slice.try_into().unwrap()).unwrap())
}
}
impl Base58 for Attribute {}
+38 -26
View File
@@ -91,8 +91,8 @@ impl ProofCmCs {
commitment_opening: &Scalar,
commitments: &[G1Projective],
pedersen_commitments_openings: &[Scalar],
private_attributes: &[Attribute],
public_attributes: &[Attribute],
private_attributes: &[&Attribute],
public_attributes: &[&Attribute],
) -> Self {
// note: this is only called from `prepare_blind_sign` that already checks
// whether private attributes are non-empty and whether we don't have too many
@@ -162,11 +162,8 @@ impl ProofCmCs {
&challenge,
&pedersen_commitments_openings.iter().collect::<Vec<_>>(),
);
let response_attributes = produce_responses(
&witness_attributes,
&challenge,
&private_attributes.iter().collect::<Vec<_>>(),
);
let response_attributes =
produce_responses(&witness_attributes, &challenge, private_attributes);
ProofCmCs {
challenge,
@@ -181,7 +178,7 @@ impl ProofCmCs {
params: &Parameters,
commitment: &G1Projective,
commitments: &[G1Projective],
public_attributes: &[Attribute],
public_attributes: &[&Attribute],
) -> bool {
if self.response_attributes.len() != commitments.len() {
return false;
@@ -203,7 +200,7 @@ impl ProofCmCs {
- public_attributes
.iter()
.zip(params.gen_hs().iter().skip(self.response_attributes.len()))
.map(|(pub_attr, hs)| hs * pub_attr)
.map(|(&pub_attr, hs)| hs * pub_attr)
.sum::<G1Projective>())
* self.challenge
+ g1 * self.response_opening
@@ -280,8 +277,12 @@ impl ProofCmCs {
}
let mut idx = 0;
// safety: bound checked + constant offset
#[allow(clippy::unwrap_used)]
let challenge_bytes = bytes[idx..idx + 32].try_into().unwrap();
idx += 32;
// safety: bound checked + constant offset
#[allow(clippy::unwrap_used)]
let response_opening_bytes = bytes[idx..idx + 32].try_into().unwrap();
idx += 32;
@@ -297,6 +298,8 @@ impl ProofCmCs {
),
)?;
// safety: bound checked + constant offset
#[allow(clippy::unwrap_used)]
let ro_len = u64::from_le_bytes(bytes[idx..idx + 8].try_into().unwrap());
idx += 8;
if bytes[idx..].len() < ro_len as usize * 32 + 8 {
@@ -313,6 +316,8 @@ impl ProofCmCs {
CoconutError::Deserialization("Failed to deserialize openings response".to_string()),
)?;
// safety: bound checked + constant offset
#[allow(clippy::unwrap_used)]
let rm_len = u64::from_le_bytes(bytes[ro_end..ro_end + 8].try_into().unwrap());
let response_attributes = try_deserialize_scalar_vec(
rm_len,
@@ -462,7 +467,7 @@ impl ProofKappaZeta {
pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self> {
// at the very minimum there must be a single attribute being proven
if bytes.len() < 32 * 4 || (bytes.len()) % 32 != 0 {
if bytes.len() != 128 {
return Err(CoconutError::DeserializationInvalidLength {
actual: bytes.len(),
modulus_target: bytes.len(),
@@ -472,24 +477,32 @@ impl ProofKappaZeta {
});
}
// safety: bound checked + constant offset
#[allow(clippy::unwrap_used)]
let challenge_bytes = bytes[..32].try_into().unwrap();
let challenge = try_deserialize_scalar(
&challenge_bytes,
CoconutError::Deserialization("Failed to deserialize challenge".to_string()),
)?;
// safety: bound checked + constant offset
#[allow(clippy::unwrap_used)]
let serial_number_bytes = &bytes[32..64].try_into().unwrap();
let response_serial_number = try_deserialize_scalar(
serial_number_bytes,
CoconutError::Deserialization("failed to deserialize the serial number".to_string()),
)?;
// safety: bound checked + constant offset
#[allow(clippy::unwrap_used)]
let binding_number_bytes = &bytes[64..96].try_into().unwrap();
let response_binding_number = try_deserialize_scalar(
binding_number_bytes,
CoconutError::Deserialization("failed to deserialize the binding number".to_string()),
)?;
// safety: bound checked + constant offset
#[allow(clippy::unwrap_used)]
let blinder_bytes = bytes[96..].try_into().unwrap();
let response_blinder = try_deserialize_scalar(
&blinder_bytes,
@@ -512,14 +525,13 @@ impl ProofKappaZeta {
#[cfg(test)]
mod tests {
use group::Group;
use rand::thread_rng;
use super::*;
use crate::scheme::keygen::keygen;
use crate::scheme::setup::setup;
use crate::scheme::verification::{compute_kappa, compute_zeta};
use super::*;
use crate::tests::helpers::random_scalars_refs;
use group::Group;
use rand::thread_rng;
#[test]
fn proof_cm_cs_bytes_roundtrip() {
@@ -530,7 +542,7 @@ mod tests {
let r = params.random_scalar();
let cms: [G1Projective; 1] = [G1Projective::random(&mut rng)];
let rs = params.n_random_scalars(1);
let private_attributes = params.n_random_scalars(1);
random_scalars_refs!(private_attributes, params, 1);
// 0 public 1 private
let pi_s = ProofCmCs::construct(&params, &cm, &r, &cms, &rs, &private_attributes, &[]);
@@ -546,7 +558,7 @@ mod tests {
G1Projective::random(&mut rng),
];
let rs = params.n_random_scalars(2);
let private_attributes = params.n_random_scalars(2);
random_scalars_refs!(private_attributes, params, 2);
// 0 public 2 privates
let pi_s = ProofCmCs::construct(&params, &cm, &r, &cms, &rs, &private_attributes, &[]);
@@ -562,20 +574,20 @@ mod tests {
let keypair = keygen(&params);
// we don't care about 'correctness' of the proof. only whether we can correctly recover it from bytes
let serial_number = params.random_scalar();
let binding_number = params.random_scalar();
let serial_number = &params.random_scalar();
let binding_number = &params.random_scalar();
let private_attributes = vec![serial_number, binding_number];
let r = params.random_scalar();
let kappa = compute_kappa(&params, &keypair.verification_key(), &private_attributes, r);
let kappa = compute_kappa(&params, keypair.verification_key(), &private_attributes, r);
let zeta = compute_zeta(&params, serial_number);
// 0 public 2 private
let pi_v = ProofKappaZeta::construct(
&params,
&keypair.verification_key(),
&serial_number,
&binding_number,
keypair.verification_key(),
serial_number,
binding_number,
&r,
&kappa,
&zeta,
@@ -592,9 +604,9 @@ mod tests {
let pi_v = ProofKappaZeta::construct(
&params,
&keypair.verification_key(),
&serial_number,
&binding_number,
keypair.verification_key(),
serial_number,
binding_number,
&r,
&kappa,
&zeta,
+35 -15
View File
@@ -83,7 +83,7 @@ pub fn aggregate_verification_keys(
pub fn aggregate_signatures(
params: &Parameters,
verification_key: &VerificationKey,
attributes: &[Attribute],
attributes: &[&Attribute],
signatures: &[PartialSignature],
indices: Option<&[SignerIndex]>,
) -> Result<Signature> {
@@ -100,7 +100,7 @@ pub fn aggregate_signatures(
let tmp = attributes
.iter()
.zip(verification_key.beta_g2.iter())
.map(|(attr, beta_i)| beta_i * attr)
.map(|(&attr, beta_i)| beta_i * attr)
.sum::<G2Projective>();
if !check_bilinear_pairing(
@@ -119,7 +119,7 @@ pub fn aggregate_signatures(
pub fn aggregate_signature_shares(
params: &Parameters,
verification_key: &VerificationKey,
attributes: &[Attribute],
attributes: &[&Attribute],
shares: &[SignatureShare],
) -> Result<Signature> {
let (signatures, indices): (Vec<_>, Vec<_>) = shares
@@ -138,13 +138,13 @@ pub fn aggregate_signature_shares(
#[cfg(test)]
mod tests {
use bls12_381::G1Projective;
use group::Group;
use crate::scheme::issuance::sign;
use crate::scheme::keygen::ttp_keygen;
use crate::scheme::setup::Parameters;
use crate::scheme::verification::verify;
use crate::tests::helpers::random_scalars_refs;
use bls12_381::G1Projective;
use group::Group;
use super::*;
@@ -155,7 +155,7 @@ mod tests {
let vks = keypairs
.into_iter()
.map(|keypair| keypair.verification_key())
.map(|keypair| keypair.verification_key().clone())
.collect::<Vec<_>>();
let aggr_vk1 = aggregate_verification_keys(&vks[..3], Some(&[1, 2, 3])).unwrap();
@@ -212,13 +212,18 @@ mod tests {
#[test]
fn signature_aggregation_works_for_any_subset_of_signatures() {
let mut params = Parameters::new(2).unwrap();
let attributes = params.n_random_scalars(2);
random_scalars_refs!(attributes, params, 2);
let keypairs = ttp_keygen(&params, 3, 5).unwrap();
let (sks, vks): (Vec<_>, Vec<_>) = keypairs
.into_iter()
.map(|keypair| (keypair.secret_key(), keypair.verification_key()))
.map(|keypair| {
(
keypair.secret_key().clone(),
keypair.verification_key().clone(),
)
})
.unzip();
let sigs = sks
@@ -312,12 +317,17 @@ mod tests {
fn signature_aggregation_doesnt_work_for_empty_set_of_signatures() {
let signatures: Vec<Signature> = vec![];
let params = Parameters::new(2).unwrap();
let attributes = params.n_random_scalars(2);
random_scalars_refs!(attributes, params, 2);
let keypairs = ttp_keygen(&params, 3, 5).unwrap();
let (_, vks): (Vec<_>, Vec<_>) = keypairs
.into_iter()
.map(|keypair| (keypair.secret_key(), keypair.verification_key()))
.map(|keypair| {
(
keypair.secret_key().clone(),
keypair.verification_key().clone(),
)
})
.unzip();
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
@@ -330,11 +340,16 @@ mod tests {
fn signature_aggregation_doesnt_work_if_indices_have_invalid_length() {
let signatures = vec![random_signature()];
let params = Parameters::new(2).unwrap();
let attributes = params.n_random_scalars(2);
random_scalars_refs!(attributes, params, 2);
let keypairs = ttp_keygen(&params, 3, 5).unwrap();
let (_, vks): (Vec<_>, Vec<_>) = keypairs
.into_iter()
.map(|keypair| (keypair.secret_key(), keypair.verification_key()))
.map(|keypair| {
(
keypair.secret_key().clone(),
keypair.verification_key().clone(),
)
})
.unzip();
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
@@ -356,11 +371,16 @@ mod tests {
fn signature_aggregation_doesnt_work_for_non_unique_indices() {
let signatures = vec![random_signature(), random_signature()];
let params = Parameters::new(2).unwrap();
let attributes = params.n_random_scalars(2);
random_scalars_refs!(attributes, params, 2);
let keypairs = ttp_keygen(&params, 3, 5).unwrap();
let (_, vks): (Vec<_>, Vec<_>) = keypairs
.into_iter()
.map(|keypair| (keypair.secret_key(), keypair.verification_key()))
.map(|keypair| {
(
keypair.secret_key().clone(),
keypair.verification_key().clone(),
)
})
.unzip();
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
@@ -25,6 +25,8 @@ impl TryFrom<&[u8]> for BlindedSerialNumber {
));
}
// safety: we've just made a check for 96 bytes
#[allow(clippy::unwrap_used)]
let inner = try_deserialize_g2_projective(
&bytes.try_into().unwrap(),
CoconutError::Deserialization(
+51 -33
View File
@@ -54,6 +54,8 @@ impl TryFrom<&[u8]> for BlindSignRequest {
let commitment_bytes_len = 48;
let commitment_hash_bytes_len = 48;
// safety: we made bound check and we're using constant offest
#[allow(clippy::unwrap_used)]
let cm_bytes = bytes[..j + commitment_bytes_len].try_into().unwrap();
let commitment = try_deserialize_g1_projective(
&cm_bytes,
@@ -63,6 +65,8 @@ impl TryFrom<&[u8]> for BlindSignRequest {
)?;
j += commitment_bytes_len;
// safety: we made bound check and we're using constant offest
#[allow(clippy::unwrap_used)]
let cm_hash_bytes = bytes[j..j + commitment_hash_bytes_len].try_into().unwrap();
let commitment_hash = try_deserialize_g1_projective(
&cm_hash_bytes,
@@ -72,6 +76,8 @@ impl TryFrom<&[u8]> for BlindSignRequest {
)?;
j += commitment_hash_bytes_len;
// safety: we made bound check and we're using constant offest
#[allow(clippy::unwrap_used)]
let c_len = u64::from_le_bytes(bytes[j..j + 8].try_into().unwrap());
j += 8;
if bytes[j..].len() < c_len as usize * 48 {
@@ -86,6 +92,14 @@ impl TryFrom<&[u8]> for BlindSignRequest {
let start = j + i * 48;
let end = start + 48;
if bytes.len() < end {
return Err(CoconutError::Deserialization(
"Failed to deserialize compressed commitment".to_string(),
));
}
// safety: we made bound check and we're using constant offest
#[allow(clippy::unwrap_used)]
let private_attributes_commitment_bytes = bytes[start..end].try_into().unwrap();
let private_attributes_commitment = try_deserialize_g1_projective(
&private_attributes_commitment_bytes,
@@ -137,7 +151,7 @@ impl Bytable for BlindSignRequest {
impl Base58 for BlindSignRequest {}
impl BlindSignRequest {
fn verify_proof(&self, params: &Parameters, public_attributes: &[Attribute]) -> bool {
fn verify_proof(&self, params: &Parameters, public_attributes: &[&Attribute]) -> bool {
self.pi_s.verify(
params,
&self.commitment,
@@ -150,8 +164,8 @@ impl BlindSignRequest {
self.commitment_hash
}
pub fn get_private_attributes_pedersen_commitments(&self) -> Vec<G1Projective> {
self.private_attributes_commitments.clone()
pub fn get_private_attributes_pedersen_commitments(&self) -> &[G1Projective] {
&self.private_attributes_commitments
}
pub fn to_bytes(&self) -> Vec<u8> {
@@ -161,12 +175,16 @@ impl BlindSignRequest {
pub fn from_bytes(bytes: &[u8]) -> Result<BlindSignRequest> {
BlindSignRequest::try_from(bytes)
}
pub fn num_private_attributes(&self) -> usize {
self.private_attributes_commitments.len()
}
}
pub fn compute_attributes_commitment(
params: &Parameters,
private_attributes: &[Attribute],
public_attributes: &[Attribute],
private_attributes: &[&Attribute],
public_attributes: &[&Attribute],
hs: &[G1Affine],
) -> (Scalar, G1Projective) {
let commitment_opening = params.random_scalar();
@@ -187,7 +205,7 @@ pub fn compute_attributes_commitment(
pub fn compute_pedersen_commitments_for_private_attributes(
params: &Parameters,
private_attributes: &[Attribute],
private_attributes: &[&Attribute],
h: &G1Projective,
) -> (Vec<Scalar>, Vec<G1Projective>) {
// Generate openings for Pedersen commitment for each private attribute
@@ -197,13 +215,13 @@ pub fn compute_pedersen_commitments_for_private_attributes(
let pedersen_commitments = commitments_openings
.iter()
.zip(private_attributes.iter())
.map(|(o_j, m_j)| params.gen1() * o_j + h * m_j)
.map(|(o_j, &m_j)| params.gen1() * o_j + h * m_j)
.collect::<Vec<_>>();
(commitments_openings, pedersen_commitments)
}
pub fn compute_hash(commitment: G1Projective, public_attributes: &[Attribute]) -> G1Projective {
pub fn compute_hash(commitment: G1Projective, public_attributes: &[&Attribute]) -> G1Projective {
let mut buff = Vec::new();
buff.extend_from_slice(commitment.to_bytes().as_ref());
for attr in public_attributes {
@@ -215,8 +233,8 @@ pub fn compute_hash(commitment: G1Projective, public_attributes: &[Attribute]) -
/// Builds cryptographic material required for blind sign.
pub fn prepare_blind_sign(
params: &Parameters,
private_attributes: &[Attribute],
public_attributes: &[Attribute],
private_attributes: &[&Attribute],
public_attributes: &[&Attribute],
) -> Result<(Vec<Scalar>, BlindSignRequest)> {
if private_attributes.is_empty() {
return Err(CoconutError::Issuance(
@@ -271,7 +289,7 @@ pub fn blind_sign(
params: &Parameters,
signing_secret_key: &SecretKey,
blind_sign_request: &BlindSignRequest,
public_attributes: &[Attribute],
public_attributes: &[&Attribute],
) -> Result<BlindedSignature> {
let num_private = blind_sign_request.private_attributes_commitments.len();
let hs = params.gen_hs();
@@ -304,7 +322,7 @@ pub fn blind_sign(
let signed_public = h * public_attributes
.iter()
.zip(signing_secret_key.ys.iter().skip(num_private))
.map(|(attr, yi)| attr * yi)
.map(|(&attr, yi)| attr * yi)
.sum::<Scalar>();
// h ^ x + c[0] ^ y[0] + ... c[m] ^ y[m] + h ^ (pub_m[0] * y[m + 1] + ... + pub_m[n] * y[m + n])
@@ -345,7 +363,7 @@ pub fn blind_sign(
pub fn verify_partial_blind_signature(
params: &Parameters,
blind_sign_request: &BlindSignRequest,
public_attributes: &[Attribute],
public_attributes: &[&Attribute],
blind_sig: &BlindedSignature,
partial_verification_key: &VerificationKey,
) -> bool {
@@ -383,7 +401,7 @@ pub fn verify_partial_blind_signature(
}
// for each public attribute, add (s^pub_j, beta_{priv + j}) to the miller terms
for (pub_attr, beta_g2) in public_attributes.iter().zip(
for (&pub_attr, beta_g2) in public_attributes.iter().zip(
partial_verification_key
.beta_g2
.iter()
@@ -415,7 +433,7 @@ pub fn verify_partial_blind_signature(
pub fn sign(
params: &mut Parameters,
secret_key: &SecretKey,
public_attributes: &[Attribute],
public_attributes: &[&Attribute],
) -> Result<Signature> {
if public_attributes.len() > secret_key.ys.len() {
return Err(CoconutError::IssuanceMaxAttributes {
@@ -429,7 +447,7 @@ pub fn sign(
// (the python implementation hashes string representation of all attributes onto the curve,
// but I think the same can be achieved by just summing the attributes thus avoiding the unnecessary
// transformation. If I'm wrong, please correct me.)
let attributes_sum = public_attributes.iter().sum::<Scalar>();
let attributes_sum = public_attributes.iter().copied().sum::<Scalar>();
let h = hash_g1((params.gen1() * attributes_sum).to_bytes());
// x + m0 * y0 + m1 * y1 + ... mn * yn
@@ -437,7 +455,7 @@ pub fn sign(
+ public_attributes
.iter()
.zip(secret_key.ys.iter())
.map(|(m_i, y_i)| m_i * y_i)
.map(|(&m_i, y_i)| m_i * y_i)
.sum::<Scalar>();
let sig2 = h * exponent;
@@ -448,13 +466,14 @@ pub fn sign(
mod tests {
use super::*;
use crate::scheme::keygen::keygen;
use crate::tests::helpers::random_scalars_refs;
#[test]
fn blind_sign_request_bytes_roundtrip() {
// 0 public and 1 private attribute
let params = Parameters::new(1).unwrap();
let private_attributes = params.n_random_scalars(1);
let public_attributes = params.n_random_scalars(0);
random_scalars_refs!(private_attributes, params, 1);
random_scalars_refs!(public_attributes, params, 0);
let (_commitments_openings, lambda) =
prepare_blind_sign(&params, &private_attributes, &public_attributes).unwrap();
@@ -467,8 +486,8 @@ mod tests {
// 2 public and 2 private attributes
let params = Parameters::new(4).unwrap();
let private_attributes = params.n_random_scalars(2);
let public_attributes = params.n_random_scalars(2);
random_scalars_refs!(private_attributes, params, 2);
random_scalars_refs!(public_attributes, params, 2);
let (_commitments_openings, lambda) =
prepare_blind_sign(&params, &private_attributes, &public_attributes).unwrap();
@@ -483,8 +502,8 @@ mod tests {
#[test]
fn successful_verify_partial_blind_signature() {
let params = Parameters::new(4).unwrap();
let private_attributes = params.n_random_scalars(2);
let public_attributes = params.n_random_scalars(2);
random_scalars_refs!(private_attributes, params, 2);
random_scalars_refs!(public_attributes, params, 2);
let (_commitments_openings, request) =
prepare_blind_sign(&params, &private_attributes, &public_attributes).unwrap();
@@ -492,7 +511,7 @@ mod tests {
let validator_keypair = keygen(&params);
let blind_sig = blind_sign(
&params,
&validator_keypair.secret_key(),
validator_keypair.secret_key(),
&request,
&public_attributes,
)
@@ -503,36 +522,35 @@ mod tests {
&request,
&public_attributes,
&blind_sig,
&validator_keypair.verification_key()
validator_keypair.verification_key()
));
}
#[test]
fn successful_verify_partial_blind_signature_no_public_attributes() {
let params = Parameters::new(4).unwrap();
let private_attributes = params.n_random_scalars(2);
random_scalars_refs!(private_attributes, params, 2);
let (_commitments_openings, request) =
prepare_blind_sign(&params, &private_attributes, &[]).unwrap();
let validator_keypair = keygen(&params);
let blind_sig =
blind_sign(&params, &validator_keypair.secret_key(), &request, &[]).unwrap();
let blind_sig = blind_sign(&params, validator_keypair.secret_key(), &request, &[]).unwrap();
assert!(verify_partial_blind_signature(
&params,
&request,
&[],
&blind_sig,
&validator_keypair.verification_key()
validator_keypair.verification_key()
));
}
#[test]
fn fail_verify_partial_blind_signature_with_wrong_key() {
let params = Parameters::new(4).unwrap();
let private_attributes = params.n_random_scalars(2);
let public_attributes = params.n_random_scalars(2);
random_scalars_refs!(private_attributes, params, 2);
random_scalars_refs!(public_attributes, params, 2);
let (_commitments_openings, request) =
prepare_blind_sign(&params, &private_attributes, &public_attributes).unwrap();
@@ -541,7 +559,7 @@ mod tests {
let validator2_keypair = keygen(&params);
let blind_sig = blind_sign(
&params,
&validator_keypair.secret_key(),
validator_keypair.secret_key(),
&request,
&public_attributes,
)
@@ -553,7 +571,7 @@ mod tests {
&request,
&public_attributes,
&blind_sig,
&validator2_keypair.verification_key()
validator2_keypair.verification_key()
),);
}
}
+36 -7
View File
@@ -23,8 +23,12 @@ use crate::utils::{
};
use crate::Base58;
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq, Eq))]
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq, Eq, Clone))]
#[cfg_attr(
feature = "key-zeroize",
derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)
)]
pub struct SecretKey {
pub(crate) x: Scalar,
pub(crate) ys: Vec<Scalar>,
@@ -62,7 +66,9 @@ impl TryFrom<&[u8]> for SecretKey {
}
// this conversion will not fail as we are taking the same length of data
#[allow(clippy::unwrap_used)]
let x_bytes: [u8; 32] = bytes[..32].try_into().unwrap();
#[allow(clippy::unwrap_used)]
let ys_len = u64::from_le_bytes(bytes[32..40].try_into().unwrap());
let actual_ys_len = (bytes.len() - 40) / 32;
@@ -97,6 +103,10 @@ impl SecretKey {
(self.x, self.ys.clone())
}
pub fn size(&self) -> usize {
self.ys.len()
}
/// Derive verification key using this secret key.
pub fn verification_key(&self, params: &Parameters) -> VerificationKey {
let g1 = params.gen1();
@@ -141,6 +151,10 @@ impl Base58 for SecretKey {}
// TODO: perhaps change points to affine representation
// to make verification slightly more efficient?
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(
feature = "key-zeroize",
derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)
)]
pub struct VerificationKey {
// TODO add gen2 as per the paper or imply it from the fact library is using bls381?
pub(crate) alpha: G2Projective,
@@ -180,7 +194,9 @@ impl TryFrom<&[u8]> for VerificationKey {
}
// this conversion will not fail as we are taking the same length of data
#[allow(clippy::unwrap_used)]
let alpha_bytes: [u8; 96] = bytes[..96].try_into().unwrap();
#[allow(clippy::unwrap_used)]
let betas_len = u64::from_le_bytes(bytes[96..104].try_into().unwrap());
let actual_betas_len = (bytes.len() - 104) / (96 + 48);
@@ -204,6 +220,8 @@ impl TryFrom<&[u8]> for VerificationKey {
for i in 0..betas_len {
let start = (104 + i * 48) as usize;
let end = start + 48;
// we're using a constant 48 byte offset (which is the size of G1 compressed) so unwrap is fine
#[allow(clippy::unwrap_used)]
let beta_i_bytes = bytes[start..end].try_into().unwrap();
let beta_i = try_deserialize_g1_projective(
&beta_i_bytes,
@@ -220,6 +238,8 @@ impl TryFrom<&[u8]> for VerificationKey {
for i in 0..betas_len {
let start = (beta_g1_end + i * 96) as usize;
let end = start + 96;
// we're using a constant 96 byte offset (which is the size of G2 compressed) so unwrap is fine
#[allow(clippy::unwrap_used)]
let beta_i_bytes = bytes[start..end].try_into().unwrap();
let beta_i = try_deserialize_g2_projective(
&beta_i_bytes,
@@ -392,7 +412,11 @@ impl Bytable for VerificationKey {
impl Base58 for VerificationKey {}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq, Eq))]
#[cfg_attr(test, derive(PartialEq, Eq, Clone))]
#[cfg_attr(
feature = "key-zeroize",
derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)
)]
pub struct KeyPair {
secret_key: SecretKey,
verification_key: VerificationKey,
@@ -429,12 +453,12 @@ impl KeyPair {
}
}
pub fn secret_key(&self) -> SecretKey {
self.secret_key.clone()
pub fn secret_key(&self) -> &SecretKey {
&self.secret_key
}
pub fn verification_key(&self) -> VerificationKey {
self.verification_key.clone()
pub fn verification_key(&self) -> &VerificationKey {
&self.verification_key
}
pub fn to_bytes(&self) -> Vec<u8> {
@@ -488,6 +512,8 @@ impl TryFrom<&[u8]> for KeyPair {
});
}
// safety: we made bound check and we're using constant offest
#[allow(clippy::unwrap_used)]
let secret_key_len =
u64::from_le_bytes(bytes[header_len..header_len + 8].try_into().unwrap()) as usize;
let secret_key_start = header_len + 8;
@@ -503,6 +529,8 @@ impl TryFrom<&[u8]> for KeyPair {
});
}
// safety: we made bound check
#[allow(clippy::unwrap_used)]
let verification_key_len = u64::from_le_bytes(
bytes[secret_key_start + secret_key_len..secret_key_start + secret_key_len + 8]
.try_into()
@@ -515,6 +543,7 @@ impl TryFrom<&[u8]> for KeyPair {
)?;
let consumed_bytes = verification_key_start + verification_key_len;
let index = if consumed_bytes < bytes.len() && [consumed_bytes..].len() == 8 {
#[allow(clippy::unwrap_used)]
Some(u64::from_le_bytes(
bytes[consumed_bytes..consumed_bytes + 8]
.try_into()
+143 -97
View File
@@ -44,7 +44,10 @@ impl TryFrom<&[u8]> for Signature {
)));
}
// safety: we just checked for the length so the unwraps are fine
#[allow(clippy::expect_used)]
let sig1_bytes: &[u8; 48] = &bytes[..48].try_into().expect("Slice size != 48");
#[allow(clippy::expect_used)]
let sig2_bytes: &[u8; 48] = &bytes[48..].try_into().expect("Slice size != 48");
let sig1 = try_deserialize_g1_projective(
@@ -88,6 +91,45 @@ impl Signature {
pub fn from_bytes(bytes: &[u8]) -> Result<Signature> {
Signature::try_from(bytes)
}
pub fn verify(
&self,
params: &Parameters,
partial_verification_key: &VerificationKey,
private_attributes: &[&Attribute],
public_attributes: &[&Attribute],
commitment_hash: &G1Projective,
) -> Result<()> {
// Verify the commitment hash
if !(commitment_hash == &self.0) {
return Err(CoconutError::Verification(
"Verification of commitment hash from signature failed".to_string(),
));
}
let alpha = partial_verification_key.alpha;
let signed_attributes = private_attributes
.iter()
.chain(public_attributes.iter())
.zip(partial_verification_key.beta_g2.iter())
.map(|(&attr, beta_i)| beta_i * attr)
.sum::<G2Projective>();
// Verify the signature share
if !check_bilinear_pairing(
&self.0.to_affine(),
&G2Prepared::from((alpha + signed_attributes).to_affine()),
&self.1.to_affine(),
params.prepared_miller_g2(),
) {
return Err(CoconutError::Unblind(
"Verification of signature share failed".to_string(),
));
}
Ok(())
}
}
impl Bytable for Signature {
@@ -102,8 +144,7 @@ impl Bytable for Signature {
impl Base58 for Signature {}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq, Eq))]
#[derive(Debug, PartialEq, Eq)]
pub struct BlindedSignature(G1Projective, G1Projective);
impl Bytable for BlindedSignature {
@@ -129,7 +170,10 @@ impl TryFrom<&[u8]> for BlindedSignature {
)));
}
// safety: we just checked for the length so the unwraps are fine
#[allow(clippy::expect_used)]
let h_bytes: &[u8; 48] = &bytes[..48].try_into().expect("Slice size != 48");
#[allow(clippy::expect_used)]
let sig_bytes: &[u8; 48] = &bytes[48..].try_into().expect("Slice size != 48");
let h = try_deserialize_g1_projective(
@@ -148,24 +192,12 @@ impl TryFrom<&[u8]> for BlindedSignature {
impl BlindedSignature {
pub fn unblind(
&self,
params: &Parameters,
partial_verification_key: &VerificationKey,
private_attributes: &[Attribute],
public_attributes: &[Attribute],
commitment_hash: &G1Projective,
pedersen_commitments_openings: &[Scalar],
) -> Result<Signature> {
// parse the signature
let h = &self.0;
let c = &self.1;
// Verify the commitment hash
if !(commitment_hash == h) {
return Err(CoconutError::Unblind(
"Verification of commitment hash from signature failed".to_string(),
));
}
let blinding_removers = partial_verification_key
.beta_g1
.iter()
@@ -175,30 +207,29 @@ impl BlindedSignature {
let unblinded_c = c - blinding_removers;
let alpha = partial_verification_key.alpha;
let signed_attributes = private_attributes
.iter()
.chain(public_attributes.iter())
.zip(partial_verification_key.beta_g2.iter())
.map(|(attr, beta_i)| beta_i * attr)
.sum::<G2Projective>();
// Verify the signature share
if !check_bilinear_pairing(
&h.to_affine(),
&G2Prepared::from((alpha + signed_attributes).to_affine()),
&unblinded_c.to_affine(),
params.prepared_miller_g2(),
) {
return Err(CoconutError::Unblind(
"Verification of signature share failed".to_string(),
));
}
Ok(Signature(*h, unblinded_c))
}
pub fn unblind_and_verify(
&self,
params: &Parameters,
partial_verification_key: &VerificationKey,
private_attributes: &[&Attribute],
public_attributes: &[&Attribute],
commitment_hash: &G1Projective,
pedersen_commitments_openings: &[Scalar],
) -> Result<Signature> {
let unblinded = self.unblind(partial_verification_key, pedersen_commitments_openings)?;
unblinded.verify(
params,
partial_verification_key,
private_attributes,
public_attributes,
commitment_hash,
)?;
Ok(unblinded)
}
pub fn to_bytes(&self) -> [u8; 96] {
let mut bytes = [0u8; 96];
bytes[..48].copy_from_slice(&self.0.to_affine().to_compressed());
@@ -237,25 +268,25 @@ impl SignatureShare {
#[cfg(test)]
mod tests {
use super::*;
use crate::hash_to_scalar;
use crate::scheme::aggregation::{aggregate_signatures, aggregate_verification_keys};
use crate::scheme::issuance::{blind_sign, compute_hash, prepare_blind_sign, sign};
use crate::scheme::keygen::{keygen, ttp_keygen};
use crate::scheme::verification::{prove_bandwidth_credential, verify, verify_credential};
use super::*;
use crate::tests::helpers::random_scalars_refs;
#[test]
fn unblind_returns_error_if_integrity_check_on_commitment_hash_fails() {
let params = Parameters::new(2).unwrap();
let private_attributes = params.n_random_scalars(2_usize);
random_scalars_refs!(private_attributes, params, 2);
let (_commitments_openings, lambda) =
prepare_blind_sign(&params, &private_attributes, &[]).unwrap();
let keypair1 = keygen(&params);
let sig1 = blind_sign(&params, &keypair1.secret_key(), &lambda, &[]).unwrap();
let sig1 = blind_sign(&params, keypair1.secret_key(), &lambda, &[]).unwrap();
let wrong_commitment_opening = params.random_scalar();
let wrong_commitment = params.gen1() * wrong_commitment_opening;
@@ -263,9 +294,9 @@ mod tests {
let wrong_commitments_openings = params.n_random_scalars(private_attributes.len());
assert!(sig1
.unblind(
.unblind_and_verify(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&private_attributes,
&[],
&fake_commitment_hash,
@@ -277,20 +308,23 @@ mod tests {
#[test]
fn unblind_returns_error_if_signature_verification_fails() {
let params = Parameters::new(2).unwrap();
let private_attributes = vec![hash_to_scalar("Attribute1"), hash_to_scalar("Attribute2")];
let private_attributes2 = vec![hash_to_scalar("Attribute3"), hash_to_scalar("Attribute4")];
let p = [hash_to_scalar("Attribute1"), hash_to_scalar("Attribute2")];
let private_attributes = vec![&p[0], &p[1]];
let p2 = [hash_to_scalar("Attribute3"), hash_to_scalar("Attribute4")];
let private_attributes2 = vec![&p2[0], &p2[1]];
let (commitments_openings, lambda) =
prepare_blind_sign(&params, &private_attributes, &[]).unwrap();
let keypair1 = keygen(&params);
let sig1 = blind_sign(&params, &keypair1.secret_key(), &lambda, &[]).unwrap();
let sig1 = blind_sign(&params, keypair1.secret_key(), &lambda, &[]).unwrap();
assert!(sig1
.unblind(
.unblind_and_verify(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&private_attributes2,
&[],
&lambda.get_commitment_hash(),
@@ -304,7 +338,7 @@ mod tests {
let params = Parameters::new(2).unwrap();
let serial_number = params.random_scalar();
let binding_number = params.random_scalar();
let private_attributes = vec![serial_number, binding_number];
let private_attributes = vec![&serial_number, &binding_number];
let keypair1 = keygen(&params);
let keypair2 = keygen(&params);
@@ -312,11 +346,11 @@ mod tests {
let (commitments_openings, lambda) =
prepare_blind_sign(&params, &private_attributes, &[]).unwrap();
let sig1 = blind_sign(&params, &keypair1.secret_key(), &lambda, &[])
let sig1 = blind_sign(&params, keypair1.secret_key(), &lambda, &[])
.unwrap()
.unblind(
.unblind_and_verify(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&private_attributes,
&[],
&lambda.get_commitment_hash(),
@@ -324,11 +358,11 @@ mod tests {
)
.unwrap();
let sig2 = blind_sign(&params, &keypair2.secret_key(), &lambda, &[])
let sig2 = blind_sign(&params, keypair2.secret_key(), &lambda, &[])
.unwrap()
.unblind(
.unblind_and_verify(
&params,
&keypair2.verification_key(),
keypair2.verification_key(),
&private_attributes,
&[],
&lambda.get_commitment_hash(),
@@ -338,39 +372,39 @@ mod tests {
let theta1 = prove_bandwidth_credential(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&sig1,
serial_number,
binding_number,
&serial_number,
&binding_number,
)
.unwrap();
let theta2 = prove_bandwidth_credential(
&params,
&keypair2.verification_key(),
keypair2.verification_key(),
&sig2,
serial_number,
binding_number,
&serial_number,
&binding_number,
)
.unwrap();
assert!(verify_credential(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&theta1,
&[],
));
assert!(verify_credential(
&params,
&keypair2.verification_key(),
keypair2.verification_key(),
&theta2,
&[],
));
assert!(!verify_credential(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&theta2,
&[],
));
@@ -379,30 +413,30 @@ mod tests {
#[test]
fn verification_on_two_public_attributes() {
let mut params = Parameters::new(2).unwrap();
let attributes = params.n_random_scalars(2);
random_scalars_refs!(attributes, params, 2);
let keypair1 = keygen(&params);
let keypair2 = keygen(&params);
let sig1 = sign(&mut params, &keypair1.secret_key(), &attributes).unwrap();
let sig2 = sign(&mut params, &keypair2.secret_key(), &attributes).unwrap();
let sig1 = sign(&mut params, keypair1.secret_key(), &attributes).unwrap();
let sig2 = sign(&mut params, keypair2.secret_key(), &attributes).unwrap();
assert!(verify(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&attributes,
&sig1,
));
assert!(!verify(
&params,
&keypair2.verification_key(),
keypair2.verification_key(),
&attributes,
&sig1,
));
assert!(!verify(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&attributes,
&sig2,
));
@@ -411,10 +445,11 @@ mod tests {
#[test]
fn verification_on_two_public_and_two_private_attributes() {
let params = Parameters::new(4).unwrap();
let public_attributes = params.n_random_scalars(2);
random_scalars_refs!(public_attributes, params, 2);
let serial_number = params.random_scalar();
let binding_number = params.random_scalar();
let private_attributes = vec![serial_number, binding_number];
let private_attributes = vec![&serial_number, &binding_number];
let keypair1 = keygen(&params);
let keypair2 = keygen(&params);
@@ -422,11 +457,11 @@ mod tests {
let (commitments_openings, lambda) =
prepare_blind_sign(&params, &private_attributes, &public_attributes).unwrap();
let sig1 = blind_sign(&params, &keypair1.secret_key(), &lambda, &public_attributes)
let sig1 = blind_sign(&params, keypair1.secret_key(), &lambda, &public_attributes)
.unwrap()
.unblind(
.unblind_and_verify(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&private_attributes,
&public_attributes,
&lambda.get_commitment_hash(),
@@ -434,11 +469,11 @@ mod tests {
)
.unwrap();
let sig2 = blind_sign(&params, &keypair2.secret_key(), &lambda, &public_attributes)
let sig2 = blind_sign(&params, keypair2.secret_key(), &lambda, &public_attributes)
.unwrap()
.unblind(
.unblind_and_verify(
&params,
&keypair2.verification_key(),
keypair2.verification_key(),
&private_attributes,
&public_attributes,
&lambda.get_commitment_hash(),
@@ -448,39 +483,39 @@ mod tests {
let theta1 = prove_bandwidth_credential(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&sig1,
serial_number,
binding_number,
&serial_number,
&binding_number,
)
.unwrap();
let theta2 = prove_bandwidth_credential(
&params,
&keypair2.verification_key(),
keypair2.verification_key(),
&sig2,
serial_number,
binding_number,
&serial_number,
&binding_number,
)
.unwrap();
assert!(verify_credential(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&theta1,
&public_attributes,
));
assert!(verify_credential(
&params,
&keypair2.verification_key(),
keypair2.verification_key(),
&theta2,
&public_attributes,
));
assert!(!verify_credential(
&params,
&keypair1.verification_key(),
keypair1.verification_key(),
&theta2,
&public_attributes,
));
@@ -489,10 +524,11 @@ mod tests {
#[test]
fn verification_on_two_public_and_two_private_attributes_from_two_signers() {
let params = Parameters::new(4).unwrap();
let public_attributes = params.n_random_scalars(2);
random_scalars_refs!(public_attributes, params, 2);
let serial_number = params.random_scalar();
let binding_number = params.random_scalar();
let private_attributes = vec![serial_number, binding_number];
let private_attributes = vec![&serial_number, &binding_number];
let keypairs = ttp_keygen(&params, 2, 3).unwrap();
@@ -502,11 +538,11 @@ mod tests {
let sigs = keypairs
.iter()
.map(|keypair| {
blind_sign(&params, &keypair.secret_key(), &lambda, &public_attributes)
blind_sign(&params, keypair.secret_key(), &lambda, &public_attributes)
.unwrap()
.unblind(
.unblind_and_verify(
&params,
&keypair.verification_key(),
keypair.verification_key(),
&private_attributes,
&public_attributes,
&lambda.get_commitment_hash(),
@@ -518,7 +554,7 @@ mod tests {
let vks = keypairs
.into_iter()
.map(|keypair| keypair.verification_key())
.map(|keypair| keypair.verification_key().clone())
.collect::<Vec<_>>();
let mut attributes = Vec::with_capacity(private_attributes.len() + public_attributes.len());
@@ -530,9 +566,14 @@ mod tests {
aggregate_signatures(&params, &aggr_vk, &attributes, &sigs[..2], Some(&[1, 2]))
.unwrap();
let theta =
prove_bandwidth_credential(&params, &aggr_vk, &aggr_sig, serial_number, binding_number)
.unwrap();
let theta = prove_bandwidth_credential(
&params,
&aggr_vk,
&aggr_sig,
&serial_number,
&binding_number,
)
.unwrap();
assert!(verify_credential(
&params,
@@ -547,9 +588,14 @@ mod tests {
aggregate_signatures(&params, &aggr_vk, &attributes, &sigs[1..], Some(&[2, 3]))
.unwrap();
let theta =
prove_bandwidth_credential(&params, &aggr_vk, &aggr_sig, serial_number, binding_number)
.unwrap();
let theta = prove_bandwidth_credential(
&params,
&aggr_vk,
&aggr_sig,
&serial_number,
&binding_number,
)
.unwrap();
assert!(verify_credential(
&params,
+26 -18
View File
@@ -43,6 +43,8 @@ impl TryFrom<&[u8]> for Theta {
));
}
// safety: we just checked for the length so the unwraps are fine
#[allow(clippy::unwrap_used)]
let blinded_message_bytes = bytes[..96].try_into().unwrap();
let blinded_message = try_deserialize_g2_projective(
&blinded_message_bytes,
@@ -51,6 +53,8 @@ impl TryFrom<&[u8]> for Theta {
),
)?;
// safety: we just checked for the length so the unwraps are fine
#[allow(clippy::unwrap_used)]
let blinded_serial_number_bytes = bytes[96..192].try_into().unwrap();
let blinded_serial_number = try_deserialize_g2_projective(
&blinded_serial_number_bytes,
@@ -130,7 +134,7 @@ impl Base58 for Theta {}
pub fn compute_kappa(
params: &Parameters,
verification_key: &VerificationKey,
private_attributes: &[Attribute],
private_attributes: &[&Attribute],
blinding_factor: Scalar,
) -> G2Projective {
params.gen2() * blinding_factor
@@ -138,11 +142,11 @@ pub fn compute_kappa(
+ private_attributes
.iter()
.zip(verification_key.beta_g2.iter())
.map(|(priv_attr, beta_i)| beta_i * priv_attr)
.map(|(&priv_attr, beta_i)| beta_i * priv_attr)
.sum::<G2Projective>()
}
pub fn compute_zeta(params: &Parameters, serial_number: Attribute) -> G2Projective {
pub fn compute_zeta(params: &Parameters, serial_number: &Attribute) -> G2Projective {
params.gen2() * serial_number
}
@@ -150,8 +154,8 @@ pub fn prove_bandwidth_credential(
params: &Parameters,
verification_key: &VerificationKey,
signature: &Signature,
serial_number: Attribute,
binding_number: Attribute,
serial_number: &Attribute,
binding_number: &Attribute,
) -> Result<Theta> {
if verification_key.beta_g2.len() < 2 {
return Err(
@@ -171,7 +175,7 @@ pub fn prove_bandwidth_credential(
// Thus, we need kappa which allows us to verify sigma'. In particular,
// kappa is computed on m as input, but thanks to the use or random value r,
// it does not reveal any information about m.
let private_attributes = vec![serial_number, binding_number];
let private_attributes = [serial_number, binding_number];
let blinded_message = compute_kappa(
params,
verification_key,
@@ -185,8 +189,8 @@ pub fn prove_bandwidth_credential(
let pi_v = ProofKappaZeta::construct(
params,
verification_key,
&serial_number,
&binding_number,
serial_number,
binding_number,
&sign_blinding_factor,
&blinded_message,
&blinded_serial_number,
@@ -221,7 +225,10 @@ pub fn check_vk_pairing(
if values_len == 0 || values_len - 1 != vk.beta_g1.len() || values_len - 1 != vk.beta_g2.len() {
return false;
}
if vk.alpha != *dkg_values.last().unwrap() {
// safety: we made an explicit check for if the length of the slice is 0, thus unwrap here is fine
#[allow(clippy::unwrap_used)]
if &vk.alpha != *dkg_values.last().as_ref().unwrap() {
return false;
}
if dkg_values
@@ -249,7 +256,7 @@ pub fn verify_credential(
params: &Parameters,
verification_key: &VerificationKey,
theta: &Theta,
public_attributes: &[Attribute],
public_attributes: &[&Attribute],
) -> bool {
if public_attributes.len() + theta.pi_v.private_attributes_len()
> verification_key.beta_g2.len()
@@ -272,7 +279,7 @@ pub fn verify_credential(
.iter()
.skip(theta.pi_v.private_attributes_len()),
)
.map(|(pub_attr, beta_i)| beta_i * pub_attr)
.map(|(&pub_attr, beta_i)| beta_i * pub_attr)
.sum::<G2Projective>();
theta.blinded_message + signed_public_attributes
@@ -291,14 +298,14 @@ pub fn verify_credential(
pub fn verify(
params: &Parameters,
verification_key: &VerificationKey,
public_attributes: &[Attribute],
public_attributes: &[&Attribute],
sig: &Signature,
) -> bool {
let kappa = (verification_key.alpha
+ public_attributes
.iter()
.zip(verification_key.beta_g2.iter())
.map(|(m_i, b_i)| b_i * m_i)
.map(|(&m_i, b_i)| b_i * m_i)
.sum::<G2Projective>())
.to_affine();
@@ -320,10 +327,11 @@ mod tests {
#[test]
fn vk_pairing() {
let params = setup(2).unwrap();
let vk = keygen(&params).verification_key();
let keypair = keygen(&params);
let vk = keypair.verification_key();
let mut dkg_values = vk.beta_g2.clone();
dkg_values.push(vk.alpha);
assert!(check_vk_pairing(&params, &dkg_values, &vk));
assert!(check_vk_pairing(&params, &dkg_values, vk));
}
#[test]
@@ -340,10 +348,10 @@ mod tests {
let theta = prove_bandwidth_credential(
&params,
&keypair.verification_key(),
keypair.verification_key(),
&signature,
serial_number,
binding_number,
&serial_number,
&binding_number,
)
.unwrap();
+5 -4
View File
@@ -1,6 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::random_scalars_refs;
use crate::tests::helpers::tests::generate_dkg_keys;
use crate::{
aggregate_verification_keys, setup, tests::helpers::*, ttp_keygen, verify_credential,
@@ -12,14 +13,14 @@ fn keygen() -> Result<(), CoconutError> {
let params = setup(5)?;
let node_indices = vec![15u64, 248, 33521];
let public_attributes = params.n_random_scalars(2);
random_scalars_refs!(public_attributes, params, 2);
// generate_keys
let coconut_keypairs = ttp_keygen(&params, 2, 3)?;
let verification_keys: Vec<VerificationKey> = coconut_keypairs
.iter()
.map(|keypair| keypair.verification_key())
.map(|keypair| keypair.verification_key().clone())
.collect();
// aggregate verification keys
@@ -50,14 +51,14 @@ fn dkg() -> Result<(), CoconutError> {
let params = setup(5)?;
let node_indices = vec![15u64, 248, 33521];
let public_attributes = params.n_random_scalars(2);
random_scalars_refs!(public_attributes, params, 2);
// generate_keys
let coconut_keypairs = generate_dkg_keys(5, &node_indices);
let verification_keys: Vec<VerificationKey> = coconut_keypairs
.iter()
.map(|keypair| keypair.verification_key())
.map(|keypair| keypair.verification_key().clone())
.collect();
// aggregate verification keys
+22 -7
View File
@@ -5,15 +5,17 @@ use crate::*;
use itertools::izip;
use std::fmt::Debug;
// unwraps are fine in the test code
#[allow(clippy::unwrap_used)]
pub fn theta_from_keys_and_attributes(
params: &Parameters,
coconut_keypairs: &Vec<KeyPair>,
indices: &[scheme::SignerIndex],
public_attributes: &Vec<PublicAttribute>,
public_attributes: &[&PublicAttribute],
) -> Result<Theta, CoconutError> {
let serial_number = params.random_scalar();
let binding_number = params.random_scalar();
let private_attributes = vec![serial_number, binding_number];
let private_attributes = vec![&serial_number, &binding_number];
// generate commitment
let (commitments_openings, blind_sign_request) =
@@ -21,7 +23,7 @@ pub fn theta_from_keys_and_attributes(
let verification_keys: Vec<VerificationKey> = coconut_keypairs
.iter()
.map(|keypair| keypair.verification_key())
.map(|keypair| keypair.verification_key().clone())
.collect();
// aggregate verification keys
@@ -33,7 +35,7 @@ pub fn theta_from_keys_and_attributes(
for keypair in coconut_keypairs {
let blinded_signature = blind_sign(
params,
&keypair.secret_key(),
keypair.secret_key(),
&blind_sign_request,
public_attributes,
)?;
@@ -49,7 +51,7 @@ pub fn theta_from_keys_and_attributes(
.map(|(idx, s, vk)| {
(
*idx,
s.unblind(
s.unblind_and_verify(
params,
vk,
&private_attributes,
@@ -81,13 +83,15 @@ pub fn theta_from_keys_and_attributes(
params,
&verification_key,
&signature,
serial_number,
binding_number,
&serial_number,
&binding_number,
)?;
Ok(theta)
}
// unwraps are fine in the test code
#[allow(clippy::unwrap_used)]
pub fn transpose_matrix<T: Debug>(matrix: Vec<Vec<T>>) -> Vec<Vec<T>> {
if matrix.is_empty() {
return vec![];
@@ -166,3 +170,14 @@ pub mod tests {
.collect()
}
}
#[macro_export]
macro_rules! random_scalars_refs {
( $x: ident, $params: expr, $n: expr ) => {
let _vec = $params.n_random_scalars($n);
#[allow(clippy::map_identity)]
let $x = _vec.iter().collect::<Vec<_>>();
};
}
pub use random_scalars_refs;
+68 -1
View File
@@ -1,4 +1,13 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
use crate::CoconutError;
use bls12_381::{G1Affine, G1Projective, Scalar};
use group::GroupEncoding;
use std::convert::TryInto;
pub trait Bytable
where
@@ -14,9 +23,67 @@ where
Self: Bytable,
{
fn try_from_bs58<S: AsRef<str>>(x: S) -> Result<Self, CoconutError> {
Self::try_from_byte_slice(&bs58::decode(x.as_ref()).into_vec().unwrap())
let bs58_decoded = &bs58::decode(x.as_ref()).into_vec()?;
Self::try_from_byte_slice(bs58_decoded)
}
fn to_bs58(&self) -> String {
bs58::encode(self.to_byte_vec()).into_string()
}
}
impl Bytable for Scalar {
fn to_byte_vec(&self) -> Vec<u8> {
self.to_bytes().to_vec()
}
fn try_from_byte_slice(slice: &[u8]) -> Result<Self, CoconutError> {
let received = slice.len();
let Ok(arr) = slice.try_into() else {
return Err(CoconutError::UnexpectedArrayLength {
typ: "Scalar".to_string(),
received,
expected: 32,
});
};
let maybe_scalar = Scalar::from_bytes(arr);
if maybe_scalar.is_none().into() {
Err(CoconutError::ScalarDeserializationFailure)
} else {
// safety: this unwrap is fine as we've just checked the element is not none
#[allow(clippy::unwrap_used)]
Ok(maybe_scalar.unwrap())
}
}
}
impl Base58 for Scalar {}
impl Bytable for G1Projective {
fn to_byte_vec(&self) -> Vec<u8> {
self.to_bytes().as_ref().to_vec()
}
fn try_from_byte_slice(slice: &[u8]) -> Result<Self, CoconutError> {
let received = slice.len();
let arr: Result<[u8; 48], _> = slice.try_into();
let Ok(bytes) = arr else {
return Err(CoconutError::UnexpectedArrayLength {
typ: "G1Projective".to_string(),
received,
expected: 48,
});
};
let maybe_g1 = G1Affine::from_compressed(&bytes);
if maybe_g1.is_none().into() {
Err(CoconutError::G1ProjectiveDeserializationFailure)
} else {
// safety: this unwrap is fine as we've just checked the element is not none
#[allow(clippy::unwrap_used)]
Ok(maybe_g1.unwrap().into())
}
}
}
impl Base58 for G1Projective {}
+3
View File
@@ -34,6 +34,7 @@ impl Polynomial {
// just return the last term of the polynomial
} else if x.is_zero().into() {
// we checked that coefficients are not empty so unwrap here is fine
#[allow(clippy::unwrap_used)]
*self.coefficients.first().unwrap()
} else {
self.coefficients
@@ -148,6 +149,8 @@ pub(crate) fn try_deserialize_scalar_vec(
let mut out = Vec::with_capacity(expected_len as usize);
for i in 0..expected_len as usize {
// we just checked we have exactly the amount of bytes we need and thus the unwrap is fine
#[allow(clippy::unwrap_used)]
let s_bytes = bytes[i * 32..(i + 1) * 32].try_into().unwrap();
let s = match Into::<Option<Scalar>>::into(Scalar::from_bytes(&s_bytes)) {
None => return Err(err),
+1
View File
@@ -2,6 +2,7 @@
name = "nym-socks5-client-core"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -3,6 +3,7 @@ name = "nym-ordered-buffer"
version = "0.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+2 -1
View File
@@ -3,12 +3,13 @@ name = "nym-socks5-proxy-helpers"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bytes = "1.0"
tokio = { version = "1.24.1", features = [ "net", "io-util", "sync", "macros", "time", "rt-multi-thread" ] }
tokio = { workspace = true, features = [ "net", "io-util", "sync", "macros", "time", "rt-multi-thread" ] }
tokio-util = { workspace = true, features = [ "io" ] } # reason for getting this guy is to to able to port to tokio 1.X more quickly by being able to use
# their `read_buf` [from the util crate] replacement rather than having to rethink/reimplement `AvailableReader` with the new AsyncRead trait definition.
# In the long run, the dependency should probably get removed in favour of pure-tokio implementation, but for time being it's fine.
+1
View File
@@ -3,6 +3,7 @@ name = "nym-socks5-requests"
version = "0.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -5,6 +5,7 @@
name = "nym-statistics-common"
version = "1.0.1"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+2 -1
View File
@@ -2,6 +2,7 @@
name = "nym-store-cipher"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -20,4 +21,4 @@ getrandom = { version = "0.2", features = ["js"] }
[features]
default = []
json = ["serde_json"]
json = ["serde_json"]
+3 -11
View File
@@ -373,14 +373,6 @@ impl TaskClient {
}
}
pub async fn wait(&mut self, duration: Duration) {
tokio::select! {
biased;
_ = self.recv() => (),
_ = sleep(duration) => (),
}
}
// Create a dummy that will never report that we should shutdown.
pub fn dummy() -> TaskClient {
let (_notify_tx, notify_rx) = watch::channel(());
@@ -502,13 +494,13 @@ impl Drop for TaskClient {
fn drop(&mut self) {
if !self.mode.should_signal_on_drop() {
self.log(
Level::Debug,
"the task client is getting dropped: this is expected during client shutdown",
Level::Trace,
"the task client is getting dropped but inststructed to not signal: this is expected during client shutdown",
);
return;
} else {
self.log(
Level::Info,
Level::Debug,
"the task client is getting dropped: this is expected during client shutdown",
);
}
+1
View File
@@ -5,6 +5,7 @@ description = "Nym common types"
authors.workspace = true
edition = "2021"
rust-version = "1.58"
license.workspace = true
[dependencies]
base64 = "0.21.4"
+10 -5
View File
@@ -77,7 +77,7 @@ impl GatewayBond {
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct GatewayNodeDetailsResponse {
pub identity_key: String,
pub sphinx_key: String,
@@ -88,6 +88,7 @@ pub struct GatewayNodeDetailsResponse {
pub data_store: String,
pub network_requester: Option<GatewayNetworkRequesterDetails>,
pub ip_packet_router: Option<GatewayIpPacketRouterDetails>,
}
impl fmt::Display for GatewayNodeDetailsResponse {
@@ -98,7 +99,7 @@ impl fmt::Display for GatewayNodeDetailsResponse {
writeln!(f, "bind address: {}", self.bind_address)?;
writeln!(
f,
"mix Port: {}, clients port: {}",
"mix port: {}, clients port: {}",
self.mix_port, self.clients_port
)?;
@@ -107,11 +108,15 @@ impl fmt::Display for GatewayNodeDetailsResponse {
if let Some(nr) = &self.network_requester {
nr.fmt(f)?;
}
if let Some(ipr) = &self.ip_packet_router {
ipr.fmt(f)?;
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct GatewayNetworkRequesterDetails {
pub enabled: bool,
@@ -149,7 +154,7 @@ impl fmt::Display for GatewayNetworkRequesterDetails {
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct GatewayIpPacketRouterDetails {
pub enabled: bool,
@@ -164,7 +169,7 @@ pub struct GatewayIpPacketRouterDetails {
impl fmt::Display for GatewayIpPacketRouterDetails {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "IP packet router:")?;
writeln!(f, "ip packet router:")?;
writeln!(f, "\tenabled: {}", self.enabled)?;
writeln!(f, "\tconfig path: {}", self.config_path)?;
+2 -1
View File
@@ -3,6 +3,7 @@ name = "wasm-storage"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,4 +18,4 @@ indexed_db_futures = { version = " 0.3.0"}
thiserror = { workspace = true }
nym-store-cipher = { path = "../../store-cipher", features = ["json"] }
wasm-utils = { path = "../utils", default-features = false }
wasm-utils = { path = "../utils", default-features = false }
+1
View File
@@ -3,6 +3,7 @@ name = "wasm-utils"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+17 -9
View File
@@ -104,8 +104,16 @@ unlicensed = "deny"
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
allow = [
#"MIT",
#"Apache-2.0",
"MIT",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"0BSD",
"MPL-2.0",
"CC0-1.0",
"Unicode-DFS-2016",
"OpenSSL",
]
# List of explicitly disallowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
@@ -143,22 +151,22 @@ exceptions = [
# Some crates don't have (easily) machine readable licensing information,
# adding a clarification entry for it allows you to manually specify the
# licensing information
#[[licenses.clarify]]
[[licenses.clarify]]
# The name of the crate the clarification applies to
#name = "ring"
name = "ring"
# The optional version constraint for the crate
#version = "*"
version = "*"
# The SPDX expression for the license requirements of the crate
#expression = "MIT AND ISC AND OpenSSL"
expression = "MIT AND ISC AND OpenSSL"
# One or more files in the crate's source used as the "source of truth" for
# the license expression. If the contents match, the clarification will be used
# when running the license check, otherwise the clarification will be ignored
# and the crate will be checked normally, which may produce warnings or errors
# depending on the rest of your configuration
#license-files = [
license-files = [
# Each entry is a crate relative path, and the (opaque) hash of its contents
#{ path = "LICENSE", hash = 0xbd0eed23 }
#]
{ path = "LICENSE", hash = 0xbd0eed23 }
]
[licenses.private]
# If true, ignores workspace crates that aren't published, or are only
+5
View File
@@ -55,6 +55,11 @@ wallet_release_version = "1.2.8"
command = "mdbook-last-changed"
renderer = ["html"]
# used for grabbing output of binary commands for automation
# https://github.com/FauconFan/mdbook-cmdrun
[preprocessor.cmdrun]
#########
# BUILD #
#########
+10 -6
View File
@@ -51,7 +51,7 @@
- [Querying the Chain](tutorials/cosmos-service/querying.md)
- [Typescript](tutorials/typescript.md)
- [[DEPRECATED] Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
- [Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
- [Tutorial Overview](tutorials/simple-service-provider/overview.md)
- [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md)
- [Building Your User Client](tutorials/simple-service-provider/user-client.md)
@@ -59,14 +59,18 @@
- [Building Your Service Provider](tutorials/simple-service-provider/service-provider.md)
- [Sending a Message Through the Mixnet](tutorials/simple-service-provider/sending-message.md)
# Shipyard Builders Hackathon 2023
- [General Info & Resources](shipyard/general.md)
- [Hackathon Challenges](shipyard/challenges-overview.md)
- [A Note on Infrastructure](shipyard/infra.md)
- [Submission Guidelines](shipyard/guidelines.md)
[//]: # (TODO make generic )
[//]: # (# Shipyard Builders Hackathon 2023 )
[//]: # (- [General Info & Resources]&#40;shipyard/general.md&#41;)
[//]: # (- [Hackathon Challenges]&#40;shipyard/challenges-overview.md&#41;)
[//]: # (- [A Note on Infrastructure]&#40;shipyard/infra.md&#41;)
[//]: # (- [Submission Guidelines]&#40;shipyard/guidelines.md&#41;)
# Events
- [37C3](events/37c3/welcome.md)
- [NymVPN](events/37c3/nym-vpn.md)
- [FAQ](events/37c3/faq.md)
- [Web3Privacy Now](./events/web3-privacy.md)
- [HCPP23-serinko](./events/hcpp23-serinko.md)
- [HCPP23-max](./events/hcpp23-max.md)
@@ -0,0 +1,70 @@
# Frequently Asked Questions @ 37C3
Besides the FAQ for CCC 2023 event listed below, you can have a look at [Nym general FAQ](https://nymtech.net/developers/faq/general-faq.html) and read through Nym's technical [documentation](https://nymtech.net/docs), [Developer Portal](https://nymtech.net/developers) and [Operators Guide](https://nymtech.net/operators).
## NymVPN
Make sure you read [NymVPN webpage](https://nymvpn.com/en), the official NymVPN [support & FAQ page](https://nymvpn.com/en/support) and our guide on how to [install, run and test](./nym-vpn.md) the client.
Below are some extra FAQ's which came out during the event.
### What's the difference between 2-hops and 5-hops
The default is 5-hops (including Entry and Exit Gateways), which means that the traffic goes from the local client to Entry Gateway -> though 3 layes of Mix Nodes -> to Exit Gateway -> internet. this option uses all the Nym Mixnet features for maximum privacy.
```
┌─►mix──┐ mix mix
│ │
Entry │ │ Exit
client ───► Gateway ──┘ mix │ mix ┌─►mix ───► Gateway ───► internet
│ │
│ │
mix └─►mix──┘ mix
```
The 2-hop option is going from the local client -> Entry Gateway -> directly to Exit Gateway -> internet. This option is good for operations demanding faster connection. Keep in mind that this setup by-passes the 3 layers of Mix Nodes.
```
Entry Exit
client ───► Gateway ────► Gateway ───► internet
```
We highly recommend to read more about [Nym network overview](https://nymtech.net/docs/architecture/network-overview.html) and the [Mixnet traffic flow](https://nymtech.net/docs/architecture/traffic-flow.html).
### Why do I see different sizes of packets in my terminal log?
One of features of Nym Mixnet's clients is to break data into the same size packets called Sphinx, which is currently ~2kb. When running NymVPN, the data log shows payload sizes, which are the raw sizes of the IP packets, not Sphinx. The payload sizes will be capped by the configured MTU, which is set around 1500 bytes.
### What is 'poisson filter' about?
By default `--enable-poisson` is disabled and packets are sent from the local client to the Entry Gateway as quickly as possible. With the poisson process enabled the Nym client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
Enabling the poisson filter is one of the key mechanisms to de-correlate input and output traffic to the Mixnet. The performance impact however is dramatic:
1 packer per 20ms is 50 packets / sec so ballpark 100kb/s.
For mobile clients that means constantly sending data eating up data allowance.
## Nym Mixnet Architecture and Rewards
We have a list of questions related to Nym Nodes and the incentives behind running them under [FAQ pages](https://nymtech.net/operators/faq/mixnodes-faq.html) in our [Operators Guide](https://nymtech.net/operators). For better knowledge about Nym architecture we recommend to read [Nym network overview](https://nymtech.net/docs/architecture/network-overview.html) and the [Mixnet traffic flow](https://nymtech.net/docs/architecture/traffic-flow.html) in our [technical documentation](https://nymtech.net/docs).
## Project Smoosh
Project Smoosh is a code name for a process in which different components of Nym Mixnet architecture get *smooshed* into one binary. Check out [Smoosh FAQ](https://nymtech.net/operators/faq/smoosh-faq.html) in Operators Guide to read more.
## Exit Gateway
Part of the the transition under code name [Project Smoosh](./faq.md#project-smoosh) is a creation of [Nym Exit Gateway](https://nymtech.net/operators/legal/exit-gateway.html) functionality. The operators running Gateways would have to “open” their nodes to a wider range of online services, in a similar fashion to Tor exit relays. The main change will be to expand the original short [allowed.list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) to a more permissive setup. An [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) will constrain the hosts that the users of the Nym VPN and Mixnet can connect to. This will be done in an effort to protect the operators, as Gateways will act both as SOCKS5 Network Requesters, and exit nodes for IP traffic from Nym VPN and Mixnet clients.
* Read more how the exit policy gets implemented [here](https://nymtech.net/operators/faq/smoosh-faq.html#how-will-the-exit-policy-be-implemented)
* Check out [Nym Operators Legal Forum](https://nymtech.net/operators/legal/exit-gateway.html)
* Do reach out to us during 37c3 with any experiences you may have running Tor Exit relays or legal findings and suggestions for Nym Exit Gateway operators
## Nym Integrations and SDKs
If you are a dev who is interested to integrate Nym, have a look on our SDK tutorials:
* [Rust SDKs](https://nymtech.net/developers/tutorials/cosmos-service/intro.html)
* [TypeScript SDKs](https://sdk.nymtech.net/)
* [Integration FAQ](https://nymtech.net/developers/faq/integrations-faq.html)
Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@@ -0,0 +1,433 @@
# NymVPN alpha
Nym has announced [NymVPN](https://nymvpn.com/en) and presented the [NymVPN Litepaper](https://nymvpn.com/nymVPN-litepaper.pdf). At CCC 2023 we have the unique opportunity to do the first alpha public testing. This page provides a how to guide, explaining steps to install and run NymVPN CLI and GUI on the Nym Sandbox testnet.
NymVPN is a client that uses [Nym Mixnet](https://nymtech.net) to anonymise users entire internet traffic.
The default is to run in 5-hop mode:
```
┌─►mix──┐ mix mix
│ │
Entry │ │ Exit
client ───► Gateway ──┘ mix │ mix ┌─►mix ───► Gateway ───► internet
│ │
│ │
mix └─►mix──┘ mix
```
Users can switch to 2-hop only mode, which is a faster but less private option. In this mode traffic is only sent between the two Gateway's, and is not passed between Mix Nodes.
The client can optionally do the first connection to the entry gateway using Wireguard. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device.
```admonish warning
NymVPN is an experimental software and it's for [testing](./nym-vpn.md#testing) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the event), and follow the steps listed in the form [*NymVPN User research at 37c3*](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko).
```
## Goals
This alpha testing will help:
* Stabilise NymVPN client
* Understand NymVPN client behavior in various setups (OS, connectivity, etc.)
* Stabilize the VPN infrastructure and improve its reliability / speed / features (e.g. IPv6 support)
* Load test the network in Sandbox environment and identify / anticipate potential weaknesses
```admonish info
Our alpha testing round is done live with some participants at CCC 2023. This guide will not work for everyone, as the NymVPN binaries aren't publicly accessible yet. Note that this setup of Nym testnet Sandbox environment is limited for CCC 2023 event and some of the configurations will not work in the future.
```
> **If you commit to test NymVPN aplha, please start with the [user research form](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko) where all the steps will be provided**.
## Preparation
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
We have CLI and UI binaries available for Linux (Debian based) and macOS.
![](images/image1.png)
* Visit the [releases page](https://github.com/nymtech/nym/releases/tag/ccc) to download the binary for your system.
* Open terminal in the same directory and make executable by running:
```sh
# for CLI
chmod +x ./nym-vpn-cli
# for GUI
chmod +x ./nym-vpn_0.0.0_amd64.AppImage
# make sure your path to package is correct and the package name as well
```
* If you prefer to use the `.deb` version for installation (Linux only), open terminal in the same directory and run:
```
sudo dpkg -i ./<PACKAGE_NAME>.deb
# or
sudo apt-get install -f ./<PACKAGE_NAME>.deb
```
* For CLI: Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries. In case of GUI setup, see the steps below.
### GUI configuration
* Create a NymVPN config directory called `nym-vpn` in your `~/.config`, either manually or by a command:
```sh
# for Linux
mkdir $HOME/.config/nym-vpn/
# for macOS
mkdir $HOME/Library/Application Support/nym-vpn/
```
* Create the network config by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the directory `nym-vpn` you just created
* Create the main config file called `config.toml` in the same directory with this content:
```toml
# for Linux
env_config_file = "/home/<USER>/.config/nym-vpn/sandbox.env" # change <USER> to your username
entry_node_location = "DE" # two letters country code
# for macOS
env_config_file = "$HOME/Library/Application Support/nym-vpn/"
entry_node_location = "DE" # two letters country code
```
## Running
***For NymVPN to work, all other VPNs must be switched off!***
**Note:** At this alpha stage of NymVPN, network connection (wifi) must be re-connected after or in-between NymVPN testing rounds.
* Get your private key for wireguard setup [here](https://nymvpn.com/en/37c3)
* See a JSON list of all Gateways [here](https://nymvpn.com/en/ccc/api/gateways)
* As NymVPN need a permission for network settings, it is run with `sudo` root command
### CLI
Make sure your terminal is open in the same directory as your `nym-vpn-cli` binary.
```admonish info
The entire command syntax with all the needed arguments' values and your private key for testing purposes can be found on our event webpage [nymvpn.com/en/37c3](https://nymvpn.com/en/37c3).
```
Running a help command:
```sh
./nym-vpn-cli --help
```
~~~admonish example collapsible=true title="Console output"
```
Usage: nym-vpn-cli [OPTIONS]
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file describing the network
--mixnet-client-path <MIXNET_CLIENT_PATH>
Path to the data directory of a previously initialised mixnet client, where the keys reside
--entry-gateway-id <ENTRY_GATEWAY_ID>
Mixnet public ID of the entry gateway
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
Auto-select entry gateway by country ISO
--exit-router-address <EXIT_ROUTER_ADDRESS>
Mixnet recipient address
--exit-gateway-id <EXIT_GATEWAY_ID>
--exit-router-country <EXIT_ROUTER_COUNTRY>
Mixnet recipient address
--enable-wireguard
Enable the wireguard traffic between the client and the entry gateway
--private-key <PRIVATE_KEY>
Associated private key
--wg-ip <WG_IP>
The IP address of the wireguard interface
--ip <IP>
The IP address of the TUN device
--mtu <MTU>
The MTU of the TUN device
--disable-routing
Disable routing all traffic through the VPN TUN device
--enable-two-hop
Enable two-hop mixnet traffic. This means that traffic jumps directly from entry gateway to exit gateway
--enable-poisson-rate
Enable Poisson process rate limiting of outbound traffic
-h, --help
Print help
-V, --version
Print version
```
~~~
To run the CLI a few things need to be specified:
```sh
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
```
### Options Flags
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to adjusted by users:
- `-c` is a path to the [Sandbox config](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) file saved as `sandbox.env`
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`) from [here](https://nymvpn.com/en/ccc/api/gateways)
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`) from here [here](https://nymvpn.com/en/ccc/api/gateways)
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/37c3)
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/37c3)
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
**More advanced options**
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
### GUI
If you installed the `.deb` package you may be able to have a NymVPN application icon in your app menu. However this may not work as the application needs root permission.
Make sure you went through the GUI configuration in the [preparation section](./nym-vpn.md#gui-configuration). Then open terminal in the same directory where you [installed](./nym-vpn.md#preparation) the binary run:
```sh
sudo -E ./nym-vpn_0.0.0_amd64.AppImage
# In case you use another binary, just add the correct name as:
sudo -E ./<FULL_BINARY_NAME>
```
In case of errors, see [troubleshooting section](./nym-vpn.md#macos-alert-on-nymvpn-ui-startup).
## Testing
One of the main aims of the demo is testing; your results will help us to make NymVPN robust and stabilise both the client and the network through provided measurements.
1. Create a directory called `nym-vpn-tests` and copy your `nym-vpn-cli` binary and `sandbox.env` to that directory
2. Copy the [block below](./nym-vpn.md#testssh) and save it to the same folder as `tests.sh`
3. Open terminal in the same directory
4. Turn off any existing VPN's (including the NymVPN instances) and run `sudo sh ./tests.sh`.
5. In case of errors, see [troubleshooting section](./nym-vpn.md#missing-jq-error) below
6. The script will print a JSON view of existing Gateways and prompt you to. **Make sure to use two different Gateways for entry and exit***
- `enter a gateway ID`: paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
- `enter an exit address`: paste one of the values labeled with a key `"address"` printed above (without `" "`)
7. Note that the testing script doesn't print many logs, in case of doubts you can check logs in the log file `temp_log.txt` located in the same directory.
8. The script shall run the tests and generate a folder called `tests_<LONG_STRING>` and files `perf_test_results.log` or `two_hop_perf_test_results.log` as well as some temp files. This is how the directory structure will look like:
```sh
nym-vpn-tests
├── tests.sh
├── nym-vpn-cli
├── sandbox.env
├── perf_test_results.log
├── tests_<LONG_STRING>
│   ├── api_response_times.txt
│   ├── download_time.txt
│   └── ping_results.txt
├── timeout
└── two_hop_perf_test_results.log
```
9. In case of errors, see [troubleshooting section](./nym-vpn.md#missing-jq-error) below
10. When the tests are finished, remove the `nym-vpn-cli` binary from the folder and compress it as `nym-vpn-tests.zip`
11. Upload this compressed file to the [questionnaire](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko) upload field when prompted.
#### tests.sh
```sh
#!/bin/bash
ENDPOINT="https://sandbox-nym-api1.nymtech.net/api/v1/gateways/described"
json_array=()
data=$(curl -s "$ENDPOINT" | jq -c '.[] | {host: .bond.gateway.host, hostname: .self_described.host_information.hostname, identity_key: .bond.gateway.identity_key, exitGateway: .self_described.ip_packet_router.address}')
while IFS= read -r entry; do
host=$(echo "$entry" | jq -r '.host')
hostname=$(echo "$entry" | jq -r '.hostname')
identity_key=$(echo "$entry" | jq -r '.identity_key')
exit_gateway_address=$(echo "$entry" | jq -r '.exitGateway // empty')
valid_ip=$(echo "$host")
if [ -n "$exit_gateway_address" ]; then
exit_gateway="{\"address\": \"${exit_gateway_address}\"}"
else
exit_gateway="{}"
fi
if [[ $valid_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
country_info=$(curl -s "http://ipinfo.io/${valid_ip}/country" | tr -d '\n')
country_info_escaped=$(echo "$country_info" | tr -d '\n' | jq -aRs . | tr -d '"')
else
country_info_escaped=""
fi
json_object="{\"hostname\": \"${hostname}\", \"identityKey\": \"${identity_key}\", \"exitGateway\": ${exit_gateway}, \"location\": \"${country_info_escaped}\"}"
json_array+=("$json_object")
done < <(echo "$data")
if [ $? -ne 0 ]; then
echo "Error fetching data from endpoint"
exit 1
fi
download_file() {
local file_url=$1
local output_file=$2
local time_file=$3
echo "starting download speed test..."
local start_time=$(date +%s)
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
wget -O $output_file $file_url
elif [[ "$OSTYPE" == "darwin"* ]]; then
curl -o $output_file $file_url
fi
local end_time=$(date +%s)
local elapsed_time=$((end_time - start_time))
echo "download speed test completed in $elapsed_time seconds." >"$time_file"
}
if ! command -v jq &>/dev/null; then
echo "jq is not installed. Please install jq to proceed."
exit 1
fi
temp_log_file="temp_log.txt"
perform_tests() {
local gateway_id=$1
local exit_address=$2
local test_directory="tests_${gateway_id}_${exit_address}"
local file_url="http://ipv4.download.thinkbroadband.com/2MB.zip"
mkdir -p "$test_directory"
local ping_results_file="${test_directory}/ping_results.txt"
local download_time_file="${test_directory}/download_time.txt"
local api_response_file="${test_directory}/api_response_times.txt"
# ping test
echo "starting ping test..."
for site in google.com youtube.com facebook.com baidu.com wikipedia.org amazon.com twitter.com instagram.com yahoo.com ebay.com netflix.com; do
ping -c 4 $site >>"$ping_results_file"
done
echo "ping test completed. Results saved in $ping_results_file"
# download speed test
download_file $file_url /dev/null "$download_time_file"
# api test
local api_endpoint="https://validator.nymtech.net/api/v1/mixnodes"
local iterations=10
>"$api_response_file"
for i in $(seq 1 $iterations); do
local start_time=$(date +%s)
local response=$(curl -s -o /dev/null -w '%{http_code}' $api_endpoint)
local end_time=$(date +%s)
local elapsed_seconds=$((end_time - start_time))
local hours=$((elapsed_seconds / 3600))
local minutes=$(((elapsed_seconds % 3600) / 60))
local seconds=$((elapsed_seconds % 60))
local human_readable_time=$(printf "%02dh:%02dm:%02ds" $hours $minutes $seconds)
echo "iteration $i: response Time = ${human_readable_time}, status code = $response" >>"$api_response_file"
done
echo "api response test completed. Results saved in $api_response_file."
}
echo $data | jq .
read -p "enter a gateway ID: " identity_key
read -p "enter an exit address: " exit_address
# starting nymVpn
sudo ./nym-vpn-cli-test -c sandbox.env --entry-gateway-id "$identity_key" --exit-router-address "$exit_address" --enable-two-hop >"$temp_log_file" 2>&1 &
timeout=15
start_time=$(date +%s)
while true; do
current_time=$(date +%s)
if grep -q "received plain" "$temp_log_file"; then
echo "successful configuration with identity_key: $identity_key and exit address: $exit_address" >>perf_test_results.log
perform_tests "$identity_key" "$exit_address"
break
fi
if ((current_time - start_time > timeout)); then
echo "failed to connect with identity_key: $identity_key using the exit address: $exit_address" >>perf_test_results.log
break
fi
sleep 1
done
echo "terminating nym-vpn-cli..."
pkill -f './nym-vpn-cli'
sleep 5
rm -f "$temp_log_file"
```
## Troubleshooting
#### Thread `main` panicked
If you see a message like:
```sh
thread 'main' panicked at /Users/runner/.cargo/git/checkouts/mullvadvpn-app-a575cf705b5dfd76/ccfbaa2/talpid-routing/src/unix.rs:301:30:
```
Restart your wifi connection and start again.
#### macOS alert on NymVPN UI startup
If you are running NymVPN on mac OS for the first time, you may see this alert message:
![](images/image3.png)
1. Head to System Settings -> Privacy & Security and click `Allow anyway`
![](images/image5.png)
2. Confirm with your password or TouchID
3. Possibly you may have to confirm again upon running the application
#### Missing `jq` error
In case of missing `jq` on Linux (Debian) install it with:
```sh
# Linux (Debian)
sudo apt-get install jq
# macOS
brew install jq
```
On some Linux distributions however the [script](./nym-vpn.md#testssh) returns `jq` error even if your system claims that `jq is already the newest version`.
In that case, comment the `jq` check in the script as follows:
```sh
#if ! command -v jq &>/dev/null; then
# echo "jq is not installed. Please install jq to proceed."
# exit 1
#fi
```
#### Error current_time: not found
When running `sudo sh ./test.sh` you may see an error like: `93: current_time: not found`. This has something to do with the `current_time` setup of your system and on itself shall not have a negative impact on the test. It has nothing to do with the client at all as it only relates to the code in our testing script.
#### Not connecting to the endpoint
In case the automatic download of all the Gateways fail (and it shouldn't), you do an easy manual work around:
1. Open the list of Gateways created by API [here](https://nymvpn.com/en/ccc/api/gateways)
2. On top click on `JSON` option (shall be default view) and `Save`
3. Save it as `data.json` to the `nym-vpn-tests` folder
4. Replace line 3 in the [script `tests.sh`](./nym-vpn.md#testssh) with:
```sh
NEW_ENDPOINT="http://localhost:8000/data.json"
```
5. In a new terminal window run:
```sh
python3 -m http.server 8000
```
6. Continue with the steps listed in [testing section](./nym-vpn.md#testing)

Some files were not shown because too many files have changed in this diff Show More