mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 19:29:13 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const crypto = require('node:crypto');
|
||||
|
||||
function createManagementIdentityCeremony(options) {
|
||||
assert.match(options.issuer, /^https:\/\/[A-Za-z0-9.-]+\/$/);
|
||||
for (const value of [
|
||||
options.audience,
|
||||
options.purpose,
|
||||
options.tokenType,
|
||||
options.subject,
|
||||
options.jtiPrefix,
|
||||
]) {
|
||||
assert.match(value, /^[A-Za-z0-9][A-Za-z0-9._:+-]{0,127}$/);
|
||||
}
|
||||
|
||||
function reviewedKey(kid) {
|
||||
assert.match(kid, /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/);
|
||||
const { privateKey, publicKey } = crypto.generateKeyPairSync('ed25519');
|
||||
return Object.freeze({
|
||||
kid,
|
||||
privateKey,
|
||||
publicJwk: Object.freeze({
|
||||
...publicKey.export({ format: 'jwk' }),
|
||||
alg: 'EdDSA',
|
||||
kid,
|
||||
use: 'sig',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
function keyset(generation, keys, revokedKids = []) {
|
||||
assert.ok(Number.isSafeInteger(generation) && generation >= 1);
|
||||
return Object.freeze({
|
||||
schemaVersion: 1,
|
||||
generation,
|
||||
issuer: options.issuer,
|
||||
audience: options.audience,
|
||||
keys: keys.map((key) => key.publicJwk),
|
||||
revokedKids: [...revokedKids],
|
||||
assuranceMappings: [
|
||||
{
|
||||
acr: 'urn:ql3:mfa',
|
||||
assurance: 'multi_factor',
|
||||
requiredAmr: ['pwd', 'otp'],
|
||||
},
|
||||
],
|
||||
constraints: {
|
||||
maxAssertionBytes: 8 * 1024,
|
||||
maxLifetimeMs: 5 * 60 * 1000,
|
||||
maxAuthenticationAgeMs: 5 * 60 * 1000,
|
||||
clockSkewMs: 5 * 1000,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function assertion(key, suffix = crypto.randomUUID()) {
|
||||
const now = Math.floor(Date.now() / 1_000);
|
||||
const header = Buffer.from(
|
||||
JSON.stringify({
|
||||
alg: 'EdDSA',
|
||||
kid: key.kid,
|
||||
typ: options.tokenType,
|
||||
}),
|
||||
).toString('base64url');
|
||||
const payload = Buffer.from(
|
||||
JSON.stringify({
|
||||
acr: 'urn:ql3:mfa',
|
||||
amr: ['pwd', 'otp'],
|
||||
aud: options.audience,
|
||||
auth_time: now - 1,
|
||||
exp: now + 290,
|
||||
iat: now,
|
||||
iss: options.issuer,
|
||||
jti: options.jtiPrefix + '-' + suffix,
|
||||
ql3_purpose: options.purpose,
|
||||
sub: options.subject,
|
||||
}),
|
||||
).toString('base64url');
|
||||
const signed = header + '.' + payload;
|
||||
return (
|
||||
signed +
|
||||
'.' +
|
||||
crypto
|
||||
.sign(null, Buffer.from(signed, 'ascii'), key.privateKey)
|
||||
.toString('base64url')
|
||||
);
|
||||
}
|
||||
|
||||
return Object.freeze({ assertion, keyset, reviewedKey });
|
||||
}
|
||||
|
||||
module.exports = { createManagementIdentityCeremony };
|
||||
Reference in New Issue
Block a user