Files
bare-operating-system/packages/bare-os-openssh/vendor/bare-ssh2/shims/crypto/x25519-compat.js
T

154 lines
4.6 KiB
JavaScript

'use strict'
const { Ber } = require('asn1')
const { Buffer: Asn1Buffer } = require('safer-buffer')
const { Buffer: RuntimeBuffer } = require('buffer')
const X25519_OID = '1.3.101.110'
function getNacl() {
return require('tweetnacl')
}
/** @param {import('safer-buffer').Buffer | Uint8Array} buf */
function u8(buf) {
const b = Asn1Buffer.from(buf)
return new Uint8Array(b.buffer, b.byteOffset, b.byteLength)
}
function berSpkiX25519(pub32) {
const pub = Asn1Buffer.from(pub32)
const w = new Ber.Writer()
w.startSequence()
w.startSequence()
w.writeOID(X25519_OID)
w.endSequence()
w.startSequence(Ber.BitString)
w.writeByte(0x00)
w._ensure(pub.length)
w._buf.set(pub, w._offset)
w._offset += pub.length
w.endSequence()
w.endSequence()
return w.buffer
}
function berPkcs8X25519(priv32) {
const priv = Asn1Buffer.from(priv32)
const w = new Ber.Writer()
w.startSequence()
w.writeInt(0x00, Ber.Integer)
w.startSequence()
w.writeOID(X25519_OID)
w.endSequence()
w.startSequence(Ber.OctetString)
w.writeBuffer(priv, Ber.OctetString)
w.endSequence()
w.endSequence()
return w.buffer
}
/** Match bare-ssh2 kex: SPKI DER ends with 32-byte X25519 public key. */
function parseSpkiX25519PubDer(der) {
const b = Asn1Buffer.from(der)
if (b.length < 32) throw new Error('Invalid X25519 SPKI DER')
return u8(b.slice(-32))
}
function parsePkcs8X25519PrivDer(der) {
const { Ber: BerR } = require('asn1')
let reader = new BerR.Reader(Asn1Buffer.from(der))
reader.readSequence()
if (reader.readInt() !== 0) throw new Error('Invalid PKCS#8 version')
reader.readSequence()
if (reader.readOID() !== X25519_OID) throw new Error('Invalid X25519 PKCS#8 OID')
reader = new BerR.Reader(reader.readString(BerR.OctetString, true))
return u8(reader.readString(BerR.OctetString, true))
}
class X25519PublicKey {
/** @param {Uint8Array | import('safer-buffer').Buffer} pub32 */
constructor(pub32) {
this.type = 'public'
this.asymmetricKeyType = 'x25519'
this._pub = Asn1Buffer.from(pub32)
if (this._pub.length !== 32) throw new Error('X25519 public key must be 32 bytes')
}
/** @param {{ type: string, format: string }} opts */
export(opts) {
if (!opts || opts.format !== 'der') throw new Error('unsupported export format')
if (opts.type === 'spki') return berSpkiX25519(this._pub)
if (opts.type === 'raw') return Asn1Buffer.from(this._pub)
throw new Error('unsupported export type')
}
}
class X25519PrivateKey {
/**
* @param {Uint8Array | import('safer-buffer').Buffer} priv32
* @param {Uint8Array | import('safer-buffer').Buffer | null} pub32
*/
constructor(priv32, pub32) {
this.type = 'private'
this.asymmetricKeyType = 'x25519'
this._priv = Asn1Buffer.from(priv32)
if (this._priv.length !== 32) throw new Error('X25519 private key must be 32 bytes')
this._pub = pub32 ? Asn1Buffer.from(pub32) : null
}
/** @param {{ type: string, format: string }} opts */
export(opts) {
if (!opts || opts.format !== 'der') throw new Error('unsupported export format')
if (opts.type === 'pkcs8') return berPkcs8X25519(this._priv)
throw new Error('unsupported export type')
}
}
function generateKeyPairSync() {
const nacl = getNacl()
const pair = nacl.box.keyPair()
return {
publicKey: new X25519PublicKey(pair.publicKey),
privateKey: new X25519PrivateKey(pair.secretKey, pair.publicKey)
}
}
/** @param {import('safer-buffer').Buffer} der */
function createPublicKeyFromSpkiDer(der) {
return new X25519PublicKey(parseSpkiX25519PubDer(der))
}
/**
* @param {{ privateKey: unknown, publicKey: unknown }} opts
* @returns {import('safer-buffer').Buffer}
*/
function diffieHellman(opts) {
const nacl = getNacl()
let privU8
let pubU8
const sk = opts.privateKey
const pk = opts.publicKey
if (sk instanceof X25519PrivateKey) privU8 = u8(sk._priv)
else if (sk && typeof sk.export === 'function')
privU8 = parsePkcs8X25519PrivDer(sk.export({ type: 'pkcs8', format: 'der' }))
else throw new Error('diffieHellman: unsupported private key')
if (pk instanceof X25519PublicKey) pubU8 = u8(pk._pub)
else if (pk && typeof pk.export === 'function')
pubU8 = parseSpkiX25519PubDer(pk.export({ type: 'spki', format: 'der' }))
else throw new Error('diffieHellman: unsupported public key')
const shared = nacl.scalarMult(privU8, pubU8)
// Use runtime `buffer` (Bare node-buffer) so kex `convertToMpint` / `hash.update` agree on `Buffer.isBuffer`.
return RuntimeBuffer.from(shared)
}
module.exports = {
generateKeyPairSync,
createPublicKeyFromSpkiDer,
diffieHellman,
X25519PublicKey,
X25519PrivateKey
}