199 lines
5.1 KiB
JavaScript
199 lines
5.1 KiB
JavaScript
'use strict'
|
|
|
|
const assert = require('./assert-compat.js')
|
|
|
|
const debug = false
|
|
|
|
const { fixtureKey, mustCall, setup } = require('./common.js')
|
|
const {
|
|
AgentProtocol,
|
|
BaseAgent,
|
|
utils: { parseKey }
|
|
} = require('../lib/index.js')
|
|
|
|
const serverCfg = { hostKeys: [fixtureKey('ssh_host_rsa_key').raw] }
|
|
|
|
const clientKey = fixtureKey('openssh_new_rsa')
|
|
|
|
{
|
|
let getIdentitiesCount = 0
|
|
let signCount = 0
|
|
class MyAgent extends BaseAgent {
|
|
getIdentities(cb) {
|
|
assert.strictEqual(++getIdentitiesCount, 1)
|
|
// Ensure that no private portion of the key is used by re-parsing the
|
|
// public version of the key
|
|
cb(null, [parseKey(clientKey.key.getPublicSSH())])
|
|
}
|
|
sign(pubKey, data, options, cb) {
|
|
assert.strictEqual(++signCount, 1)
|
|
assert.strictEqual(pubKey.getPublicPEM(), clientKey.key.getPublicPEM())
|
|
const sig = clientKey.key.sign(data, options.hash)
|
|
cb(null, sig)
|
|
}
|
|
}
|
|
|
|
const username = 'Agent User'
|
|
const { server } = setup('Custom agent authentication', {
|
|
client: { username, agent: new MyAgent() },
|
|
server: serverCfg,
|
|
|
|
debug
|
|
})
|
|
|
|
server.on(
|
|
'connection',
|
|
mustCall((conn) => {
|
|
let authAttempt = 0
|
|
conn
|
|
.on(
|
|
'authentication',
|
|
mustCall((ctx) => {
|
|
assert(ctx.username === username, `Wrong username: ${ctx.username}`)
|
|
switch (++authAttempt) {
|
|
case 1:
|
|
assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`)
|
|
return ctx.reject()
|
|
case 3:
|
|
assert(ctx.signature, 'Missing publickey signature')
|
|
// FALLTHROUGH
|
|
case 2:
|
|
assert(ctx.method === 'publickey', `Wrong auth method: ${ctx.method}`)
|
|
assert(ctx.key.algo === clientKey.key.type, `Wrong key algo: ${ctx.key.algo}`)
|
|
assert.deepStrictEqual(
|
|
clientKey.key.getPublicSSH(),
|
|
ctx.key.data,
|
|
'Public key mismatch'
|
|
)
|
|
break
|
|
}
|
|
if (ctx.signature) {
|
|
const result = clientKey.key.verify(ctx.blob, ctx.signature, ctx.hashAlgo)
|
|
assert(result === true, 'Could not verify publickey signature')
|
|
}
|
|
ctx.accept()
|
|
}, 3)
|
|
)
|
|
.on(
|
|
'ready',
|
|
mustCall(() => {
|
|
assert.strictEqual(getIdentitiesCount, 1)
|
|
assert.strictEqual(signCount, 1)
|
|
conn.end()
|
|
})
|
|
)
|
|
})
|
|
)
|
|
}
|
|
{
|
|
const client = new AgentProtocol(true)
|
|
const server = new AgentProtocol(false)
|
|
|
|
server.on(
|
|
'identities',
|
|
mustCall((req) => {
|
|
setImmediate(() => server.failureReply(req))
|
|
})
|
|
)
|
|
client.getIdentities(
|
|
mustCall((err, keys) => {
|
|
assert(err, 'Missing expected error')
|
|
})
|
|
)
|
|
|
|
client.pipe(server).pipe(client)
|
|
}
|
|
{
|
|
const client = new AgentProtocol(true)
|
|
const server = new AgentProtocol(false)
|
|
|
|
server.on(
|
|
'identities',
|
|
mustCall((req) => {
|
|
const keys = [clientKey.key]
|
|
server.getIdentitiesReply(req, keys)
|
|
})
|
|
)
|
|
client.getIdentities(
|
|
mustCall((err, keys) => {
|
|
assert(!err, 'Unexpected error')
|
|
assert.strictEqual(keys.length, 1)
|
|
assert.strictEqual(keys[0].isPrivateKey(), false)
|
|
assert.strictEqual(keys[0].getPublicPEM(), clientKey.key.getPublicPEM())
|
|
})
|
|
)
|
|
|
|
client.pipe(server).pipe(client)
|
|
}
|
|
{
|
|
const client = new AgentProtocol(true)
|
|
const server = new AgentProtocol(false)
|
|
const buf = Buffer.from('data to sign')
|
|
|
|
server.on(
|
|
'sign',
|
|
mustCall((req, pubKey, data, options) => {
|
|
assert.strictEqual(pubKey.getPublicPEM(), clientKey.key.getPublicPEM())
|
|
assert.deepStrictEqual(data, buf)
|
|
assert.strictEqual(options.hash, undefined)
|
|
server.failureReply(req)
|
|
})
|
|
)
|
|
client.sign(
|
|
clientKey.key.getPublicSSH(),
|
|
buf,
|
|
mustCall((err, signature) => {
|
|
assert(err, 'Missing expected error')
|
|
})
|
|
)
|
|
|
|
client.pipe(server).pipe(client)
|
|
}
|
|
{
|
|
const client = new AgentProtocol(true)
|
|
const server = new AgentProtocol(false)
|
|
const buf = Buffer.from('data to sign')
|
|
|
|
server.on(
|
|
'sign',
|
|
mustCall((req, pubKey, data, options) => {
|
|
assert.strictEqual(pubKey.getPublicPEM(), clientKey.key.getPublicPEM())
|
|
assert.deepStrictEqual(data, buf)
|
|
assert.strictEqual(options.hash, undefined)
|
|
server.signReply(req, clientKey.key.sign(data))
|
|
})
|
|
)
|
|
client.sign(
|
|
clientKey.key.getPublicSSH(),
|
|
buf,
|
|
mustCall((err, signature) => {
|
|
assert(!err, 'Unexpected error')
|
|
const pubKey = parseKey(clientKey.key.getPublicSSH())
|
|
assert.strictEqual(pubKey.verify(buf, signature), true)
|
|
})
|
|
)
|
|
|
|
client.pipe(server).pipe(client)
|
|
}
|
|
{
|
|
// Test that outstanding requests are handled upon unexpected closure of the
|
|
// protocol stream
|
|
|
|
const client = new AgentProtocol(true)
|
|
const server = new AgentProtocol(false)
|
|
|
|
server.on(
|
|
'identities',
|
|
mustCall((req) => {
|
|
server.destroy()
|
|
})
|
|
)
|
|
client.getIdentities(
|
|
mustCall((err) => {
|
|
assert(err, 'Missing expected error')
|
|
})
|
|
)
|
|
|
|
client.pipe(server).pipe(client)
|
|
}
|