Files: 6a055e1be48956691b060c261266f3d9e41250d9 / tmpl / more / protocols / private-box.html.js
7991 bytesRaw
1 | var page = require('../../page.part') |
2 | var com = require('../../com.part') |
3 | |
4 | module.exports = () => page({ |
5 | title: 'Private Box', |
6 | section: 'more', |
7 | tab: 'more-protocols', |
8 | path: '/more/protocols/private-box.html', |
9 | content: ` |
10 | <h2>Private Box</h2> |
11 | <p> |
12 | Private-box is a format for encrypting a private message to many parties. |
13 | You can <strong><a href="https://github.com/auditdrivencrypto/private-box">find the repository on GitHub</a></strong>. |
14 | </p> |
15 | |
16 | <h2 id="properties">Properties</h2> |
17 | <p> |
18 | This protocol was designed for use with secure-scuttlebutt. |
19 | In this place, messages are placed in public, and the sender is known via a signature, |
20 | but we can hide the recipient and the content. |
21 | </p> |
22 | |
23 | <h4 id="-recipients-are-hidden-">Recipients are hidden.</h4> |
24 | <p> |
25 | An eaves-dropper cannot know the recipients or their number. |
26 | Since the message is encrypted to each recipient, and then placed in public, |
27 | to receive a message you will have to decrypt every message posted. |
28 | This would not be scalable if you had to decrypt every message on the internet, |
29 | but if you can restrict the number of messages you might have to decrypt, |
30 | then it's reasonable. For example, if you frequented a forum which contained these messages, |
31 | then it would only be a reasonable number of messages, and posting a message would only |
32 | reveal that you where talking to some other member of that forum. |
33 | </p> |
34 | <p>Hiding access to such a forum is another problem that's out of the current scope.</p> |
35 | |
36 | <h4 id="-the-number-of-recipients-are-hidden-">The number of recipients are hidden.</h4> |
37 | <p> |
38 | If the number of recipients was not hidden, then sometimes it would be possible |
39 | to deanonymise the recipients, if there was a large group discussion with |
40 | an unusual number of recipients. Encrypting the number of recipients means that, |
41 | when a message is not encrypted to you, you will attempt to decrypt same number of times |
42 | as the maximum recipients. |
43 | </p> |
44 | |
45 | <h4 id="-a-valid-recipient-does-not-know-the-other-recipients-">A valid recipient does not know the other recipients.</h4> |
46 | <p> |
47 | A valid recipient knows the number of recipients but not who they are. |
48 | This is more a sideeffect of the design than an intentional design element. |
49 | The plaintext contents may reveal the recipients, if needed. |
50 | </p> |
51 | |
52 | <h4 id="-by-providing-the-key-for-a-message-an-outside-party-could-decrypt-the-message-">By providing the key for a message, an outside party could decrypt the message.</h4> |
53 | <p> |
54 | When you tell someone a secret you must trust them not to reveal it. |
55 | Anyone who knows the key could reveal that to some other party who could then read the message content, |
56 | but not the recipients (unless the sender revealed the ephemeral secret key). |
57 | </p> |
58 | |
59 | <h2 id="assumptions">Assumptions</h2> |
60 | <p> |
61 | Messages will be posted in public, so that the sender is likely to be known, |
62 | and everyone can read the messages. (This makes it possible to hide the recipient, |
63 | but probably not the sender.) |
64 | </p> |
65 | <p>Resisting traffic analysis of the timing or size of messages is out of scope of this spec.</p> |
66 | |
67 | <h2 id="prior-art">Prior Art</h2> |
68 | <h4 id="-pgp-">PGP</h4> |
69 | <p> |
70 | In PGP the recipient, the sender, and the subject are sent as plaintext. |
71 | If the recipient is known, then the metadata graph of who is communicating with who can be read, |
72 | which, since it is easier to analyze than the content, is important to protect. |
73 | </p> |
74 | <h4 id="-sodium-seal-">Sodium seal</h4> |
75 | <p> |
76 | The Sodium library provides a <em>seal</em> function that generates an ephemeral keypair, |
77 | derives a shared key to encrypt a message, and then sends the ephemeral public key and the message. |
78 | The recipient is hidden, and it is forward secure if the sender throws out the ephemeral key. |
79 | However, it's only possible to have one recipient. |
80 | </p> |
81 | |
82 | <h4 id="-minilock-">Minilock</h4> |
83 | <p> |
84 | Minilock uses a similar approach to <code>private-box</code> but does not hide the |
85 | number of recipients. In the case of a group discussion where multiple rounds |
86 | of messages are sent to everyone, this may enable an eavesdropper to deanonymize |
87 | the participiants of a discussion if the sender of each message is known. |
88 | </p> |
89 | |
90 | <h2 id="api">API</h2> |
91 | |
92 | <h4 id="-encrypt-plaintext-buffer-recipients-array-curve25519_pk-">encrypt (plaintext Buffer, recipients Array<curve25519_pk>)</h4> |
93 | <p> |
94 | Takes a plaintext buffer of the message you want to encrypt, |
95 | and an array of recipient public keys. |
96 | Returns a message that is encrypted to all recipients |
97 | and openable by them with <code>PrivateBox.decrypt</code>. |
98 | The recipients must be between 1 and 7 items long. |
99 | </p> |
100 | <p> |
101 | The encrypted length will be <code>56 + (recipients.length * 33) + plaintext.length</code> bytes long, |
102 | between 89 and 287 bytes longer than the plaintext. |
103 | </p> |
104 | |
105 | <h4 id="-decrypt-cyphertext-buffer-secretkey-curve25519_sk-">decrypt (cyphertext Buffer, secretKey curve25519_sk)</h4> |
106 | <p> |
107 | Attempt to decrypt a private-box message, using your secret key. |
108 | If you where an intended recipient then the plaintext will be returned. |
109 | If it was not for you, then <code>undefined</code> will be returned. |
110 | </p> |
111 | |
112 | <h2 id="protocol">Protocol</h2> |
113 | <h4 id="-encryption-">Encryption</h4> |
114 | <p>Private-box generates:</p> |
115 | <ul> |
116 | <li><code>ephemeral</code>: an ephemeral curve25519 keypair that will only be used with this message.</li> |
117 | <li><code>body_key</code>: a random key that will be used to encrypt the plaintext body.</li> |
118 | </ul> |
119 | <p> |
120 | First, private-box outputs the ephemeral public key, then multiplies each recipient public key |
121 | with its secret to produce ephemeral shared keys (<code>shared_keys[1..n]</code>). |
122 | Then, private-box concatenates <code>body_key</code> with the number of recipients, |
123 | encrypts that to each shared key, and concatenates the encrypted body. |
124 | </p> |
125 | ${ com.code({ js: ` |
126 | function encrypt (plaintext, recipients) { |
127 | var ephemeral = keypair() |
128 | var nonce = random(24) |
129 | var body_key = random(32) |
130 | var body_key_with_length = concat([ |
131 | body_key, |
132 | recipients.length |
133 | ]) |
134 | return concat([ |
135 | nonce, |
136 | ephemeral.publicKey, |
137 | concat(recipients.map(function (publicKey) { |
138 | return secretbox( |
139 | body_key_with_length, |
140 | nonce, |
141 | scalarmult(publicKey, ephemeral.secretKey) |
142 | ) |
143 | }), |
144 | secretbox(plaintext, nonce, body_key) |
145 | ]) |
146 | }` }) } |
147 | |
148 | <h4 id="-decryption-">Decryption</h4> |
149 | <p> |
150 | <code>private-box</code> takes the nonce and ephemeral public key, |
151 | multiplies that with your secret key, then tests each possible |
152 | recipient slot until it either decrypts a key or runs out of slots. |
153 | If it runs out of slots, the message was not addressed to you, |
154 | so <code>undefined</code> is returned. Else, the message is found and the body |
155 | is decrypted. |
156 | </p> |
157 | ${ com.code({ js: ` |
158 | function decrypt (cyphertext, secretKey) { |
159 | var next = reader(cyphertext) // next() will read |
160 | // the passed N bytes |
161 | var nonce = next(24) |
162 | var publicKey = next(32) |
163 | var sharedKey = salarmult(publicKey, secretKey) |
164 | |
165 | for(var i = 0; i < 7; i++) { |
166 | var maybe_key = next(33) |
167 | var key_with_length = secretbox_open( |
168 | maybe_key, |
169 | nonce, |
170 | sharedKey |
171 | ) |
172 | if (key_with_length) { // decrypted! |
173 | var key = key_with_length.slice(0, 32) |
174 | var length = key_with_length[32] |
175 | return secretbox_open( |
176 | key, |
177 | cyphertext.slice( |
178 | 56 + 33*(length+1), |
179 | cyphertext.length |
180 | ), |
181 | ) |
182 | } |
183 | } |
184 | // this message was not addressed |
185 | // to the owner of secretKey |
186 | return undefined |
187 | }` }) } |
188 | ` |
189 | }) |
190 |
Built with git-ssb-web