├── .nojekyll ├── robots.txt ├── publish.sh ├── README.md ├── ui.js ├── wireguard.js └── index.html /.nojekyll: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf www 4 | mkdir www 5 | cp index.html *.js www/ 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wireguard Key Generator 2 | 3 | Browser-based, offline-capable, client-side, trustless 4 | 5 | ## Available 6 | 7 | ### IPFS 8 | 9 | These are content-addressed, so you can trust the content does not change. There 10 | are two hashes, one with files stored as UnixFS, and another with files stored 11 | as raw data: 12 | 13 | - [bafybeiguzvr2ntejejphl2uktl726qkjqzpvoguifn4ymellaqjdlvghbq](https://explore.ipld.io/#/explore/bafybeiguzvr2ntejejphl2uktl726qkjqzpvoguifn4ymellaqjdlvghbq) (UnixFS) 14 | 15 | Gateway links: 16 | 17 | - https://flk-ipfs.xyz/ipfs/bafybeiguzvr2ntejejphl2uktl726qkjqzpvoguifn4ymellaqjdlvghbq 18 | - https://ipfs.io/ipfs/bafybeiguzvr2ntejejphl2uktl726qkjqzpvoguifn4ymellaqjdlvghbq 19 | 20 | ### Web 21 | 22 | - https://wg-keygen.carrano.com.ar 23 | - https://jcarrano.github.io/wg-keygen-notrust/ 24 | 25 | Note that this link is not content-addressed, so here you should trust me. 26 | 27 | ## What is this 28 | 29 | This is a website that can generate a Wireguard keypair. The user 30 | is given two files to download: a wireguard config with his private key (and additional parameters), 31 | to load into his VPN software, and a "fragment", which is a `[Peer]` section that he 32 | must send to the server administrator. 33 | 34 | AT NO POINT IS ANY EXTERNAL SERVER CONTACTED. KEYS NEVER LEAVE THE USER'S COMPUTER UNTIL 35 | THEY EXPLICITLY SEND THE FRAGMENT TO THE SEVER ADMIN. 36 | 37 | Compared to other online key generators out there, this one does not require you to 38 | trust me (the author) or the person running the webserver. 39 | 40 | - You don't have to trust me because the code is small and clear. You can review and 41 | audit it from the point where I imported Jason Donenfeld's code through all 42 | the additions I made. 43 | - You do not have to trust the webserver not to tamper with the site because it 44 | is hosted on IPFS, which is [content-addressed](https://en.wikipedia.org/wiki/Content-addressable_storage), 45 | so if the address/hash is the same, the content is the same. 46 | 47 | Alternatively, you could host the site yourself. 48 | 49 | ## Single-file version 50 | 51 | A single-file version with embedded scripts is available under the "single-file" 52 | branch, however it does not work on IPFS gateways due the content-security 53 | policy. 54 | 55 | ## Some additional background 56 | 57 | When setting up a Wireguard VPN server you have two choices: 58 | 59 | - Generate the private keys yourself and send them to users, in which 60 | case the files must be send under a secure (private, authenticated) channel 61 | (i.e. NOT email). 62 | - Users can generate a keypair and send the server admin the public key over 63 | an authenticated (but not necessarily private) channel, in which case they 64 | need to have the technical know how to use the command line tools and to 65 | build a configuration file. 66 | 67 | This website allows users to generate their own keys and configuration files with 68 | no special knowledge required.. 69 | 70 | ### Instructions 71 | 72 | The VPN admin fills in the form fields and clicks "save", which generates a URL 73 | with the parameters saved inside the query string. Upon opening this URL the form will 74 | be pre-filled. 75 | 76 | The admin sends the URL to users, which generate the keys and send the "Server 77 | Fragment" back to the admin. An email address can optionally be specified, 78 | which will enable a "mailto" link to directly mail the fragment back to the 79 | admin. 80 | 81 | ## License 82 | 83 | This code is licensed under the GNU GPLv2, since that was the license of the 84 | original JS code. I would like a more permissive license. 85 | -------------------------------------------------------------------------------- /ui.js: -------------------------------------------------------------------------------- 1 | (new URL(window.location.href)).searchParams.forEach((x, y) => { 2 | let el = document.getElementById(y); 3 | if (el) { 4 | if (el.type == "checkbox") { 5 | el.checked = (x == "on"); 6 | el.dispatchEvent(new Event("change")); 7 | // el.dispatchEvent(new initEvent("change", false, true)); 8 | } 9 | else el.value = x; 10 | } 11 | }); 12 | 13 | // Make the result section disappear when changing relevant input fields 14 | for (var element of document.getElementById("relevant_for_clientconfig").getElementsByTagName("input")) { 15 | document.getElementById(element.id).setAttribute('onbeforeinput', 'getElementById("results").setAttribute("style", "display: none")'); 16 | } 17 | 18 | function genCfg() { 19 | if (document.getElementById("pka").checked) genPsk(); 20 | if (document.getElementById("cna").checked) generateCN(); 21 | let results = document.getElementById('results'); 22 | results.setAttribute("style", ""); 23 | let kp = wireguard.generateKeypair(); 24 | let fd = new FormData(document.getElementById('params')); 25 | let clientcfg = [ 26 | "[Interface]", 27 | "# PublicKey = " + kp.publicKey, 28 | (fd.get("cn") ? "# ClientName = " + fd.get("cn") : ""), 29 | "PrivateKey = " + kp.privateKey, 30 | "Address = " + fd.get("ca"), 31 | (fd.get("dn")) ? ("DNS = " + fd.get("dn")) : "", 32 | (fd.get("mt") > 0) ? ("MTU = " + fd.get("mt")) : "", 33 | "[Peer]", 34 | "Endpoint = " + fd.get("sa") + ":" + fd.get("sp"), 35 | "PublicKey = " + fd.get("sk"), 36 | (fd.get("pk")) ? ("PresharedKey = " + fd.get("pk")) : "", 37 | "AllowedIPs = " + fd.get("aa"), 38 | (fd.get("ka") > 0) ? ("PersistentKeepalive = " + fd.get("ka")) : ""].filter(Boolean).join("\n"); 39 | let client_dl = document.getElementById('client-dl'); 40 | client_dl.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(clientcfg)); 41 | client_dl.setAttribute("download", "wireguard.conf") 42 | let client_sh = document.getElementById('client-sh'); 43 | client_sh.textContent = clientcfg; 44 | 45 | let serverfrag = [ 46 | "[Peer]", 47 | (fd.get("cn") ? "# ClientName = " + fd.get("cn") : ""), 48 | "PublicKey = " + kp.publicKey, 49 | (fd.get("pk")) ? ("PresharedKey = " + fd.get("pk")) : "", 50 | "AllowedIPs = " + fd.get("ca")].filter(Boolean).join("\n"); 51 | let server_dl = document.getElementById('server-dl'); 52 | let server_sh = document.getElementById('server-sh'); 53 | server_sh.textContent = serverfrag; 54 | let encodedfrag = encodeURIComponent(serverfrag); 55 | server_dl.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodedfrag); 56 | server_dl.setAttribute("download", "wg-" + (fd.get("cn") ? fd.get("cn") : "peer") + ".conf") 57 | let server_em = document.getElementById('server-em'); 58 | let subject = "Wireguard server config" + (fd.get("cn") ? " for " + fd.get("cn") : ""); 59 | if (fd.get("ae")) { 60 | to = 'to=' + encodeURIComponent(fd.get("ae")) + '&'; 61 | } 62 | else to = ''; 63 | server_em.setAttribute('href', 64 | 'mailto:?' + to + 'subject=' + encodeURIComponent(subject) + "&body=" + encodedfrag); 65 | 66 | results.setAttribute("style", "display: inherit"); 67 | } 68 | 69 | function genPsk() { 70 | let pkfield = document.getElementById('pk'); 71 | pkfield.value = wireguard.generatePresharedKey(); 72 | pkfield.dispatchEvent(new InputEvent('beforeinput')); 73 | } 74 | 75 | function generateCN() { 76 | d = new Date(); 77 | var datestring = d.getFullYear().toString().substring(2) + "-" + ("0" + (d.getMonth() + 1)).slice(-2) + "-" + ("0" + d.getDate()).slice(-2) + 78 | "_" + ("0" + d.getHours()).slice(-2) + "-" + ("0" + d.getMinutes()).slice(-2) + "-" + ("0" + d.getSeconds()).slice(-2); 79 | let cnfield = document.getElementById('cn'); 80 | cnfield.value = 'user_' + datestring; 81 | cnfield.dispatchEvent(new InputEvent('beforeinput')); 82 | } 83 | 84 | function copycl(text) { 85 | navigator.clipboard.writeText(text).then(function () { 86 | console.log('Async: Copying to clipboard was successful!'); 87 | }, function (err) { 88 | console.error('Async: Could not copy text: ', err); 89 | }); 90 | } 91 | -------------------------------------------------------------------------------- /wireguard.js: -------------------------------------------------------------------------------- 1 | /*! SPDX-License-Identifier: GPL-2.0 2 | * 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | (function() { 7 | function gf(init) { 8 | var r = new Float64Array(16); 9 | if (init) { 10 | for (var i = 0; i < init.length; ++i) 11 | r[i] = init[i]; 12 | } 13 | return r; 14 | } 15 | 16 | function pack(o, n) { 17 | var b, m = gf(), t = gf(); 18 | for (var i = 0; i < 16; ++i) 19 | t[i] = n[i]; 20 | carry(t); 21 | carry(t); 22 | carry(t); 23 | for (var j = 0; j < 2; ++j) { 24 | m[0] = t[0] - 0xffed; 25 | for (var i = 1; i < 15; ++i) { 26 | m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); 27 | m[i - 1] &= 0xffff; 28 | } 29 | m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); 30 | b = (m[15] >> 16) & 1; 31 | m[14] &= 0xffff; 32 | cswap(t, m, 1 - b); 33 | } 34 | for (var i = 0; i < 16; ++i) { 35 | o[2 * i] = t[i] & 0xff; 36 | o[2 * i + 1] = t[i] >> 8; 37 | } 38 | } 39 | 40 | function carry(o) { 41 | var c; 42 | for (var i = 0; i < 16; ++i) { 43 | o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536); 44 | o[i] &= 0xffff; 45 | } 46 | } 47 | 48 | function cswap(p, q, b) { 49 | var t, c = ~(b - 1); 50 | for (var i = 0; i < 16; ++i) { 51 | t = c & (p[i] ^ q[i]); 52 | p[i] ^= t; 53 | q[i] ^= t; 54 | } 55 | } 56 | 57 | function add(o, a, b) { 58 | for (var i = 0; i < 16; ++i) 59 | o[i] = (a[i] + b[i]) | 0; 60 | } 61 | 62 | function subtract(o, a, b) { 63 | for (var i = 0; i < 16; ++i) 64 | o[i] = (a[i] - b[i]) | 0; 65 | } 66 | 67 | function multmod(o, a, b) { 68 | var t = new Float64Array(31); 69 | for (var i = 0; i < 16; ++i) { 70 | for (var j = 0; j < 16; ++j) 71 | t[i + j] += a[i] * b[j]; 72 | } 73 | for (var i = 0; i < 15; ++i) 74 | t[i] += 38 * t[i + 16]; 75 | for (var i = 0; i < 16; ++i) 76 | o[i] = t[i]; 77 | carry(o); 78 | carry(o); 79 | } 80 | 81 | function invert(o, i) { 82 | var c = gf(); 83 | for (var a = 0; a < 16; ++a) 84 | c[a] = i[a]; 85 | for (var a = 253; a >= 0; --a) { 86 | multmod(c, c, c); 87 | if (a !== 2 && a !== 4) 88 | multmod(c, c, i); 89 | } 90 | for (var a = 0; a < 16; ++a) 91 | o[a] = c[a]; 92 | } 93 | 94 | function clamp(z) { 95 | z[31] = (z[31] & 127) | 64; 96 | z[0] &= 248; 97 | } 98 | 99 | function generatePublicKey(privateKey) { 100 | var r, z = new Uint8Array(32); 101 | var a = gf([1]), 102 | b = gf([9]), 103 | c = gf(), 104 | d = gf([1]), 105 | e = gf(), 106 | f = gf(), 107 | _121665 = gf([0xdb41, 1]), 108 | _9 = gf([9]); 109 | for (var i = 0; i < 32; ++i) 110 | z[i] = privateKey[i]; 111 | clamp(z); 112 | for (var i = 254; i >= 0; --i) { 113 | r = (z[i >>> 3] >>> (i & 7)) & 1; 114 | cswap(a, b, r); 115 | cswap(c, d, r); 116 | add(e, a, c); 117 | subtract(a, a, c); 118 | add(c, b, d); 119 | subtract(b, b, d); 120 | multmod(d, e, e); 121 | multmod(f, a, a); 122 | multmod(a, c, a); 123 | multmod(c, b, e); 124 | add(e, a, c); 125 | subtract(a, a, c); 126 | multmod(b, a, a); 127 | subtract(c, d, f); 128 | multmod(a, c, _121665); 129 | add(a, a, d); 130 | multmod(c, c, a); 131 | multmod(a, d, f); 132 | multmod(d, b, _9); 133 | multmod(b, e, e); 134 | cswap(a, b, r); 135 | cswap(c, d, r); 136 | } 137 | invert(c, c); 138 | multmod(a, a, c); 139 | pack(z, a); 140 | return z; 141 | } 142 | 143 | function generatePresharedKey() { 144 | var privateKey = new Uint8Array(32); 145 | window.crypto.getRandomValues(privateKey); 146 | return privateKey; 147 | } 148 | 149 | function generatePrivateKey() { 150 | var privateKey = generatePresharedKey(); 151 | clamp(privateKey); 152 | return privateKey; 153 | } 154 | 155 | function encodeBase64(dest, src) { 156 | var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]); 157 | for (var i = 0; i < 4; ++i) 158 | dest[i] = input[i] + 65 + 159 | (((25 - input[i]) >> 8) & 6) - 160 | (((51 - input[i]) >> 8) & 75) - 161 | (((61 - input[i]) >> 8) & 15) + 162 | (((62 - input[i]) >> 8) & 3); 163 | } 164 | 165 | function keyToBase64(key) { 166 | var i, base64 = new Uint8Array(44); 167 | for (i = 0; i < 32 / 3; ++i) 168 | encodeBase64(base64.subarray(i * 4), key.subarray(i * 3)); 169 | encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0])); 170 | base64[43] = 61; 171 | return String.fromCharCode.apply(null, base64); 172 | } 173 | 174 | window.wireguard = { 175 | generatePresharedKey: function() { 176 | return keyToBase64(generatePresharedKey()); 177 | }, 178 | generateKeypair: function() { 179 | var privateKey = generatePrivateKey(); 180 | var publicKey = generatePublicKey(privateKey); 181 | return { 182 | publicKey: keyToBase64(publicKey), 183 | privateKey: keyToBase64(privateKey) 184 | }; 185 | } 186 | }; 187 | })(); 188 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Wireguard Key Generator (Trustless) 7 | 8 | 9 | 10 | 11 | 12 | 14 | 73 | 74 | 75 | 76 |

