@smartdcc/dccboxed-keystore
Version:
DCC Boxed server keystore exposed as json db.
209 lines • 8.32 kB
JavaScript
;
/*
* Created on Thu Aug 04 2022
*
* Copyright (c) 2022 Smart DCC Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.KeyStoreDB = void 0;
exports.queryOptionsHasEUI = queryOptionsHasEUI;
const node_json_db_1 = require("node-json-db");
const node_crypto_1 = require("node:crypto");
const certificateMetadata_1 = require("./certificateMetadata");
function pushOptionsHasCertificate(p) {
return 'certificate' in p;
}
function queryOptionsHasEUI(q) {
return 'eui' in q;
}
class KeyStoreDB {
constructor(filename) {
this.db = new node_json_db_1.JsonDB(new node_json_db_1.Config(filename, true, true));
}
/**
* Wrap constructor for async operations.
*
* @param filename
* @returns
*/
static async new(filename) {
const instance = new KeyStoreDB(filename);
await instance.db.load();
return instance;
}
async query(options) {
if (queryOptionsHasEUI(options)) {
let tree;
try {
tree = (await this.db.getData(`/${(0, certificateMetadata_1.normaliseEUI)(options.eui)}/${certificateMetadata_1.KeyUsage[options.keyUsage]}`));
}
catch {
return null;
}
/* accumulate matches */
const results = [];
for (const serial in tree) {
const e = tree[serial];
/* skip any entries without the correct role */
if (typeof options.role === 'number' && e.role !== options.role) {
continue;
}
/* skip any entries without the request cert/key */
if (!(options.lookup in e) || typeof e[options.lookup] !== 'string') {
continue;
}
let material;
if (options.lookup === 'certificate') {
material = {
certificate: new node_crypto_1.X509Certificate(e.certificate),
};
}
else {
material = {
privateKey: (0, node_crypto_1.createPrivateKey)({
key: e.privateKey,
type: 'pkcs8',
format: 'pem',
}),
};
}
const role = {};
if (typeof e.role === 'number') {
role.role = e.role;
}
const name = {};
if (typeof e.name === 'string') {
name.name = e.name;
}
results.push({
eui: options.eui instanceof certificateMetadata_1.EUI ? options.eui : new certificateMetadata_1.EUI(options.eui),
keyUsage: [options.keyUsage],
serial: BigInt(serial),
...role,
...material,
...name,
});
}
if (results.length >= 1) {
return results;
}
}
else {
/* search by serial */
const tree = (await this.db.getData('/'));
for (const id in tree) {
for (const usage in tree[id]) {
for (const serial in tree[id][usage]) {
if (serial === options.serial.toString()) {
const e = tree[id][usage][serial];
if (options.lookup in e &&
typeof e[options.lookup] === 'string') {
let material;
if (options.lookup === 'certificate') {
material = {
certificate: new node_crypto_1.X509Certificate(e.certificate),
};
}
else {
material = {
privateKey: (0, node_crypto_1.createPrivateKey)({
key: e.privateKey,
type: 'pkcs8',
format: 'pem',
}),
};
}
const role = {};
if (typeof e.role === 'number') {
role.role = e.role;
}
const name = {};
if (typeof e.name === 'string') {
name.name = e.name;
}
return {
eui: new certificateMetadata_1.EUI(id),
keyUsage: [certificateMetadata_1.KeyUsage[usage]],
serial: options.serial,
...role,
...material,
...name,
};
}
}
}
}
}
}
return null;
}
async push(options) {
let meta;
const certificate = {};
const privateKey = {};
const name = {};
if (typeof options.name === 'string') {
name.name = options.name;
}
if (pushOptionsHasCertificate(options)) {
certificate.certificate = options.certificate.toJSON();
try {
meta = (0, certificateMetadata_1.buildOrgCertificateMetadata)(options.certificate);
}
catch {
try {
meta = (0, certificateMetadata_1.buildDeviceCertificateMetadata)(options.certificate);
}
catch {
throw new Error('unable to extract metadata from certificate');
}
}
if (options.private) {
privateKey.privateKey = options.private?.export({
format: 'pem',
type: 'pkcs8',
});
if (!options.certificate.checkPrivateKey(options.private)) {
throw new Error('invalid key pair');
}
}
}
else {
meta = { ...options.meta, eui: new certificateMetadata_1.EUI((0, certificateMetadata_1.normaliseEUI)(options.meta.eui)) };
privateKey.privateKey = options.private?.export({
format: 'pem',
type: 'pkcs8',
});
}
const entry = {
...certificate,
...privateKey,
...name,
};
if (typeof meta.role === 'number') {
entry.role = meta.role;
}
if (meta.keyUsage.length !== 1 &&
meta.keyUsage[0] !== certificateMetadata_1.KeyUsage.digitalSignature &&
meta.keyUsage[0] !== certificateMetadata_1.KeyUsage.keyAgreement) {
throw new Error('unsupported keyUsage');
}
await this.db.push(`/${(0, certificateMetadata_1.normaliseEUI)(meta.eui)}/${certificateMetadata_1.KeyUsage[meta.keyUsage[0]]}/${meta.serial}`, entry, false);
return meta;
}
}
exports.KeyStoreDB = KeyStoreDB;
//# sourceMappingURL=db.js.map