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