Wireguard Key Generator (Trustless)

77 |
78 |

Use this webpage on a private window!!!

79 |
80 |
81 |
82 | Parameters 83 |
84 | 85 | : 86 | :
88 | :
90 | : 91 | 92 | :
94 | :
95 | :
96 | :
98 | :
99 | :
101 | : 102 | 103 | :
105 | :
106 |
107 | 108 |
109 |
110 |
111 |
112 |

Note: for maximum security, close all other browser tabs and all programs before 113 | generating the configuration files

114 | 115 |
116 |
117 |

You must keep the client configuration for yourself and send the 118 | server fragment to the VPN server administrator 119 |

120 |

Client Configuration

121 | 💾 122 | 📋
124 |

125 | 		

Server Fragment

126 | 💾 127 | 📋 129 | 📧
130 |

131 | 		

Note: for maximum security, reboot your computer after you are done

132 |
133 |
134 |
135 | Additional info 136 |

The parameters are usually set by the server administrator and can be saved in URL's query string. Only 137 | the 138 | client address and name need to be changed per client. The client name is optional and serves to 139 | uniquely 140 | label the config fragments sent to the server.

141 |

Filling in "Admin Email" will enable a mailto link.

142 |

If this webpage is not used in private mode, the contents of the client config will be stored in the 143 | browser's history as the download location of the file.

144 |

There are no guarantees that javascript crypto is safe from side-channel attacks and there is no secure 145 | wipe function, that's why it is recommended to close all other tabs before and reboot the machine after. 146 |

147 |

NO WARRANTY EXPRESSED OR IMPLIED!!!!

148 |
149 |
150 |
151 |

This tool © 2022 Juan I Carrano and contributors.
Keygen code © 2015-2020 Jason A. Donenfeld.
152 | This project is not associated with WireGuard®. "WireGuard" and the "WireGuard" logo are registered 153 | trademarks of Jason A. Donenfeld.
154 | GitHub Repository 155 |

156 |
157 | 159 | 160 | 161 | 162 | --------------------------------------------------------------------------------