├── ECDSA_authentication.png ├── build_instructions.md ├── e2ee-multisig.md └── primary-key-accounts.md /ECDSA_authentication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunchuk-io/docs/0ea63be65155e1617706bbeeea43a5dd3980d42f/ECDSA_authentication.png -------------------------------------------------------------------------------- /build_instructions.md: -------------------------------------------------------------------------------- 1 | # Nunchuk Build Instructions 2 | 3 | ## Release key and signatures 4 | 5 | [Release key](https://keyserver.ubuntu.com/pks/lookup?search=0x8C8ECD3F660CA53CD878792A6E38A462ED2EF525&fingerprint=on&op=index) 6 | 7 | Release signatures are attached to each release at https://github.com/nunchuk-io/nunchuk-desktop/releases. 8 | 9 | ## Verifying builds 10 | 11 | Follow these steps: 12 | 13 | 1. Download the app for your OS (.zip) and the signature file (.asc) into the same directory, then open the Command Line and `cd` into the directory. 14 | 2. Import the public key of our signer. The signing key can be found above. 15 | 16 | `gpg --keyserver keyserver.ubuntu.com --recv-keys 0x8C8ECD3F660CA53CD878792A6E38A462ED2EF525` 17 | 18 | 3. Verify the checksum 19 | 20 | `sha256sum --check SHA256SUMS.asc` 21 | 22 | The output should say "OK" if the checksum is valid for the given file (if you only download the app for one OS, ignore the warnings for the other OSes). 23 | 24 | 25 | 4. Verify the signature 26 | 27 | `gpg --verify SHA256SUMS.asc` 28 | 29 | The output should say "Good signature" from our signer. 30 | 31 | ## Linux 32 | 33 | On Linux, you will need to install udev rules for the devices to be reachable by [HWI](https://github.com/bitcoin-core/HWI). Get the rules from HWI then run the following command: 34 | 35 | ```Shell 36 | cd hwilib/; \ 37 | sudo cp udev/*.rules /etc/udev/rules.d/ && \ 38 | sudo udevadm trigger && \ 39 | sudo udevadm control --reload-rules && \ 40 | sudo groupadd plugdev && \ 41 | sudo usermod -aG plugdev `whoami` 42 | ``` 43 | 44 | Visit [here](https://github.com/bitcoin-core/HWI/tree/master/hwilib/udev) for more information. 45 | -------------------------------------------------------------------------------- /e2ee-multisig.md: -------------------------------------------------------------------------------- 1 | - [Introduction](#e2ee-for-multi-party-multisig-in-bitcoin) 2 | - [API](#api) 3 | - [Data structures](#data-structures) 4 | - [Workflows](#workflows) 5 | - [Matrix notes](#matrix-notes) 6 | - [Schema](#schema) 7 | - [1. Wallet creation or recovery](#1-wallet-creation-or-recovery) 8 | - [1.1 Init wallet](#11-init-wallet) 9 | - [1.2 Join wallet](#12-join-wallet) 10 | - [1.3 Leave wallet](#13-leave-wallet) 11 | - [1.4 Wallet ready](#14-wallet-ready) 12 | - [1.5 Finalize wallet](#15-finalize-wallet) 13 | - [1.6 Cancel wallet](#16-cancel-wallet) 14 | - [1.7 Delete wallet](#17-delete-wallet) 15 | - [2. Transaction creation](#2-transaction-creation) 16 | - [2.1 Init transaction](#21-init-transaction) 17 | - [2.2 Sign transaction](#22-sign-transaction) 18 | - [2.3 Transaction ready](#23-transaction-ready) 19 | - [2.4 Broadcast transaction](#24-broadcast-transaction) 20 | - [2.5 Cancel transaction](#25-cancel-transaction) 21 | 22 | # E2EE for multi-party multisig in Bitcoin 23 | This document describes how peer-to-peer communication and data exchange for multi-party multisig in Bitcoin can be facilitated using [Matrix](https://matrix.org/). 24 | 25 | Matrix is an open standard and communication protocol for real-time communication. Matrix supports end-to-end-encryption (E2EE) for one-on-one and group chats, thanks to its [Olm and Megolm protocols](https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide). 26 | 27 | Matrix clients that follow the below schema can construct Bitcoin transactions across different Matrix federations. 28 | 29 | The schema is currently used in all [Nunchuk applications](https://nunchuk.io). 30 | 31 | If you have any feedback or questions, please email support@nunchuk.io or join [our Slack](https://join.slack.com/t/nunchukio/shared_invite/zt-xqdlvl5g-xKKohQu_R7IUo7_np8rVaw). 32 | 33 | # API 34 | See https://github.com/nunchuk-io/libnunchuk/blob/main/include/nunchukmatrix.h. 35 | 36 | # Data structures 37 | - Bitcoin wallet configurations are stored in the [BSMS format](https://github.com/bitcoin/bips/blob/master/bip-0129.mediawiki) (built on top of [Output Descriptors](https://bitcoinops.org/en/topics/output-script-descriptors/)). 38 | - Bitcoin transactions are stored in the [PSBT format](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki). 39 | - PSBTs, in turn, are embedded within the [Matrix custom event types](https://spec.matrix.org/v1.4/client-server-api/#types-of-room-events) defined below. 40 | 41 | # Workflows 42 | There are 2 main flows: 43 | - Wallet creation (or recovery) 44 | - Initialize wallet creation/recovery session 45 | - Join wallet 46 | - Leave/cancel wallet 47 | - Finalize wallet 48 | 49 | - Transaction creation 50 | - Initialize transaction 51 | - Cancel or Sign transaction (repeated until enough signatures have been collected) 52 | - Broadcast transaction 53 | 54 | # Matrix notes 55 | - If the Matrix room is E2EE-enabled, all Bitcoin wallet and transaction events will also be end-to-end-encrypted. 56 | - [`m.relates_to`](https://spec.matrix.org/v1.4/client-server-api/#forming-relationships-between-events) is used to establish the relationship graph of events for each workflow. 57 | - Matrix limits the event size to be [<= 64kB (65536 bytes)](https://spec.matrix.org/v1.4/client-server-api/#size-limits). This works for the majority of PSBTs, but not all. 58 | - For extremely large PSBTs (such as Bitcoin transactions with lots of inputs and/or outputs), the PSBTs need to be converted to media files and [uploaded to the Matrix room](https://spec.matrix.org/unstable/client-server-api/#post_matrixmediav3upload). 59 | 60 | # Schema 61 | ## 1. Wallet creation or recovery 62 | ### 1.1 Init wallet 63 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 64 | - Event ID: $5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA 65 | - Sender: @olvaus:nunchuk.io 66 | 67 | ```json 68 | { 69 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 70 | "type": "io.nunchuk.wallet", 71 | "content": { 72 | "msgtype": "io.nunchuk.wallet.init", 73 | "v": 1, 74 | "body": { 75 | "name": "Wallet Name", 76 | "description": "Wallet description (optional)", 77 | "m": 2, 78 | "n": 3, 79 | "address_type": "NATIVE_SEGWIT", // LEGACY, NESTED_SEGWIT or NATIVE_SEGWIT (default NATIVE_SEGWIT) 80 | // address_type could be replaced with script_format (P2WSH, P2WSH-P2SH, P2SH) 81 | "is_escrow": false, 82 | "member": [], // Optional key whitelisting (if not empty, join event key must belong to this list) 83 | "chain": "MAIN", // MAIN, TESTNET or REGTEST (default MAIN) 84 | "bsms": "BSMS 1.0 ..." 85 | } 86 | } 87 | } 88 | ``` 89 | 90 | ### 1.2 Join wallet 91 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 92 | - Event ID: $maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM 93 | - Sender: @olvaus:nunchuk.io (@liz:nunchuk.io, @niro:nunchuk.io) 94 | 95 | ```json 96 | { 97 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 98 | "type": "io.nunchuk.wallet", 99 | "content": { 100 | "msgtype": "io.nunchuk.wallet.join", 101 | "v": 1, 102 | "body": { 103 | "key": "[1cf0bf7e/48'/0'/0'/2']xpub6FL8FhxNNUVnG64YurPd16AfGyvFLhh7S2uSsDqR3Qfcm6o9jtcMYwh6DvmcBF9qozxNQmTCVvWtxLpKTnhVLN3Pgnu2D3pAoXYFgVyd8Yz", // KEY format follow https://github.com/bitcoin/bips/blob/master/bip-0129.mediawiki#signer-1 104 | // If init_event.content.body.member != [], member must include key 105 | "type": "HARDWARE", // NFC, HARDWARE, AIRGAP, SOFTWARE or FOREIGN_SOFTWARE 106 | "io.nunchuk.relates_to": { 107 | "init_event": { 108 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 109 | "type": "io.nunchuk.wallet", 110 | "sender": "@olvaus:nunchuk.io", 111 | "ts": 1631596260590, 112 | "event_id": "$5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA", 113 | "content": { 114 | "msgtype": "io.nunchuk.wallet.init", 115 | "v": 1, 116 | "body": { 117 | "name": "Wallet Name", 118 | "description": "Wallet description (optional)", 119 | "m": 2, 120 | "n": 3, 121 | "address_type": "NATIVE_SEGWIT", 122 | "is_escrow": false, 123 | "member": [], 124 | "chain": "MAIN", 125 | "bsms": "BSMS 1.0 ..." 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | } 133 | ``` 134 | 135 | ### 1.3 Leave wallet 136 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 137 | - Event ID: $maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM 138 | - Sender: @olvaus:nunchuk.io (@liz:nunchuk.io, @niro:nunchuk.io) 139 | 140 | ```json 141 | { 142 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 143 | "type": "io.nunchuk.wallet", 144 | "content": { 145 | "msgtype": "io.nunchuk.wallet.leave", 146 | "v": 1, 147 | "body": { 148 | "reason": "", 149 | "io.nunchuk.relates_to": { 150 | "init_event": { 151 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 152 | "type": "io.nunchuk.wallet", 153 | "sender": "@olvaus:nunchuk.io", 154 | "ts": 1631596260590, 155 | "event_id": "$5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA", 156 | "content": { 157 | "msgtype": "io.nunchuk.wallet.init", 158 | "v": 1, 159 | "body": { 160 | "name": "Wallet Name", 161 | "description": "Wallet description (optional)", 162 | "m": 2, 163 | "n": 3, 164 | "address_type": "NATIVE_SEGWIT", 165 | "is_escrow": false, 166 | "member": [], 167 | "chain": "MAIN", 168 | "bsms": "BSMS 1.0 ..." 169 | } 170 | } 171 | }, 172 | "join_event_id": "$maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM" 173 | } 174 | } 175 | } 176 | } 177 | ``` 178 | 179 | ### 1.4 Wallet ready 180 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 181 | - Event ID: $maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM 182 | - Sender: @olvaus:nunchuk.io 183 | 184 | ```json 185 | { 186 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 187 | "type": "io.nunchuk.wallet", 188 | "content": { 189 | "msgtype": "io.nunchuk.wallet.ready", 190 | "v": 1, 191 | "body": { 192 | "io.nunchuk.relates_to": { 193 | "init_event": { 194 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 195 | "type": "io.nunchuk.wallet", 196 | "sender": "@olvaus:nunchuk.io", 197 | "ts": 1631596260590, 198 | "event_id": "$5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA", 199 | "content": { 200 | "msgtype": "io.nunchuk.wallet.init", 201 | "v": 1, 202 | "body": { 203 | "name": "Wallet Name", 204 | "description": "Wallet description (optional)", 205 | "m": 2, 206 | "n": 3, 207 | "address_type": "NATIVE_SEGWIT", 208 | "is_escrow": false, 209 | "member": [], 210 | "chain": "MAIN", 211 | "bsms": "BSMS 1.0 ..." 212 | } 213 | } 214 | }, 215 | "join_ids": [ 216 | "$maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM", 217 | "liz:nunchuk.io_join_event_id", 218 | "niro:nunchuk.io_join_event_id" 219 | ] 220 | } 221 | } 222 | } 223 | } 224 | ``` 225 | 226 | ### 1.5 Finalize wallet 227 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 228 | - Event ID: $maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM 229 | - Sender: @olvaus:nunchuk.io 230 | 231 | ```json 232 | { 233 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 234 | "type": "io.nunchuk.wallet", 235 | "content": { 236 | "msgtype": "io.nunchuk.wallet.create", 237 | "v": 1, 238 | "body": { 239 | "descriptor": "wsh(sortedmulti(1,[59865f44/48'/0'/0'/2']026d15412460ba0d881c21837bb999233896085a9ed4e5445bd637c10e579768ba,[b7044ca6/48'/0'/0'/2']030baf0497ab406ff50cb48b4013abac8a0338758d2fd54cd934927afa57cc2062))#rzx9dffd", 240 | // (?) maybe support descriptor_template with path_restriction later 241 | "first_address": "bc1quqy523xu3l8che3s8vja8n33qtg0uyugr9l5z092s3wa50p8t7rqy6zumf", 242 | "io.nunchuk.relates_to": { 243 | "init_event": { 244 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 245 | "type": "io.nunchuk.wallet", 246 | "sender": "@olvaus:nunchuk.io", 247 | "ts": 1631596260590, 248 | "event_id": "$5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA", 249 | "content": { 250 | "msgtype": "io.nunchuk.wallet.init", 251 | "v": 1, 252 | "body": { 253 | "name": "Wallet Name", 254 | "description": "Wallet description (optional)", 255 | "m": 2, 256 | "n": 3, 257 | "address_type": "NATIVE_SEGWIT", 258 | "is_escrow": false, 259 | "member": [], 260 | "chain": "MAIN", 261 | "bsms": "BSMS 1.0 ..." 262 | } 263 | } 264 | }, 265 | "join_ids": [ 266 | "$maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM", 267 | "liz:nunchuk.io_join_event_id", 268 | "niro:nunchuk.io_join_event_id" 269 | ] 270 | } 271 | } 272 | } 273 | } 274 | ``` 275 | 276 | ### 1.6 Cancel wallet 277 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 278 | - Event ID: $maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM 279 | - Sender: @olvaus:nunchuk.io 280 | 281 | ```json 282 | { 283 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 284 | "type": "io.nunchuk.wallet", 285 | "content": { 286 | "msgtype": "io.nunchuk.wallet.cancel", 287 | "v": 1, 288 | "body": { 289 | "reason": "", 290 | "io.nunchuk.relates_to": { 291 | "init_event": { 292 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 293 | "type": "io.nunchuk.wallet", 294 | "sender": "@olvaus:nunchuk.io", 295 | "ts": 1631596260590, 296 | "event_id": "$5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA", 297 | "content": { 298 | "msgtype": "io.nunchuk.wallet.init", 299 | "v": 1, 300 | "body": { 301 | "name": "Wallet Name", 302 | "description": "Wallet description (optional)", 303 | "m": 2, 304 | "n": 3, 305 | "address_type": "NATIVE_SEGWIT", 306 | "is_escrow": false, 307 | "member": [], 308 | "chain": "MAIN", 309 | "bsms": "BSMS 1.0 ..." 310 | } 311 | } 312 | } 313 | } 314 | } 315 | } 316 | } 317 | ``` 318 | 319 | ### 1.7 Delete wallet 320 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 321 | - Event ID: $maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM 322 | - Sender: @olvaus:nunchuk.io 323 | 324 | ```json 325 | { 326 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 327 | "type": "io.nunchuk.wallet", 328 | "content": { 329 | "msgtype": "io.nunchuk.wallet.delete", 330 | "v": 1, 331 | "body": { 332 | "wallet_id": "5pu4agtf", 333 | "io.nunchuk.relates_to": { 334 | "init_event": { 335 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 336 | "type": "io.nunchuk.wallet", 337 | "sender": "@olvaus:nunchuk.io", 338 | "ts": 1631596260590, 339 | "event_id": "$5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA", 340 | "content": { 341 | "msgtype": "io.nunchuk.wallet.init", 342 | "v": 1, 343 | "body": { 344 | "name": "Wallet Name", 345 | "description": "Wallet description (optional)", 346 | "m": 2, 347 | "n": 3, 348 | "address_type": "NATIVE_SEGWIT", 349 | "is_escrow": false, 350 | "member": [], 351 | "chain": "MAIN", 352 | "bsms": "BSMS 1.0 ..." 353 | } 354 | } 355 | } 356 | } 357 | } 358 | } 359 | } 360 | ``` 361 | 362 | ## 2. Transaction creation 363 | ### 2.1 Init transaction 364 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 365 | - Event ID: $5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA 366 | - Sender: @olvaus:nunchuk.io 367 | 368 | ```json 369 | { 370 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 371 | "type": "io.nunchuk.transaction", 372 | "content": { 373 | "msgtype": "io.nunchuk.transaction.init", 374 | "v": 1, 375 | "body": { 376 | "wallet_id": "", 377 | "memo": "Transaction memo (optional)", 378 | "psbt": "unsigned PSBT encoded in base64", 379 | "fee_rate": 1, 380 | "subtract_fee_from_amount": false, 381 | "chain": "MAIN", // MAIN, TESTNET or REGTEST (default is MAIN) 382 | } 383 | } 384 | } 385 | ``` 386 | 387 | ### 2.2 Sign transaction 388 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 389 | - Event ID: $maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM 390 | - Sender: @olvaus:nunchuk.io (@liz:nunchuk.io, @niro:nunchuk.io) 391 | 392 | ```json 393 | { 394 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 395 | "type": "io.nunchuk.transaction", 396 | "content": { 397 | "msgtype": "io.nunchuk.transaction.sign", 398 | "v": 1, 399 | "body": { 400 | "psbt": "signed PSBT encoded in base64", 401 | "master_fingerprint": "bc4e023d", 402 | "io.nunchuk.relates_to": { 403 | "init_event": { 404 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 405 | "type": "io.nunchuk.transaction", 406 | "sender": "@olvaus:nunchuk.io", 407 | "ts": 1631596260590, 408 | "event_id": "$5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA", 409 | "content": { 410 | "msgtype": "io.nunchuk.transaction.init", 411 | "v": 1, 412 | "body": { 413 | "wallet_id": "", 414 | "memo": "Transaction memo (optional)", 415 | "psbt": "unsigned PSBT encoded in base64", 416 | "fee_rate": 1, 417 | "subtract_fee_from_amount": false, 418 | "chain": "MAIN" 419 | } 420 | } 421 | } 422 | } 423 | } 424 | } 425 | } 426 | ``` 427 | 428 | ### 2.3 Transaction ready 429 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 430 | - Event ID: $maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM 431 | - Sender: @olvaus:nunchuk.io 432 | 433 | ```json 434 | { 435 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 436 | "type": "io.nunchuk.transaction", 437 | "content": { 438 | "msgtype": "io.nunchuk.transaction.ready", 439 | "v": 1, 440 | "body": { 441 | "io.nunchuk.relates_to": { 442 | "init_event": { 443 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 444 | "type": "io.nunchuk.transaction", 445 | "sender": "@olvaus:nunchuk.io", 446 | "ts": 1631596260590, 447 | "event_id": "$5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA", 448 | "content": { 449 | "msgtype": "io.nunchuk.transaction.init", 450 | "v": 1, 451 | "body": { 452 | "wallet_id": "", 453 | "memo": "Transaction memo (optional)", 454 | "psbt": "unsigned PSBT encoded in base64", 455 | "fee_rate": 1, 456 | "subtract_fee_from_amount": false, 457 | "chain": "MAIN" 458 | } 459 | } 460 | }, 461 | "sign_ids": [ 462 | "$maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM", 463 | "liz:nunchuk.io_sign_event_id", 464 | "niro:nunchuk.io_sign_event_id" 465 | ] 466 | } 467 | } 468 | } 469 | } 470 | ``` 471 | 472 | ### 2.4 Broadcast transaction 473 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 474 | - Event ID: $maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM 475 | - Sender: @olvaus:nunchuk.io 476 | 477 | ```json 478 | { 479 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 480 | "type": "io.nunchuk.transaction", 481 | "content": { 482 | "msgtype": "io.nunchuk.transaction.broadcast", 483 | "v": 1, 484 | "body": { 485 | "tx_id": "", 486 | "io.nunchuk.relates_to": { 487 | "init_event": { 488 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 489 | "type": "io.nunchuk.transaction", 490 | "sender": "@olvaus:nunchuk.io", 491 | "ts": 1631596260590, 492 | "event_id": "$5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA", 493 | "content": { 494 | "msgtype": "io.nunchuk.transaction.init", 495 | "v": 1, 496 | "body": { 497 | "wallet_id": "", 498 | "memo": "Transaction memo (optional)", 499 | "psbt": "unsigned PSBT encoded in base64", 500 | "fee_rate": 1, 501 | "subtract_fee_from_amount": false, 502 | "chain": "MAIN" 503 | } 504 | } 505 | }, 506 | "sign_ids": [ 507 | "$maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM", 508 | "liz:nunchuk.io_sign_event_id", 509 | "niro:nunchuk.io_sign_event_id" 510 | ] 511 | } 512 | } 513 | } 514 | } 515 | ``` 516 | 517 | ### 2.5 Cancel transaction 518 | - Room ID: !DPZjhLvVqrnlOtrqAy:matrix.org 519 | - Event ID: $maTWq8emJUHXMIWzOpowzgfKp4jBE-i_edNhOwhWKVM 520 | - Sender: @olvaus:nunchuk.io 521 | 522 | ```json 523 | { 524 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 525 | "type": "io.nunchuk.transaction", 526 | "content": { 527 | "msgtype": "io.nunchuk.transaction.cancel", 528 | "v": 1, 529 | "body": { 530 | "reason": "", 531 | "io.nunchuk.relates_to": { 532 | "init_event": { 533 | "room_id": "!DPZjhLvVqrnlOtrqAy:matrix.org", 534 | "type": "io.nunchuk.transaction", 535 | "sender": "@olvaus:nunchuk.io", 536 | "ts": 1631596260590, 537 | "event_id": "$5NCjFf5HI6yVlgBhBHo2_qAqTZWs6MXKJh3QUPc9AsA", 538 | "content": { 539 | "msgtype": "io.nunchuk.transaction.init", 540 | "v": 1, 541 | "body": { 542 | "wallet_id": "", 543 | "memo": "Transaction memo (optional)", 544 | "psbt": "unsigned PSBT encoded in base64", 545 | "fee_rate": 1, 546 | "subtract_fee_from_amount": false, 547 | "chain": "MAIN" 548 | } 549 | } 550 | } 551 | } 552 | } 553 | } 554 | } 555 | ``` 556 | 557 | 558 | -------------------------------------------------------------------------------- /primary-key-accounts.md: -------------------------------------------------------------------------------- 1 | - [Introduction](#primary-key-accounts) 2 | - [Background](#background) 3 | - [Authentication using ECDSA](#authentication-using-ecdsa) 4 | - [1. Create an identity](#1-create-an-identity) 5 | - [2. Create a nonce](#2-create-a-nonce) 6 | - [3. Sign nonce with private key](#3-sign-nonce-with-private-key) 7 | - [4. Authenticate using signed message](#4-authentication-using-signed-message) 8 | - [4.1 Sign-up](#41-sign-up) 9 | - [4.2 Sign-in](#42-sign-in) 10 | 11 | # Primary Key accounts 12 | Primary Key accounts are one of Nunchuk's authentication mechanisms. It replaces the usual username/password login with one of your Bitcoin public-private key pairs. 13 | 14 | Primary Key accounts are built on Bitcoin Core's [`MessageSign()`](https://github.com/bitcoin/bitcoin/blob/07f2c25d04c39a0074e1d9ee1b24b3e359c8153f/src/util/message.h#L64) API. 15 | 16 | # Background 17 | Email logins have been highly popular as part of an overall Internet architecture for various reasons: 18 | - Email accounts allow for easy correspondence between the users and the service provider, including critical security updates. 19 | - Certain types of content and services are better served using email. 20 | - Emails represent digital capital (a form of Proof-of-Work) and can act as a natural 2FA against hacking and spam. 21 | 22 | Nevertheless, email address data have been frequently targeted, leaked and exploited over the years, especially when service providers have little to no security systems in place. 23 | 24 | Primary Key accounts are our solution to that. It uses public-key cryptography and leverages Bitcoin's native ECDSA signature (it can be updated to Schnorr later) to provide an alternative authentication mechanism. 25 | 26 | If you have any feedback or questions, please email support@nunchuk.io or join [our Slack](https://join.slack.com/t/nunchukio/shared_invite/zt-xqdlvl5g-xKKohQu_R7IUo7_np8rVaw). 27 | 28 | # Authentication using ECDSA 29 | 30 | ![Sequence Diagram](ECDSA_authentication.png) 31 | 32 | ## 1. Create an identity 33 | 34 | # We use bitcoin-cli here, but feel free to use any other tools 35 | > bitcoin-cli getnewaddress 36 | 37 | < 1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN 38 | 39 | ## 2. Create a nonce 40 | 41 | # identity = hash(public_key). In case of bitcoin-cli, we use the address, which was already hashed. 42 | # username is an alias to identity 43 | > GET /account/v1/nonce?identity=${identity}&username=${username} 44 | 45 | < {"nonce":"thisisanonce"} 46 | 47 | ## 3. Sign nonce with private key 48 | 49 | # We use bitcoin-cli here, but feel free to use any other tools 50 | # message should be the combination of username and nonce. eg: ${username}${nonce} 51 | > bitcoin-cli signmessage "${identity}" "${message}" 52 | 53 | ## 4. Authentication using signed message 54 | 55 | ### 4.1 Sign-up 56 | 57 | > POST /account/v1/sign-up 58 | { 59 | "identity": "${identity}", 60 | "username": "${username}", 61 | "nonce": "${nonce}", 62 | "signature": "${signature}" 63 | } 64 | 65 | # Response really depends on the server implementation. But usually, it includes an access_token, token type and expiration time. 66 | < {"access_token": "thisisanaccess_token", "token_type": "Bearer", "expires_in": 3600} 67 | 68 | ### 4.2 Sign-in 69 | 70 | 1. Server looks up where (identity, username) is located in the database 71 | 2. Verify the signature 72 | 73 | ``` 74 | > POST /account/v1/sign-up 75 | { 76 | "identity": "${identity}", 77 | "username": "${username}", 78 | "nonce": "${nonce}", 79 | "signature": "${signature}" 80 | } 81 | 82 | # Response really depends on the server implementation. But usually, it includes an access_token, token type and expiration time. 83 | < {"access_token": "thisisanaccess_token", "token_type": "Bearer", "expires_in": 3600} 84 | ``` 85 | --------------------------------------------------------------------------------