const assert = require("node:assert/strict");
const crypto = require("node:crypto");
const test = require("node:test");

const { buildResponse, decodePapPassword, parseAttributes, verifyChap, verifyMessageAuthenticator } = require("../lib/radius-server.cjs");

function encryptPap(password, secret, authenticator) {
  const plain = Buffer.alloc(Math.ceil(Buffer.byteLength(password) / 16) * 16 || 16);
  plain.write(password);
  const encrypted = Buffer.alloc(plain.length);
  let previous = authenticator;
  for (let offset = 0; offset < plain.length; offset += 16) {
    const digest = crypto.createHash("md5").update(Buffer.concat([Buffer.from(secret), previous])).digest();
    for (let index = 0; index < 16; index += 1) encrypted[offset + index] = plain[offset + index] ^ digest[index];
    previous = encrypted.subarray(offset, offset + 16);
  }
  return encrypted;
}

test("RADIUS PAP password decrypts with request authenticator", () => {
  const authenticator = crypto.randomBytes(16);
  const encrypted = encryptPap("pelanggan-123", "shared-secret", authenticator);
  assert.equal(decodePapPassword(encrypted, "shared-secret", authenticator), "pelanggan-123");
});

test("RADIUS CHAP verifies the supplied password", () => {
  const identifier = Buffer.from([17]);
  const challenge = crypto.randomBytes(16);
  const digest = crypto.createHash("md5").update(Buffer.concat([identifier, Buffer.from("secret"), challenge])).digest();
  assert.equal(verifyChap(Buffer.concat([identifier, digest]), challenge, "secret"), true);
  assert.equal(verifyChap(Buffer.concat([identifier, digest]), challenge, "wrong"), false);
});

test("RADIUS response authenticator follows RFC 2865", () => {
  const request = Buffer.alloc(20);
  request[0] = 1;
  request[1] = 77;
  request.writeUInt16BE(20, 2);
  crypto.randomBytes(16).copy(request, 4);
  const response = buildResponse(request, 2, "shared-secret");
  assert.equal(response[0], 2);
  assert.equal(response[1], 77);
  assert.equal(response.length, 20);
  assert.equal(parseAttributes(response).length, 0);
});

test("RADIUS response includes a valid Message-Authenticator when requested", () => {
  const request = Buffer.alloc(38);
  request[0] = 1;
  request[1] = 12;
  request.writeUInt16BE(38, 2);
  crypto.randomBytes(16).copy(request, 4);
  request[20] = 80;
  request[21] = 18;
  crypto.createHmac("md5", "shared-secret").update(request).digest().copy(request, 22);
  assert.equal(verifyMessageAuthenticator(request, "shared-secret"), true);
  const response = buildResponse(request, 2, "shared-secret");
  assert.equal(response.length, 38);
  assert.equal(parseAttributes(response)[0].type, 80);
  assert.equal(verifyMessageAuthenticator(response, "shared-secret"), true);
});
