Commit 004d7cab authored by Aral Balkan's avatar Aral Balkan
Browse files

Initial add

parents
Pipeline #970 canceled with stages
const { SodiumPlus, X25519PublicKey } = require('sodium-plus')
let sodium
// In this spike, I’m hard-coding the public encryption key.
// Normally, we would read it in from the place’s configuration.
const publicEncryptionKey = new X25519PublicKey(Buffer.from('101e27f87e7b2529154fbc9828d552b829ae7d0bd9b3d64c4f435b79f4481048', 'hex'))
console.log('>>>>', publicEncryptionKey.toString('hex'))
module.exports = async (request, response) => {
// Initialise Sodium Plus if necessary.
if (!sodium) sodium = await SodiumPlus.auto()
// Generate a new private path fragment. This is the
// hexadecimal representation of a 32-byte random buffer.
const randomBuffer = await sodium.randombytes_buf(32)
const unecryptedPrivateSocketPathFragment = randomBuffer.toString('hex')
console.log('Unencrypted secret path', unecryptedPrivateSocketPathFragment)
// Next, we encrypt it using the person’s public encryption key.
// Since this is over a TLS connection, we don’t need to prove our
// identity so a sealed box will suffice.
const encryptedPrivateSocketPathFragment = (await sodium.crypto_box_seal(unecryptedPrivateSocketPathFragment, publicEncryptionKey)).toString('hex')
console.log('Encrypted secret path', encryptedPrivateSocketPathFragment)
response.json({
encryptedPrivateSocketPathFragment
})
}
node_modules
This diff is collapsed.
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Client</title>
</head>
<body>
<h1>I am a clien, woohoo!</h1>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Sign in spike</title>
<script type='module' src='index.js'></script>
</head>
<body>
<h1>My site</h1>
<nav>
<ul>
<li><a href='/#/'>Home</a></li>
<li><a href='/#/about'>About</a></li>
<li><a href='/#/private'>Private</a></li>
</ul>
</nav>
<section id='/'>
<h2>Home</h2>
<p>This is the home.</p>
</section>
<section id='/about' hidden>
<h2>About</h2>
<p>This is About.</p>
</section>
<section id='/sign-in' hidden>
<h2>Sign in</h2>
<form id='signInForm' name='signInForm'>
<label>Password</label>
<input name='passphrase' type='password'></input>
<button>Sign in</button>
</form>
</section>
<section id='/private' hidden>
<h2>Private</h2>
<p>This is private.</p>
</section>
</body>
</html>
import session25519 from 'session25519'
import blake from 'blakejs'
import nacl from 'tweetnacl'
import naclUtil from 'tweetnacl-util'
import sealedBox from 'tweetnacl-sealedbox-js'
let signedIn = false
let secretPath = null
// Add sealed box functionality to TweetNaCl.
nacl.sealedBox = sealedBox
window.addEventListener('load', async () => {
// Get the public keys (we will be using them later if the
// person wants to sign in to the server).
const publicKeysResponse = await fetch('/keys')
const publicKeys = await publicKeysResponse.json()
const publicEncryptionKey = publicKeys.encryption
const signInForm = document.getElementById('signInForm')
signInForm.addEventListener('submit', async event => {
event.preventDefault()
console.log(signInForm.elements.passphrase.value)
// Get the encrypted private socket path fragment.
const privateSocketResponse = await fetch('/private-socket')
const privateSocket = await privateSocketResponse.json()
const encryptedPrivateSocketPathFragment = privateSocket.encryptedPrivateSocketPathFragment
console.log(encryptedPrivateSocketPathFragment)
const passphrase = signInForm.elements.passphrase.value
// Regenerate the private key.
// Note: hostname is hardcoded in this example!
const blake2bHashOfHostnameAsSalt = blake.blake2bHex('sign-in.small-web.org')
console.log(blake2bHashOfHostnameAsSalt)
const keys = await generateKeys(blake2bHashOfHostnameAsSalt, passphrase)
console.log(keys)
const secretKey = keys.secretKey.toString('hex')
const publicKey = toHex(keys.publicKey)
console.log('public key', publicKey)
console.log('testUnseal', naclUtil.encodeUTF8(testUnseal))
const sealedBoxOpenResult = nacl.sealedBox.open(hexToUInt8Array(encryptedPrivateSocketPathFragment), keys.publicKey, keys.secretKey)
if (sealedBoxOpenResult === null) {
alert('Wrong passphrase. Please try again.')
return
}
// OK, signed in.
secretPath = naclUtil.encodeUTF8(sealedBoxOpenResult)
console.log('secretPath', secretPath)
signedIn = true
showSection('/private')
})
})
function generateKeys(blake2bHashOfHostnameAsSalt, passphrase){
return new Promise((resolve, reject) => {
session25519(blake2bHashOfHostnameAsSalt, passphrase, function (error, keys) {
if (error) {
console.log('error', error)
return reject(error)
}
resolve(keys)
})
})
}
window.addEventListener('hashchange', () => {
const currentHash = location.hash
const id = currentHash.replace('#', '')
switch (currentHash) {
case '#/private':
if (signedIn) {
showSection(id)
} else {
showSection('/sign-in')
}
break
default:
showSection(id)
break
}
})
function showSection(id) {
document.querySelectorAll('section[id^="/"]').forEach(section => {
section.hidden = section.id !== id
})
}
// Uint8Array to Hex String
// Author: Michael Fabian 'Xaymar' Dirks
// https://blog.xaymar.com/2020/12/08/fastest-uint8array-to-hex-string-conversion-in-javascript/
// Pre-Init
const LUT_HEX_4b = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
const LUT_HEX_8b = new Array(0x100)
for (let n = 0; n < 0x100; n++) {
LUT_HEX_8b[n] = `${LUT_HEX_4b[(n >>> 4) & 0xF]}${LUT_HEX_4b[n & 0xF]}`
}
// End Pre-Init
function toHex(buffer) {
let out = ''
for (let idx = 0, edx = buffer.length; idx < edx; idx++) {
out += LUT_HEX_8b[buffer[idx]]
}
return out
}
// Hex string to Uint8Array
function hexToUInt8Array(string) {
var bytes = new Uint8Array(Math.ceil(string.length / 2));
for (var i = 0; i < bytes.length; i++) bytes[i] = parseInt(string.substr(i * 2, 2), 16);
return bytes
}
{
"name": "sign-in",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"big-integer": {
"version": "1.6.48",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
"integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w=="
},
"blake2b": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/blake2b/-/blake2b-2.1.3.tgz",
"integrity": "sha512-pkDss4xFVbMb4270aCyGD3qLv92314Et+FsKzilCLxDz5DuZ2/1g3w4nmBbu6nKApPspnjG7JcwTjGZnduB1yg==",
"requires": {
"blake2b-wasm": "^1.1.0",
"nanoassert": "^1.0.0"
}
},
"blake2b-wasm": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-1.1.7.tgz",
"integrity": "sha512-oFIHvXhlz/DUgF0kq5B1CqxIDjIJwh9iDeUUGQUcvgiGz7Wdw03McEO7CfLBy7QKGdsydcMCgO9jFNBAFCtFcA==",
"requires": {
"nanoassert": "^1.0.0"
}
},
"blake2s-js": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/blake2s-js/-/blake2s-js-1.3.0.tgz",
"integrity": "sha512-+H4oLkODOSeDfDD11PxaZ3Gqi9mtr0QribQRPkQ+v29ZQ689mK/rk5hUVtEjgh3Vq8c4uHPALV0rWjfC6ygrug=="
},
"blakejs": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz",
"integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U="
},
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"libsodium": {
"version": "0.7.8",
"resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.8.tgz",
"integrity": "sha512-/Qc+APf0jbeWSaeEruH0L1/tbbT+sbf884ZL0/zV/0JXaDPBzYkKbyb/wmxMHgAHzm3t6gqe7bOOXAVwfqVikQ=="
},
"libsodium-wrappers": {
"version": "0.7.8",
"resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.8.tgz",
"integrity": "sha512-PDhPWXBqd/SaqAFUBgH2Ux7b3VEEJgyD6BQB+VdNFJb9PbExGr/T/myc/MBoSvl8qLzfm0W0IVByOQS5L1MrCg==",
"requires": {
"libsodium": "0.7.8"
}
},
"nanoassert": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz",
"integrity": "sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40="
},
"poly1305-js": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/poly1305-js/-/poly1305-js-0.4.3.tgz",
"integrity": "sha512-PZ/bfUvUoIPZ7DwfiEVq0WLZ7scYORkVyUeIFcM6MpeNyE9s6SMz9TUKbYma6k4HOuXZumvu1zvLBdEnku0/UA==",
"requires": {
"big-integer": "^1.6.48"
}
},
"scrypt-async": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/scrypt-async/-/scrypt-async-1.3.1.tgz",
"integrity": "sha1-oR/W+smBtLgj7gHe4CIRaVAN2uk="
},
"session25519": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/session25519/-/session25519-1.1.0.tgz",
"integrity": "sha1-wEc9Y99jpzaTQxi3vtNWGUuCA8o=",
"requires": {
"base64-js": "^1.1.1",
"blake2s-js": "^1.0.3",
"scrypt-async": "^1.2.0",
"tweetnacl": "^0.14.0"
},
"dependencies": {
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
}
}
},
"sodium-plus": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/sodium-plus/-/sodium-plus-0.9.0.tgz",
"integrity": "sha512-WWKxrd81qDL7C1A10yxNmZ135yovEZuIRnZ/BIf/FcajYBupbKbPdgzwlusPHLVxkMDDamcarq9PxxRBUSqpCw==",
"requires": {
"buffer": "^5.6.0",
"libsodium-wrappers": "^0.7.6",
"poly1305-js": "^0.4.2",
"typedarray-to-buffer": "^3.1.5",
"xsalsa20": "^1.1.0"
}
},
"tweetnacl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
},
"tweetnacl-sealedbox-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/tweetnacl-sealedbox-js/-/tweetnacl-sealedbox-js-1.2.0.tgz",
"integrity": "sha512-QoCr8K2hwri+ky7SUa22oSre8g88XaWi0hwwWd16pJMuDyn5gL/UyE0PyR2EOFEMJ70T2trJ9+Sv+Qa18olEmQ==",
"requires": {
"blakejs": "^1.1.0",
"tweetnacl": "^1.0.1"
}
},
"tweetnacl-util": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz",
"integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw=="
},
"typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
"requires": {
"is-typedarray": "^1.0.0"
}
},
"xsalsa20": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/xsalsa20/-/xsalsa20-1.1.0.tgz",
"integrity": "sha512-zd3ytX2cm+tcSndRU+krm0eL4TMMpZE7evs5hLRAoOy6gviqLfe3qOlkjF3i5SeAkQUCeJk0lJZrEU56kHRfWw=="
}
}
}
{
"name": "sign-in",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"blake2b": "^2.1.3",
"blakejs": "^1.1.0",
"session25519": "^1.1.0",
"sodium-plus": "^0.9.0",
"tweetnacl": "^1.0.3",
"tweetnacl-sealedbox-js": "^1.2.0",
"tweetnacl-util": "^0.15.1"
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment