test-userauth-agent.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. 'use strict';
  2. const assert = require('assert');
  3. const debug = false;
  4. const {
  5. fixtureKey,
  6. mustCall,
  7. setup,
  8. } = require('./common.js');
  9. const {
  10. AgentProtocol,
  11. BaseAgent,
  12. utils: { parseKey },
  13. } = require('../lib/index.js');
  14. const serverCfg = { hostKeys: [ fixtureKey('ssh_host_rsa_key').raw ] };
  15. const clientKey = fixtureKey('openssh_new_rsa');
  16. {
  17. let getIdentitiesCount = 0;
  18. let signCount = 0;
  19. class MyAgent extends BaseAgent {
  20. getIdentities(cb) {
  21. assert.strictEqual(++getIdentitiesCount, 1);
  22. // Ensure that no private portion of the key is used by re-parsing the
  23. // public version of the key
  24. cb(null, [ parseKey(clientKey.key.getPublicSSH()) ]);
  25. }
  26. sign(pubKey, data, options, cb) {
  27. assert.strictEqual(++signCount, 1);
  28. assert.strictEqual(pubKey.getPublicPEM(), clientKey.key.getPublicPEM());
  29. const sig = clientKey.key.sign(data, options.hash);
  30. cb(null, sig);
  31. }
  32. }
  33. const username = 'Agent User';
  34. const { server } = setup(
  35. 'Custom agent authentication',
  36. {
  37. client: { username, agent: new MyAgent() },
  38. server: serverCfg,
  39. debug,
  40. }
  41. );
  42. server.on('connection', mustCall((conn) => {
  43. let authAttempt = 0;
  44. conn.on('authentication', mustCall((ctx) => {
  45. assert(ctx.username === username,
  46. `Wrong username: ${ctx.username}`);
  47. switch (++authAttempt) {
  48. case 1:
  49. assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`);
  50. return ctx.reject();
  51. case 3:
  52. assert(ctx.signature, 'Missing publickey signature');
  53. // FALLTHROUGH
  54. case 2:
  55. assert(ctx.method === 'publickey',
  56. `Wrong auth method: ${ctx.method}`);
  57. assert(ctx.key.algo === clientKey.key.type,
  58. `Wrong key algo: ${ctx.key.algo}`);
  59. assert.deepStrictEqual(clientKey.key.getPublicSSH(),
  60. ctx.key.data,
  61. 'Public key mismatch');
  62. break;
  63. }
  64. if (ctx.signature) {
  65. assert(clientKey.key.verify(ctx.blob, ctx.signature) === true,
  66. 'Could not verify publickey signature');
  67. }
  68. ctx.accept();
  69. }, 3)).on('ready', mustCall(() => {
  70. assert.strictEqual(getIdentitiesCount, 1);
  71. assert.strictEqual(signCount, 1);
  72. conn.end();
  73. }));
  74. }));
  75. }
  76. {
  77. const client = new AgentProtocol(true);
  78. const server = new AgentProtocol(false);
  79. server.on('identities', mustCall((req) => {
  80. setImmediate(() => server.failureReply(req));
  81. }));
  82. client.getIdentities(mustCall((err, keys) => {
  83. assert(err, 'Missing expected error');
  84. }));
  85. client.pipe(server).pipe(client);
  86. }
  87. {
  88. const client = new AgentProtocol(true);
  89. const server = new AgentProtocol(false);
  90. server.on('identities', mustCall((req) => {
  91. const keys = [ clientKey.key ];
  92. server.getIdentitiesReply(req, keys);
  93. }));
  94. client.getIdentities(mustCall((err, keys) => {
  95. assert(!err, 'Unexpected error');
  96. assert.strictEqual(keys.length, 1);
  97. assert.strictEqual(keys[0].isPrivateKey(), false);
  98. assert.strictEqual(keys[0].getPublicPEM(), clientKey.key.getPublicPEM());
  99. }));
  100. client.pipe(server).pipe(client);
  101. }
  102. {
  103. const client = new AgentProtocol(true);
  104. const server = new AgentProtocol(false);
  105. const buf = Buffer.from('data to sign');
  106. server.on('sign', mustCall((req, pubKey, data, options) => {
  107. assert.strictEqual(pubKey.getPublicPEM(), clientKey.key.getPublicPEM());
  108. assert.deepStrictEqual(data, buf);
  109. assert.strictEqual(options.hash, undefined);
  110. server.failureReply(req);
  111. }));
  112. client.sign(clientKey.key.getPublicSSH(),
  113. buf,
  114. mustCall((err, signature) => {
  115. assert(err, 'Missing expected error');
  116. }));
  117. client.pipe(server).pipe(client);
  118. }
  119. {
  120. const client = new AgentProtocol(true);
  121. const server = new AgentProtocol(false);
  122. const buf = Buffer.from('data to sign');
  123. server.on('sign', mustCall((req, pubKey, data, options) => {
  124. assert.strictEqual(pubKey.getPublicPEM(), clientKey.key.getPublicPEM());
  125. assert.deepStrictEqual(data, buf);
  126. assert.strictEqual(options.hash, undefined);
  127. server.signReply(req, clientKey.key.sign(data));
  128. }));
  129. client.sign(clientKey.key.getPublicSSH(),
  130. buf,
  131. mustCall((err, signature) => {
  132. assert(!err, 'Unexpected error');
  133. const pubKey = parseKey(clientKey.key.getPublicSSH());
  134. assert.strictEqual(pubKey.verify(buf, signature), true);
  135. }));
  136. client.pipe(server).pipe(client);
  137. }
  138. {
  139. // Test that outstanding requests are handled upon unexpected closure of the
  140. // protocol stream
  141. const client = new AgentProtocol(true);
  142. const server = new AgentProtocol(false);
  143. server.on('identities', mustCall((req) => {
  144. server.destroy();
  145. }));
  146. client.getIdentities(mustCall((err) => {
  147. assert(err, 'Missing expected error');
  148. }));
  149. client.pipe(server).pipe(client);
  150. }