current commit
260916.1 · lib,src: apply multiple updates to dtls implementation
// Flags: --experimental-dtls --no-warnings

// Test: session.authorized and session.authorizationError report the result of
// peer certificate chain verification.
//
// OpenSSL verifies the chain even under SSL_VERIFY_NONE -- it just does not
// abort on failure -- so these stay accurate when rejectUnauthorized is false,
// which is what makes a custom authorization policy possible.

import { hasCrypto, mustCall, skip } from '../common/index.mjs';
import * as fixtures from '../common/fixtures.mjs';
import assert from 'node:assert';

if (!hasCrypto) {
  skip('missing crypto');
}

if (!process.features.dtls) {
  skip('DTLS is not enabled');
}

const { connect, listen } = await import('node:dtls');

const cert = fixtures.readKey('agent1-cert.pem');
const key = fixtures.readKey('agent1-key.pem');
const ca1 = fixtures.readKey('ca1-cert.pem');
const ca2 = fixtures.readKey('ca2-cert.pem');

// --- Client side ---------------------------------------------------------

const endpoint = listen(() => {}, {
  cert, key, host: '127.0.0.1', port: 0,
});
const { port } = endpoint.address;

for (const [description, options, authorized, authorizationError] of [
  ['a trusted chain and matching identity verifies',
   { servername: 'agent1', ca: [ca1] },
   true, undefined],
  ['an untrusted chain reports the issuer failure',
   { servername: 'agent1', ca: [ca2], rejectUnauthorized: false },
   false, 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY'],
  ['a trusted chain with the wrong identity reports the name mismatch',
   { servername: 'not-agent1', ca: [ca1], rejectUnauthorized: false },
   false, 'HOSTNAME_MISMATCH'],
]) {
  const client = connect('127.0.0.1', port, options);
  await client.opened;
  assert.strictEqual(client.authorized, authorized, description);
  assert.strictEqual(client.authorizationError, authorizationError,
                     description);
  await client.close();
}

await endpoint.close();

// --- Server side ---------------------------------------------------------

// A peer that sent no certificate is reported as unverified rather than
// authorized, even though OpenSSL has nothing to find fault with.
{
  const serverSession = Promise.withResolvers();
  const server = listen(mustCall((session) => {
    session.onhandshake = mustCall(() => serverSession.resolve(session));
  }), { cert, key, host: '127.0.0.1', port: 0 });

  const client = connect('127.0.0.1', server.address.port, {
    servername: 'agent1', ca: [ca1],
  });
  await client.opened;

  const session = await serverSession.promise;
  assert.strictEqual(session.authorized, false);
  assert.strictEqual(session.authorizationError, 'UNABLE_TO_GET_ISSUER_CERT');

  await client.close();
  await server.close();
}

// A peer that sent a chain the server trusts is authorized.
{
  const serverSession = Promise.withResolvers();
  const server = listen(mustCall((session) => {
    session.onhandshake = mustCall(() => serverSession.resolve(session));
  }), {
    cert, key, ca: [ca1], requestCert: true, host: '127.0.0.1', port: 0,
  });

  const client = connect('127.0.0.1', server.address.port, {
    cert, key, servername: 'agent1', ca: [ca1],
  });
  await client.opened;

  const session = await serverSession.promise;
  assert.strictEqual(session.authorized, true);
  assert.strictEqual(session.authorizationError, undefined);

  await client.close();
  await server.close();
}