'use strict' const bareCrypto = require('bare-crypto') /** * TweetNaCl defaults to `crypto.randomBytes` (Node) or WebCrypto; Bare/Pear often * has neither at load time, so `randombytes` stays `() => { throw new Error('no PRNG') }`. * X25519 (`nacl.box.keyPair`) needs entropy — wire PRNG before any ssh2 KEX path runs. */ ;(function wireTweetnaclPrng() { try { const nacl = require('tweetnacl') if (typeof nacl.setPRNG !== 'function') return if (typeof bareCrypto.randomFillSync !== 'function') return nacl.setPRNG(function (x, n) { const u8 = x instanceof Uint8Array ? x : new Uint8Array(x.buffer, x.byteOffset, x.byteLength) bareCrypto.randomFillSync(u8.subarray(0, n)) }) } catch { /* optional graph */ } })() const { Ber } = require('asn1') const { Buffer: Asn1Buffer } = require('safer-buffer') const { getCiphers, getHashes } = require('./lists.js') const { wrapSign, wrapVerify } = require('./pem-ed25519.js') const nextTick = typeof queueMicrotask === 'function' ? (fn) => queueMicrotask(fn) : (fn) => setTimeout(fn, 0) /** * Encode Ed25519 raw keys as SPKI / PKCS#8 DER (same layout as Node/OpenSSL RFC 8410), * which bare-ssh2 `lib/keygen.js` parses via `convertKeys`. */ function ed25519RawToDer(pub32, privSeed32) { const pub = Asn1Buffer.from(pub32) const priv = Asn1Buffer.from(privSeed32) const pubWriter = new Ber.Writer() pubWriter.startSequence() pubWriter.startSequence() pubWriter.writeOID('1.3.101.112') pubWriter.endSequence() pubWriter.startSequence(Ber.BitString) pubWriter.writeByte(0x00) pubWriter._ensure(pub.length) pubWriter._buf.set(pub, pubWriter._offset) pubWriter._offset += pub.length pubWriter.endSequence() pubWriter.endSequence() const privWriter = new Ber.Writer() privWriter.startSequence() privWriter.writeInt(0x00, Ber.Integer) privWriter.startSequence() privWriter.writeOID('1.3.101.112') privWriter.endSequence() privWriter.startSequence(Ber.OctetString) privWriter.writeBuffer(priv, Ber.OctetString) privWriter.endSequence() privWriter.endSequence() return { publicKey: pubWriter.buffer, privateKey: privWriter.buffer } } function generateKeyPairSyncBareEd25519(type, options) { const pubEnc = (options && options.publicKeyEncoding) || {} const privEnc = (options && options.privateKeyEncoding) || {} if ( String(type).toLowerCase() !== 'ed25519' || pubEnc.type !== 'spki' || pubEnc.format !== 'der' || privEnc.type !== 'pkcs8' || privEnc.format !== 'der' ) { throw new Error( 'Bare crypto shim: generateKeyPairSync only supports ed25519 with { spki, der } / { pkcs8, der } encodings' ) } const { publicKey, privateKey } = bareCrypto.generateKeyPair('ed25519') const pubRaw = publicKey._key const privRaw = privateKey._key const pub32 = Asn1Buffer.from(pubRaw) let priv32 = Asn1Buffer.from(privRaw) if (priv32.length === 64) priv32 = Asn1Buffer.from(priv32.subarray(0, 32)) if (pub32.length !== 32 || priv32.length !== 32) { throw new Error('Unexpected Ed25519 key material length from bare-crypto') } return ed25519RawToDer(pub32, priv32) } function generateKeyPairSync(type, options) { if (typeof bareCrypto.generateKeyPairSync === 'function') { return bareCrypto.generateKeyPairSync(type, options) } const t = String(type || '').toLowerCase() if (t === 'x25519') { const x = require('./x25519-compat.js') if (options && Object.keys(options).length > 0) { const pubEnc = options.publicKeyEncoding || {} const privEnc = options.privateKeyEncoding || {} if ( pubEnc.type !== 'spki' || pubEnc.format !== 'der' || privEnc.type !== 'pkcs8' || privEnc.format !== 'der' ) { throw new Error( 'Bare crypto shim: x25519 generateKeyPairSync only supports default or spki/pkcs8 der' ) } } return x.generateKeyPairSync() } if (t === 'ed25519') return generateKeyPairSyncBareEd25519(type, options) throw new Error(`Bare crypto shim: generateKeyPairSync not supported for type ${type}`) } function generateKeyPair(type, options, callback) { if (typeof options === 'function') { callback = options options = undefined } const native = bareCrypto.generateKeyPair if (typeof callback === 'function') { if (typeof native === 'function' && native.length >= 3) { return native.call(bareCrypto, type, options, callback) } nextTick(() => { try { const pair = generateKeyPairSync(type, options) callback(null, pair.publicKey, pair.privateKey) } catch (err) { callback(err) } }) return } if (typeof native === 'function') { return native.call(bareCrypto, type, options) } } function createPublicKey(descriptor) { if ( descriptor && descriptor.type === 'spki' && descriptor.format === 'der' && descriptor.key ) { return require('./x25519-compat.js').createPublicKeyFromSpkiDer(descriptor.key) } if (typeof bareCrypto.createPublicKey === 'function') { return bareCrypto.createPublicKey(descriptor) } throw new Error('Bare crypto shim: createPublicKey only supports X25519 SPKI DER on Bare') } function diffieHellman(opts) { return require('./x25519-compat.js').diffieHellman(opts) } function createECDH(curveName) { throw new Error( `Bare crypto shim: createECDH(${curveName}) is not implemented; bare-ssh2 should negotiate curve25519-sha256` ) } module.exports = Object.assign({}, bareCrypto, { getCiphers, getHashes, generateKeyPairSync, generateKeyPair, createPublicKey, diffieHellman, createECDH, sign: wrapSign(bareCrypto, bareCrypto.sign.bind(bareCrypto)), verify: wrapVerify(bareCrypto, bareCrypto.verify.bind(bareCrypto)) })