zeronet-crypto
Version:
Various cryptographic functions for ZeroNet
225 lines (209 loc) • 5.09 kB
JavaScript
const forge = require('node-forge')
const debug = require('debug')
const log = debug('zeronet-crypto:gen')
const pki = forge.pki
const openssl = require('./openssl')
const attrs = [{
name: 'commonName',
value: 'Example Company'
}, {
name: 'countryName',
value: 'US'
}, {
shortName: 'ST',
value: 'Virginia'
}, {
name: 'localityName',
value: 'New York'
}, {
name: 'organizationName',
value: 'Example, LLC'
}, {
shortName: 'OU',
value: 'Test'
}]
const certSet = [{
name: 'basicConstraints',
cA: true
}, {
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
}, {
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
emailProtection: true,
timeStamping: true
}, {
name: 'nsCertType',
client: true,
server: true,
email: true,
objsign: true,
sslCA: true,
emailCA: true,
objCA: true
}, {
name: 'subjectAltName',
altNames: []
}, {
name: 'subjectKeyIdentifier'
}]
function validate (what, fields) {
if (!what) throw new Error('Invalid! Value is false!')
fields.forEach(field => {
const v = what[field]
if (!v || !v.trim()) throw new Error('Invalid! Field ' + field + ' is empty!')
if (typeof v !== 'string') throw new Error('Invalid! Field ' + field + ' is not a string!')
})
return what
}
function rsa () { // x509 2k rsa cert
// generate a keypair and create an X.509v3 certificate
var keys = pki.rsa.generateKeyPair(2048)
var cert = pki.createCertificate()
cert.publicKey = keys.publicKey
// NOTE: serialNumber is the hex encoded value of an ASN.1 INTEGER.
// Conforming CAs should ensure serialNumber is:
// - no more than 20 octets
// - non-negative (prefix a '00' if your value starts with a '1' bit)
cert.serialNumber = '01'
cert.validity.notBefore = new Date()
cert.validity.notAfter = new Date()
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1)
cert.setSubject(attrs)
cert.setIssuer(attrs)
cert.setExtensions(certSet)
// self-sign certificate
cert.sign(keys.privateKey)
// convert a Forge certificate to PEM
var pem = pki.certificateToPem(cert)
return validate({
cert: Buffer.from(pem).toString('hex'),
key: Buffer.from(pki.privateKeyToPem(keys.privateKey)).toString('hex')
}, ['cert', 'key'])
}
function rsaFast () { // openssl is A LOT faster
try {
return validate(openssl.x509(), ['cert', 'key'])
} catch (e) {
log('rsaFast fail, use default')
log(e)
try {
return rsa()
} catch (en) {
throw e
}
}
}
const cp = require('child_process')
let w, q
const Queue = require('data-queue')
function spawnWorker (cb) {
log('spawning worker')
const env = Object.assign({}, process.env)
env.IS_TLS_GEN_WORKER = 1
w = cp.fork(__filename, {
env,
stdio: 'inherit'
})
w.once('message', m => cb(!m.ready)) // eslint-disable-line standard/no-callback-literal
}
function doTask (t) {
return cb => {
if (!q) {
q = Queue()
spawnWorker(err => {
q.error(err)
if (err) {
q = null
return cb(err)
}
function gloop () {
const t = setTimeout(() => {
q.error(true)
w.send({
type: 'die'
})
w = null
q = null
}, 1000)
q.get((e, d) => {
if (e) return
clearTimeout(t)
w.send({
type: 'gen',
key: d.t
})
log('sending work to worker')
w.once('message', m => {
log('work finished')
if (m.r) {
for (var p in m.r) {
m.r[p] = Buffer.from(m.r[p], 'hex')
}
d.cb(null, m.r)
} else if (m.err) {
const e = new Error('Keygen failed')
e.stack = m.err
d.cb(e)
}
gloop()
})
})
}
gloop()
log('adding task for %s to new worker', t)
if (q.append({
cb,
t
})) return cb(q.append())
})
} else {
log('adding task for %s to existing worker', t)
if (q.append({
cb,
t
})) return cb(q.append())
}
}
}
if (!process.env.IS_TLS_GEN_WORKER) {
module.exports.rsa = doTask('rsa')
} else {
const types = openssl.supported() ? {
rsa,
rsaFast
} : {
rsa
}
process.send({
ready: true
})
process.on('message', (msg) => {
if (msg.type === 'die') {
log('worker: exiting')
process.exit(0)
} else if (msg.type === 'gen') {
if (types[msg.key + 'Fast']) msg.key += 'Fast'
log('worker: doing work %s', msg.key)
try {
process.send({
r: types[msg.key]()
})
log('worker: work success')
} catch (e) {
log('worker: work failed')
process.send({
e: e.stack
})
}
}
})
}