PageSigner v3 release. Based on the TLSNotary protocol which supports TLS 1.2+

This commit is contained in:
themighty1
2021-11-11 21:04:02 +03:00
parent 4ba443bddc
commit 4e125731fd
183 changed files with 144038 additions and 26 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "core/twopc/circuits"]
path = core/twopc/circuits
url = https://github.com/tlsnotary/circuits

29
README
View File

@@ -1,22 +1,15 @@
----------------------------------------------------------------
UPDATE: Jul 2021
A flaw in PageSigner has been brought to our attention which allows to create forged proofs under certain conditions.
We are working on a mitigation.
PageSigner - a cryptographically secure webpage screenshot tool.
Read more here:
https://github.com/tlsnotary/pagesigner/issues/46
----------------------------------------------------------------
PageSigner is a Chromium extension implementing a client for the TLSNotary protocol.
You can install PageSigner from Chrome webstore:
https://chrome.google.com/webstore/detail/pagesigner/abblaaeblmamacadkdmnejjikbdkemcj
PageSigner works with many Chromium-based browsers (e.g. Chromium, Vivaldi, Brave, Google Chrome)
Or you can install it by cloning this repo with:
You can load PageSigner from the source files of this repo this way:
1. go to chrome://extensions and Enable "Developer mode"
2. Click "Load unpacked"
3. Navigate inside pagesigner's top folder and click Open.
-----
Firefox addon creation is not supported anymore due to missing API in Firefox.
- git clone --recurse-submodules https://github.com/tlsnotary/pagesigner
- go to chrome://extensions and Enable "Developer mode"
- Click "Load unpacked"
- Navigate inside pagesigner's top folder and click Open.

15
background.html Normal file
View File

@@ -0,0 +1,15 @@
<html>
<head>
</head>
<body>
<script src="core/third-party/simple-js-ec-math.js"></script>
<script src="core/third-party/sodium.js"></script> <!--dont put this any lower in the list or youll get require() not defined -->
<script src="core/third-party/nacl-fast.js"></script>
<script src="core/third-party/fastsha256.js"></script>
<script src="core/third-party/cbor.js"></script>
<script src="core/third-party/cose.js"></script>
<script src="core/twopc/circuits/casmbundle.js"></script>
<script type="module" src="core/Main.js"></script>
<!-- <script type="module" src="core/test.js"></script> -->
</body>
</html>

144
core/FirstTimeSetup.js Normal file
View File

@@ -0,0 +1,144 @@
import {wait} from './utils.js';
// class FakeFS imitates node.js's fs.readFileSync() by
// reading the files in advance and outputting their content when readFileSync() is called
class FakeFS{
constructor(){
this.fileList = {}; // {fileName: <text string>}
}
// on init we read all .casm and .txt files in core/twopc/circuits
async init(){
const that = this;
await new Promise(function(resolve) {
chrome.runtime.getPackageDirectoryEntry(function(root){
root.getDirectory('core/twopc/circuits', {create: false}, function(dir) {
dir.createReader().readEntries(async function(results) {
for (let i=0; i < results.length; i+=1){
const e = results[i];
if (e.name.endsWith('.casm') || e.name.endsWith('.txt')){
const url = chrome.extension.getURL('core/twopc/circuits/'+e.name);
const resp = await fetch(url);
const text = await resp.text();
that.fileList[e.name] = text;
}
}
resolve();
});
});
});
});
}
// feed back all data read in init()
// eslint-disable-next-line no-unused-vars
readFileSync(path, _unused){
return this.fileList[path];
}
}
// FirstTimeSetup.start() is invoked once on first install. It assembles the circuits,
// serializes them into a compact binary format and stores them in the browser cache.
// All future invocations of Pagesigner use these serialized cached circuits.
export class FirstTimeSetup{
async start(pm){
const worker = new Worker(chrome.extension.getURL('core/twopc/webWorkers/serializeCircuits.js'));
console.log('parsing circuits, this is done only once on first launch and will take ~30 secs');
console.time('parsing raw circuits');
const obj = {};
const oldfs = window['fs'];
window['fs'] = new FakeFS();
await window['fs'].init();
// start from the last circuits in order to give the user a quicker initial update
for (let n=1; n < 7; n++){
const i = 7-n;
const text = CASM.parseAndAssemble('c'+i+'.casm');
const newobj = await new Promise(function(resolve) {
worker.onmessage = function(event) {
let newobj = event.data.obj;
newobj['gatesBlob'] = new Uint8Array(event.data.blob);
resolve(newobj);
};
worker.postMessage({'text': text});
});
obj[i] = newobj;
pm.update('first_time', {'current': n, 'total': 6});
await wait(100); // make sure update reaches popup
}
window['fs'] = oldfs;
console.timeEnd('parsing raw circuits');
return obj;
}
parseCircuitFirstTime(text){
const obj = {};
// we don't do any sanity/formatting checks because the circuits
// were output by casm.js and have a well-defined structure
const rows = text.split('\n');
obj['gatesCount'] = Number(rows[0].split(' ')[0]);
console.log('obj[\'gatesCount\']', obj['gatesCount']);
obj['wiresCount'] = Number(rows[0].split(' ')[1]);
obj['notaryInputSize'] = Number(rows[1].split(' ')[1]);
obj['clientInputSize'] = Number(rows[1].split(' ')[2]);
obj['outputSize'] = Number(rows[2].split(' ')[1]);
// each gate is serialized as
// 1 byte: gate type XOR==0 AND==1 INV==2
// 3 bytes: 1st input wire number
// 3 bytes: 2nd input wire number
// 3 bytes: output wire number
const gateByteSize = 10;
const opBytes = {'XOR': 0, 'AND': 1, 'INV': 2};
// first 3 rows are not gates but metadata
const blob = new Uint8Array((rows.length-3)*gateByteSize);
let blobOffset = 0;
let andCount = 0;
for (let i=0; i < (rows.length-3); i++){
const gate = rows[3+i];
const tokens = gate.split(' ');
const op = tokens[tokens.length-1];
const opByte = opBytes[op];
blob.set([opByte], blobOffset);
blobOffset+=1;
if (op === 'XOR' || op === 'AND'){
const in1 = this.intToThreeBytes(tokens[tokens.length-4]);
const in2 = this.intToThreeBytes(tokens[tokens.length-3]);
const out = this.intToThreeBytes(tokens[tokens.length-2]);
blob.set(in1, blobOffset);
blobOffset+=3;
blob.set(in2, blobOffset);
blobOffset+=3;
blob.set(out, blobOffset);
blobOffset+=3;
if (op == 'AND'){
andCount+=1;
}
}
else if (op === 'INV'){
const in1 = this.intToThreeBytes(tokens[tokens.length-3]);
const out = this.intToThreeBytes(tokens[tokens.length-2]);
blob.set(in1, blobOffset);
blobOffset+=3;
blob.set([0,0,0], blobOffset);
blobOffset+=3;
blob.set(out, blobOffset);
blobOffset+=3;
}
else {
throw('unknown op');
}
}
obj['andGateCount'] = andCount;
obj['gatesBlob'] = blob;
return obj;
}
intToThreeBytes(i){
const byteArray = Array(3);
byteArray[0] = (i >> 16) & 0xFF;
byteArray[1] = (i >> 8) & 0xFF;
byteArray[2] = i & 0xFF;
return byteArray;
}
}

1073
core/Main.js Normal file

File diff suppressed because it is too large Load Diff

54
core/ProgressMonitor.js Normal file
View File

@@ -0,0 +1,54 @@
// class ProgressMonitor receives progress information about client's garbling,
// evaluation, blob upload, blob download. It dispatches progress status messages
// periodically or when queried.
// because Chrome has a bug and does not remove onMessage listener, we
// use only one ProgressMonitor and reset its state when a new notarization
// session starts
export class ProgressMonitor{
constructor(){
this.progress = {
download: {},
upload: {},
garbling: {},
last_stage: {},
first_time: {},
};
// progress listeners may ask to give the current progress state
const that = this;
chrome.runtime.onMessage.addListener(function(data) {
if (data.destination != 'progress monitor') return;
chrome.runtime.sendMessage({
destination: 'progress listeners',
progress: that.progress
});
});
}
init(){
this.progress = {
download: {},
upload: {},
garbling: {},
last_stage: {},
first_time: {},
};
}
// update is called with updates progress information
update(type, obj){
this.progress[type] = obj;
chrome.runtime.sendMessage({
destination: 'progress listeners',
progress: this.progress
});
}
// destroy de-registers listeners
// doesn't do anything because of what seems like a Chrome bug.
destroy(){
// TODO it seems like Chrome does not remove onMessage listener
chrome.runtime.onMessage.removeListener(this.listener);
}
}

262
core/Socket.js Normal file
View File

@@ -0,0 +1,262 @@
// The only way to determine if the server is done sending data is to check that out receiving
// buffer has nothing but complete TLS records i.e. that there is no incomplete TLS records
// However it was observed that in cases when getting e.g. zip files, some servers first send HTTP header as one
// TLS record followed by the body as another record(s)
// That's why after receiving a complete TLS record we wait to get some more data
// This extra waiting must not be done for the handshake messages to avoid adding latency and having the handshake
// dropped by the server
// We do not communicate directly with the server but we send messages to the helper app
// It is the helper app which opens a TCP socket and sends/receives data
import {global} from './globals.js';
import {ba2str, b64decode, concatTA, b64encode, str2ba, ba2int} from './utils.js';
export class Socket {
constructor(name, port){
this.name = name;
this.port = port;
this.uid = Math.random().toString(36).slice(-10);
this.buffer = new Uint8Array();
// connect will throw if we couldnt establish a connection to the server for this long
this.connectTimeout = 5 * 1000;
// recv() will reject if no data was seen for this long
this.noDataTimeout = 5 * 1000;
// close the socket after this time, even if it is in the middle of receiving data
this.lifeTime = 40 * 1000;
// delay after which we make a final check of the receicing buffer and if there was no data,
// from the server, the we consider the data transmission finished
this.delayBeforeFinalIteration = 500;
this.wasClosed = false;
this.backendPort = 20022;
}
async connect() {
const that = this;
let timer;
const response = await new Promise(async function(resolve, reject) {
// dont wait for connect for too long
timer = setTimeout(function() {
reject('Unable to connect to the webserver. Please check your internet connection.');
return;
}, that.connectTimeout);
const msg = {'command': 'connect','args': {'name': that.name,'port': that.port},'uid': that.uid};
if (global.usePythonBackend){
const url = 'http://127.0.0.1:' + that.backendPort;
const payload = JSON.stringify(msg);
try{
var req = await fetch (url, {method:'POST', body: str2ba(payload).buffer, cache: 'no-store'});
}
catch (error) {
reject('connection error');
return;
}
const text = new Uint8Array (await req.arrayBuffer());
const response = ba2str(text);
resolve(JSON.parse(response));
return;
}
else {
chrome.runtime.sendMessage(global.appId, msg, function(response) {resolve(response);});
}
})
.catch(function(e){
throw(e);
});
// we need to access runtime.lastError to prevent Chrome from complaining
// about unchecked error
chrome.runtime.lastError;
clearTimeout(timer);
if (response == undefined){
throw (undefined);
}
if (response.retval != 'success') {
// throw(response.retval)
}
// else if (response.retval == 'success') {
setTimeout(function() {
if (! that.wasClosed)
that.close();
}, that.lifeTime);
// endless data fetching loop for the lifetime of this Socket
that.fetchLoop();
return 'ready';
}
async send(data_in) {
var msg = {'command': 'send', 'args': {'data': Array.from(data_in)}, 'uid': this.uid};
if (global.usePythonBackend){
msg.args.data = Array.from(b64encode(msg.args.data));
await fetch('http://127.0.0.1:20022', {method:'POST', body: JSON.stringify(msg),
cache: 'no-store'});
}
else{
chrome.runtime.sendMessage(global.appId, msg);
}
}
// poll the backend for more data
async fetchLoop() {
if (this.wasClosed) {
return;
}
var that = this;
var response = await new Promise(async function(resolve){
var msg = {'command': 'recv', 'uid': that.uid};
if (global.usePythonBackend){
var req = await fetch('http://127.0.0.1:20022', {method:'POST', body: JSON.stringify(msg),
cache: 'no-store'});
var text = new Uint8Array(await req.arrayBuffer());
var response = JSON.parse(ba2str(text));
if (response.data.length > 0){
response.data = Array.from(b64decode(response.data));
}
resolve(response);
}
else {
chrome.runtime.sendMessage(global.appId, msg, function(response) {resolve(response);});
}
});
if (response.data.length > 0){
console.log('fetched some data', response.data.length, that.uid);
that.buffer = concatTA(that.buffer, new Uint8Array(response.data));
}
setTimeout(function() {
that.fetchLoop();
}, 100);
}
// fetchLoop has built up the recv buffer
// check if there are complete records in the buffer,return them if yes or wait some more if no
recv (is_handshake) {
if (is_handshake == undefined) {
is_handshake = false;
}
var that = this;
return new Promise(function(resolve, reject) {
var dataLastSeen = new Date().getTime();
var complete_records = new Uint8Array();
var buf = new Uint8Array();
var resolved = false;
var lastIteration = false;
function finished_receiving() {
console.log('recv promise resolving', that.uid);
resolved = true;
resolve(complete_records);
}
var check = function() {
var now = new Date().getTime();
if ((now - dataLastSeen) > that.noDataTimeout){
reject('recv: no data timeout');
return;
}
// console.log('check()ing for more data', uid);
if (resolved) {
console.log('returning because resolved');
return;
}
if (that.buffer.length === 0) {
if (lastIteration){
finished_receiving();
return;
}
setTimeout(function() {check();}, 100);
return;
}
// else got new data
if (lastIteration){
console.log('more data received on last iteration', that.uid);
lastIteration = false;
}
console.log('new data in check', that.buffer.length);
dataLastSeen = now;
buf = concatTA(buf, that.buffer);
that.buffer = new Uint8Array();
const rv = that.check_complete_records(buf);
complete_records = concatTA(complete_records, rv.comprecs);
if (!rv.is_complete) {
console.log('check_complete_records failed', that.uid);
buf = rv.incomprecs;
setTimeout(function() {check();}, 100);
return;
}
else {
console.log('got complete records', that.uid);
if (is_handshake) {
finished_receiving();
return;
}
else {
console.log('in recv waiting for an extra second', that.uid);
buf = new Uint8Array();
// give the server another second to send more data
lastIteration = true;
setTimeout(function() {check();}, that.delayBeforeFinalIteration);
}
}
};
check();
})
.catch(function(error){
throw(error);
});
}
async close() {
this.wasClosed = true;
var msg = {'command': 'close','uid': this.uid};
console.log('closing socket', this.uid);
if (global.usePythonBackend){
await fetch('http://127.0.0.1:20022', {method:'POST', body: JSON.stringify(msg),
cache: 'no-store'});
}
else {
chrome.runtime.sendMessage(global.appId, msg);
}
}
check_complete_records(d) {
/* '''Given a response d from a server,
we want to know if its contents represents
a complete set of records, however many.'''
*/
let complete_records = new Uint8Array();
while (d) {
if (d.length < 5) {
return {
'is_complete': false,
'comprecs': complete_records,
'incomprecs': d
};
}
var l = ba2int(d.slice(3, 5));
if (d.length < (l + 5)) {
return {
'is_complete': false,
'comprecs': complete_records,
'incomprecs': d
};
} else if (d.length === (l + 5)) {
return {
'is_complete': true,
'comprecs': concatTA(complete_records, d)
};
} else {
complete_records = concatTA(complete_records, d.slice(0, l + 5));
d = d.slice(l + 5);
continue;
}
}
}
}
if (typeof module !== 'undefined'){ // we are in node.js environment
module.exports={
};
}

627
core/TLS.js Normal file
View File

@@ -0,0 +1,627 @@
import {TWOPC} from './twopc/TWOPC.js';
import {global} from './globals.js';
import {ba2str, b64decode, concatTA, int2ba, sha256, b64encode, str2ba, assert,
ba2int, dechunk_http, gunzip_http, getRandom, sigDER2p1363, pubkeyPEM2raw, eq,
xor, AESECBencrypt, buildChunkMetadata, b64urlencode} from './utils.js';
import {verifyChain, checkCertSubjName} from './verifychain.js';
import {Socket} from './Socket.js';
export class TLS {
// allHandshakes is a concatenation of all handshake messages up to this point.
// This is only data visible at the handshake layer and does not include record layer headers
allHandshakes;
// certPath is an array of certificates from the server arranged by pkijs in the ascending
// order from leaf to root
certPath;
// cke is TLS handshake's Client Key Exchange message
cke;
clientRandom;
commPrivkey;
commSymmetricKey;
headers;
isMhm; // multiple handshake messages
mustVerifyCert;
notaryWillEncryptRequest;
options;
port;
rsaSig;
serverRandom;
sckt;
serverEcPubkey;
secret; // for debug purposes only
// sid is id for this notarization session
sid;
serverName;
useMaxFragmentLength;
constructor (serverName, port, headers, options){
this.serverName = serverName;
this.port = port;
this.headers = headers;
this.sid = Math.random().toString(36).slice(-10);
this.options = options;
this.useMaxFragmentLength = options.useMaxFragmentLength;
this.notaryWillEncryptRequest = options.notaryWillEncryptRequest;
this.mustVerifyCert = options.mustVerifyCert;
this.sckt = new Socket(serverName, port);
}
buildClientHello(){
let tmp = [];
tmp.push(0x00, 0x0a); // Type supported_groups
tmp.push(0x00, 0x04); // Length
tmp.push(0x00, 0x02); // Supported Groups List Length
tmp.push(0x00, 0x17); // Supported Group: secp256r1
const supported_groups_extension = new Uint8Array(tmp);
tmp = [];
tmp.push(0x00, 0x0d); // Type signature_algorithms
tmp.push(0x00, 0x04); // Length
tmp.push(0x00, 0x02); // Signature Hash Algorithms Length
tmp.push(0x04, 0x01); // Signature Algorithm: rsa_pkcs1_sha256 (0x0401)
const signature_algorithm_extension = new Uint8Array(tmp);
tmp = [];
const server_name = str2ba(this.serverName);
tmp.push(0x00, 0x00); // Extension type: server_name
tmp.push(...Array.from(int2ba(server_name.length+5, 2))); // Length
tmp.push(...Array.from(int2ba(server_name.length+3, 2))); // Server Name List Length
tmp.push(0x00); // Type: host name
tmp.push(...Array.from(int2ba(server_name.length, 2))); // Server Name Length
tmp.push(...Array.from(server_name));
const server_name_extension = new Uint8Array(tmp);
tmp = [];
if (this.useMaxFragmentLength){
tmp.push(0x00, 0x01); // Type: max_fragment_length
tmp.push(0x00, 0x01); // Length
// allowed values 0x01 = 512 0x02 = 1024 0x03 = 2048 0x04 = 4096
// some servers support 0x04 but send alert if < 0x04
tmp.push(0x04);
}
const max_fragment_length_extension = new Uint8Array(tmp);
const extlen = supported_groups_extension.length + signature_algorithm_extension.length +
server_name_extension.length + max_fragment_length_extension.length;
tmp = [];
tmp.push(0x01); // Handshake type: Client Hello
tmp.push(...int2ba(extlen + 43, 3) ); // Length
tmp.push(0x03, 0x03); // Version: TLS 1.2
this.clientRandom = getRandom(32);
tmp.push(...Array.from(this.clientRandom));
tmp.push(0x00); // Session ID Length
tmp.push(0x00, 0x02); // Cipher Suites Length
tmp.push(0xc0, 0x2f); // Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
tmp.push(0x01); // Compression Methods Length
tmp.push(0x00); // Compression Method: null
tmp.push(...int2ba(extlen, 2));
const ch = concatTA(
new Uint8Array(tmp),
supported_groups_extension,
signature_algorithm_extension,
server_name_extension,
max_fragment_length_extension);
this.allHandshakes = ch;
tmp = [];
tmp.push(0x16); // Type: Handshake
tmp.push(0x03, 0x03); // Version: TLS 1.2
tmp.push(...int2ba(ch.length, 2)); // Length
const tls_record_header = new Uint8Array(tmp);
return concatTA(tls_record_header, ch);
}
async verifyNotarySig(sigDER, pubKey, signed_data, options){
const isRaw = (options == 'raw') ? true : false;
const sig_p1363 = sigDER2p1363(sigDER);
const notaryPubkey = isRaw ? pubKey : pubkeyPEM2raw(pubKey);
const pubkeyCryptoKey = await crypto.subtle.importKey(
'raw', notaryPubkey.buffer, {name: 'ECDSA', namedCurve:'P-256'}, true, ['verify']);
// eslint-disable-next-line no-unused-vars
const result = await crypto.subtle.verify(
{'name':'ECDSA', 'hash':'SHA-256'}, pubkeyCryptoKey, sig_p1363.buffer, signed_data.buffer);
return result;
}
async parse_commpk_commpksig_cpk(dataEnc){
// Notary's EC pubkey (to derive ECDH secret for communication) is not encrypted
const comm_pk = dataEnc.slice(0,65);
// for debug purposes only
this.secret = dataEnc.slice(65,97);
this.commSymmetricKey = await getECDHSecret(comm_pk, this.commPrivkey);
const data = await this.decryptFromNotary(
this.commSymmetricKey,
dataEnc.slice(65+this.secret.length));
let o = 0; // offset
// get signature over communication pubkey
const ssrvLen = ba2int(data.slice(o,o+=1));
const signingServerRetval = data.slice(o, o+=ssrvLen);
const cpk = data.slice(o,o+=65); // Client's pubkey for ECDH
// parse signing server's return value
o = 0;
const sessionSigLen = ba2int(signingServerRetval.slice(o, o+=1));
const sessionSig = signingServerRetval.slice(o, o+=sessionSigLen);
const ephemKeySigLen = ba2int(signingServerRetval.slice(o, o+=1));
const ephemKeySig = signingServerRetval.slice(o, o+=ephemKeySigLen);
const ephemPubKey = signingServerRetval.slice(o, o+=65);
const ephemValidFrom = signingServerRetval.slice(o, o+=4);
const ephemValidUntil = signingServerRetval.slice(o, o+=4);
// check signature
const to_be_signed = await sha256(comm_pk);
assert(await this.verifyNotarySig(sessionSig, ephemPubKey, to_be_signed, 'raw') == true);
// the ephemeral key with its validity time range is signed by master key
const ephemTBS = await sha256(concatTA(ephemPubKey, ephemValidFrom, ephemValidUntil));
assert(await this.verifyNotarySig(ephemKeySig, this.notary.pubkeyPEM, ephemTBS) == true);
this.checkEphemKeyExpiration(ephemValidFrom, ephemValidUntil);
return cpk;
}
parseServerHello(s){
let p = 0;
assert(eq(s.slice(p, p+=1), [0x02])); // Server Hello
// eslint-disable-next-line no-unused-vars
const shlen = ba2int(s.slice(p, p+=3));
assert(eq(s.slice(p, p+=2), [0x03, 0x03])); // Version: TLS 1.2
this.serverRandom = s.slice(p, p+=32);
const sidlen = ba2int(s.slice(p, p+=1));
if (sidlen > 0){
p+=sidlen; // 32 bytes of session ID, if any
}
assert(eq(s.slice(p, p+=2), [0xc0, 0x2f])); // Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
assert(eq(s.slice(p, p+=1), [0x00])); // Compression Method: null (0)
// May contain Extensions. We don't need to parse them
}
async parseCertificate(s){
let p = 0;
assert(eq(s.slice(p, p+=1), [0x0b])); // Certificate
// eslint-disable-next-line no-unused-vars
const clen = ba2int(s.slice(p, p+=3));
const certslen = ba2int(s.slice(p, p+=3));
const certs_last_pos = p + certslen;
const certs = [];
while (p < certs_last_pos){
const certlen = ba2int(s.slice(p, p+=3));
const certder = s.slice(p, p+certlen);
p+=certlen;
certs.push(certder);
}
const vcrv = await verifyChain(certs);
assert (vcrv.result == true);
this.certPath = vcrv.certificatePath;
assert(checkCertSubjName(this.certPath[0], this.serverName) == true);
return p;
}
async parseServerKeyExchange(s){
let p = 0;
assert(eq(s.slice(p, p+=1), [0x0c])); // Handshake Type: Server Key Exchange (12)
// eslint-disable-next-line no-unused-vars
const skelen = ba2int(s.slice(p, p+=3));
// EC Diffie-Hellman Server Params
assert(eq(s.slice(p, p+=1), [0x03])); // Curve Type: named_curve (0x03)
assert(eq(s.slice(p, p+=2), [0x00, 0x17])); // Named Curve: secp256r1 (0x0017)
const pklen = ba2int(s.slice(p, p+=1));
assert (pklen == 65); // Pubkey Length: 65
this.serverEcPubkey = s.slice(p, p+=pklen);
assert(eq(s.slice(p, p+=2), [0x04, 0x01])); // #Signature Algorithm: rsa_pkcs1_sha256 (0x0401)
const siglen = ba2int(s.slice(p, p+=2));
this.rsaSig = s.slice(p, p+=siglen);
const result = await TLS.verifyECParamsSig(this.certPath[0], this.serverEcPubkey, this.rsaSig, this.clientRandom, this.serverRandom);
assert (result == true);
return p;
}
// Obsolete: encrypt request without 2PC.
async encryptRequest(headers_str, client_write_key, client_write_IV, chunkSize){
let headers = str2ba(headers_str);
chunkSize = chunkSize || headers.length; // one chunk only
const chunks = Math.ceil(headers.length/chunkSize);
const encReq = [];
for (let i=0; i < chunks; i++){
let thisChunkSize = chunkSize;
if (i == chunks-1){
// last chunk may be smaller
const rem = headers.length % chunkSize;
thisChunkSize = (rem == 0) ? chunkSize : rem;
}
const explicit_nonce = int2ba(2+i, 8);
// const explicit_nonce = getRandom(8)
const nonce = concatTA(client_write_IV, explicit_nonce);
const seq_num = 1+i;
const aad = concatTA(
int2ba(seq_num, 8),
new Uint8Array([0x17, 0x03, 0x03]), // type 0x17 = Application data , TLS Version 1.2
int2ba(thisChunkSize, 2)); // unencrypted data length in bytes
const cwkCryptoKey = await crypto.subtle.importKey(
'raw',
client_write_key.buffer,
'AES-GCM',
true,
['encrypt', 'decrypt']);
const ciphertext = await crypto.subtle.encrypt({
name: 'AES-GCM',
iv: nonce.buffer,
additionalData: aad.buffer},
cwkCryptoKey,
headers.slice(chunkSize*i, chunkSize*(i+1)).buffer);
encReq.push(concatTA(explicit_nonce, new Uint8Array(ciphertext)));
}
return encReq;
}
// ephemeral key usage time must be within the time of ephemeral key validity
checkEphemKeyExpiration(validFrom, validUntil, time){
time = time || Math.floor(new Date().getTime() / 1000);
if (ba2int(validFrom) > time || time > ba2int(validUntil)){
throw('Ephemeral key expired');
}
}
async buildAndSendClientHello(){
const ch = this.buildClientHello();
await this.sckt.connect();
this.sckt.send(ch); // Send Client Hello
}
// receiveAndParseServerHelloAndFriends receives Server Hello, Certificate,
// Server Key Exchange, and Server Hello Done and parses them
async receiveAndParseServerHello(){
try{
this.sckt.recv_timeout = 5 * 1000;
var s = await this.sckt.recv(true);
} catch (err){
await this.sckt.close();
// some incompatible websites silently do not respond to ClientHello
throw('Failed to receive a response from a webserver. Make sure your internet connection is working and try again. If this error persists, this may mean that the webserver is not compatible with PageSigner. Please contact the PageSigner devs about this issue.');
}
// restore normal timeout value
this.sckt.recv_timeout = 20 * 1000;
// Parse Server Hello, Certificate, Server Key Exchange, Server Hello Done
if (eq(s.slice(0,2), [0x15, 0x03])){
console.log('Server sent Alert instead of Server Hello');
throw ('Unfortunately PageSigner is not yet able to notarize this website. Please contact the PageSigner devs about this issue.');
}
let p = 0; // current position in the byte stream
assert(eq(s.slice(p, p+=1), [0x16])); // Type: Handshake
assert(eq(s.slice(p, p+=2), [0x03, 0x03])); // Version: TLS 1.2
const handshakelen = ba2int(s.slice(p, p+=2));
// This may be the length of multiple handshake messages (MHM)
// For MHM there is only 1 TLS Record layer header followed by Handshake layer messages
// Without MHM, each handshake message has its own TLS Record header
// Parse Server Hello
const shlen = ba2int(s.slice(p+1, p+4));
const sh = s.slice(p, p + 4 + shlen);
this.updateAllHandshakes(sh);
this.parseServerHello(sh);
p = 5+4+shlen;
if (handshakelen > shlen+4) {
this.isMhm = true; }// multiple handshake messages
let reclenMhm = 0;
if (!this.isMhm){
// read the TLS Record header
assert(eq(s.slice(p, p+=3), [0x16, 0x03, 0x03])); // Type: Handshake # Version: TLS 1.2
reclenMhm = ba2int(s.slice(p, p+=2));
}
// Parse Certificate
const clen = ba2int(s.slice(p+1, p+4));
if (!this.isMhm) {
assert(reclenMhm == clen+4);}
const c = s.slice(p, p + 4 + clen);
this.allHandshakes = concatTA(this.allHandshakes, c);
const cParsedByted = await this.parseCertificate(c);
p += cParsedByted;
if (this.isMhm && (handshakelen+5 == p)){
// another MHM header will follow, read its header
assert(eq(s.slice(p, p+=1), [0x16])); // Type: Handshake
assert(eq(s.slice(p, p+=2), [0x03, 0x03])); // Version: TLS 1.2
// eslint-disable-next-line no-unused-vars
const handshakelen = ba2int(s.slice(p, p+=2)); // This may be the length of multiple handshake messages (MHM)
}
reclenMhm = 0;
if (!this.isMhm){
// read the TLS Record header
assert(eq(s.slice(p, p+=3), [0x16, 0x03, 0x03])); // Type: Handshake # Version: TLS 1.2
reclenMhm = ba2int(s.slice(p, p+=2));
}
// Parse Server Key Exchange
const skelen = ba2int(s.slice(p+1, p+4));
if (!this.isMhm){
assert(reclenMhm == skelen+4);}
const ske = s.slice(p, p + 4+ skelen);
this.allHandshakes = concatTA(this.allHandshakes, ske);
const skeParsedByted = await this.parseServerKeyExchange(ske);
p += skeParsedByted;
// Parse Server Hello Done
if (!this.isMhm) {
// read the TLS Record header
assert(eq(s.slice(p, p+=3), [0x16, 0x03, 0x03])); // Type: Handshake # Version: TLS 1.2
// eslint-disable-next-line no-unused-vars
const reclen = ba2int(s.slice(p, p+=2));
}
const shd = s.slice(p, p+=4);
assert(eq(shd, [0x0e, 0x00, 0x00, 0x00]));
assert(p == s.length);
this.updateAllHandshakes(shd);
return this.serverEcPubkey;
}
// buildClientKeyExchange builds the TLS handshake's Client Key Exchange message
// cpubBytes is client's pubkey for the ECDH
async buildClientKeyExchange(cpubBytes){
let tmp = [0x10]; // Handshake type: Client Key Exchange
tmp.push(0x00, 0x00, 0x42); // Length
tmp.push(0x41); // Pubkey Length: 65
// 0x04 means compressed pubkey format
this.cke = concatTA(new Uint8Array(tmp), new Uint8Array([0x04]), cpubBytes);
this.updateAllHandshakes(this.cke);
}
getRandoms(){
return [this.clientRandom, this.serverRandom];
}
getAllHandshakes(){
return this.allHandshakes;
}
getCertPath(){
return this.certPath;
}
getRSAsignature(){
return this.rsaSig;
}
// sendClientFinished accepts encrypted Client Finished (CF), auth tag for CF, verify_data for CF.
// It then sends Client Key Exchange, Change Cipher Spec and encrypted Client Finished
async sendClientFinished(encCF, tagCF){
const cke_tls_record_header = new Uint8Array([0x16, 0x03, 0x03, 0x00, 0x46]); // Type: Handshake, Version: TLS 1.2, Length
const ccs = new Uint8Array([0x14, 0x03, 0x03, 0x00, 0x01, 0x01]);
const client_finished = concatTA(int2ba(1, 8), encCF, tagCF);
// Finished message of 40 (0x28) bytes length
const data_to_send = concatTA(cke_tls_record_header, this.cke, ccs,
new Uint8Array([0x16, 0x03, 0x03, 0x00, 0x28]), client_finished);
this.sckt.send(data_to_send);
}
updateAllHandshakes(appendMsg){
this.allHandshakes = concatTA(this.allHandshakes, appendMsg);
}
// receiveServerFinished receives Change Cipher Spec and encrypted Server Finished.
// Returns encrypted Server Finished
async receiveServerFinished(){
const data = await this.sckt.recv(true);
if (eq(data.slice(0,2), [0x15, 0x03])){
console.log('Server sent Alert instead of Server Finished');
throw('Server sent Alert instead of Server Finished');
}
// Parse CCS and Server's Finished
const ccs_server = data.slice(0,6);
assert(eq(ccs_server, [0x14, 0x03, 0x03, 0x00, 0x01, 0x01]));
let f = null; // server finished
if (data.length === 6) {
// didnt receive the Server Finished, try again
f = await this.sckt.recv(true);
}
else {
f = data.slice(6);
}
assert (eq(f.slice(0,5), [0x16, 0x03, 0x03, 0x00, 0x28]));
const encSF = f.slice(5, 45); // encrypted Server Finished
// There may be some extra data received after the Server Finished. We ignore it.
return encSF;
}
async buildAndSendRequest(gctrBlocks, ghashOutputs, encRequestBlocks){
// authTags contains authentication tag for each TLS record in the request
// (For now there's a limit of 1 TLS record for the client request)
const authTags = [];
assert(ghashOutputs.length === gctrBlocks.length);
for (let i=0; i < ghashOutputs.length; i++){
authTags[i] = xor(ghashOutputs[i], gctrBlocks[i]);
}
const finalRecords = [];
const TLSRecord = concatTA(int2ba(2, 8), ...encRequestBlocks, authTags[0]);
finalRecords.push(TLSRecord);
let appdata = new Uint8Array();
for (let i=0; i< finalRecords.length; i++){
appdata = concatTA(
appdata,
new Uint8Array([0x17, 0x03, 0x03]), // Type: Application data, TLS Version 1.2
int2ba(finalRecords[i].length, 2), // 2-byte length of encrypted data
finalRecords[i]);
}
console.log('sending http request');
this.sckt.send(appdata);
}
// receiveServerResponse returns encrypted server response split into TLS records
async receiveServerResponse(){
const server_response = await this.sckt.recv();
this.sckt.close();
console.log('server_response.length', server_response.length);
const encRecords = this.splitResponseIntoRecords(server_response);
return encRecords;
}
// Not in use now, will be used for the selective hiding in the future
async hashesOfAllCounterBlocks(server_write_key, server_write_IV, encRecords){
// calculate hashes of every counter
var encCounters = []; // encrypted counters for each TLS recorsd
var hashesOfEncCounters = []; // hash of each AES block
for (var i=0; i<encRecords.length; i++){
var rec = encRecords[i];
var numberOfBlocks = Math.ceil((rec.length-8-16) / 16);
var nonce = rec.slice(0,8);
var hashesOfEncCountersInRecord = [];
var encCountersInRecord = [];
for (var j=0; j<numberOfBlocks; j++){
var count_ba = int2ba(j+2, 4); // block counter starts with 2
var counter = concatTA(server_write_IV, nonce, count_ba);
let counter_enc = AESECBencrypt(server_write_key, counter);
var hashOfEncCounter = await sha256(counter_enc);
hashesOfEncCountersInRecord.push(hashOfEncCounter);
encCountersInRecord.push(counter_enc);
}
hashesOfEncCounters.push(concatTA(...hashesOfEncCountersInRecord));
encCountersInRecord = [].concat.apply([], encCountersInRecord); // flatten
encCounters.push(encCountersInRecord);
// flatten the array and hash it
return await sha256([].concat.apply([], hashesOfEncCounters));
}
}
// split a raw TLS response into encrypted application layer records
splitResponseIntoRecords(s){
var records = [];
var p = 0; // position in the stream
while (p < s.length){
if (! eq(s.slice(p,p+3), [0x17,0x03,0x03])){
if (eq(s.slice(p,p+3), [0x15,0x03,0x03])){
// if the alert is not the first record, then it is most likely a
// close_notify
if (records.length == 0){
console.log('Server sent Alert instead of response');
throw('Server sent Alert instead of response');
}
}
else{
console.log('Server sent an unknown message');
throw('Server sent an unknown message');
}
}
p+=3;
let reclen = ba2int(s.slice(p, p+=2));
let record = s.slice(p, p+=reclen);
records.push(record);
}
assert(p == s.length, 'The server sent a misformatted reponse');
return records;
}
// verify signature over EC parameters from Server Key Exchange
static async verifyECParamsSig(cert, ECpubkey, sig, cr, sr){
const modulus = new Uint8Array(cert.subjectPublicKeyInfo.parsedKey.modulus.valueBlock.valueHex);
// JSON web key format for public key with exponent 65537
const jwk = {'kty':'RSA', 'use':'sig', 'e': 'AQAB', 'n': b64urlencode(modulus)};
// 4 bytes of EC Diffie-Hellman Server Params + pubkey
const to_be_signed = concatTA(cr, sr, new Uint8Array([0x03, 0x00, 0x17, 0x41]), ECpubkey);
const rsa_pubkey = await crypto.subtle.importKey(
'jwk',
jwk,
{name: 'RSASSA-PKCS1-v1_5', hash:'SHA-256'},
true, ['verify']);
try {
var result = await crypto.subtle.verify('RSASSA-PKCS1-v1_5', rsa_pubkey, sig.buffer, to_be_signed.buffer);
} catch (e) {
console.log('EC parameters signature verification failed');
console.log(e, e.name);
throw('EC parameters signature verification failed');
}
return result;
}
}
export async function decrypt_tls_responseV6(encRecords, key, IV){
const cryptoKey = await crypto.subtle.importKey(
'raw', key.buffer, 'AES-GCM', true, ['decrypt']);
let seq_num = 0; // seq_num 0 was in the Server Finished message, we will start with seq_num 1
const plaintext = [];
for (let i=0; i < encRecords.length; i++){
const rec = encRecords[i];
if (i === encRecords.length-1 && rec.length === 26){
// this is close_notify which we don't include in the output
continue;
}
let tmp = [];
seq_num += 1;
tmp.push(...int2ba(seq_num, 8));
tmp.push(...[0x17,0x03,0x03]); // type 0x17 = Application Data, TLS Version 1.2
// len(unencrypted data) == len (encrypted data) - len(explicit nonce) - len (auth tag)
tmp.push(...int2ba(rec.length - 8 - 16, 2));
const aad = new Uint8Array(tmp);// additional authenticated data
const nonce = concatTA(IV, rec.slice(0,8));
plaintext.push( new Uint8Array (await crypto.subtle.decrypt(
{name: 'AES-GCM', iv: nonce.buffer, additionalData: aad.buffer},
cryptoKey,
rec.slice(8).buffer)));
}
return plaintext;
}
export async function getExpandedKeys(preMasterSecret, cr, sr){
const Secret_CryptoKey = await crypto.subtle.importKey(
'raw',
preMasterSecret.buffer,
{name: 'HMAC', hash:'SHA-256'},
true,
['sign']);
// calculate Master Secret and expanded keys
const seed = concatTA(str2ba('master secret'), cr, sr);
const a0 = seed;
const a1 = new Uint8Array (await crypto.subtle.sign('HMAC', Secret_CryptoKey, a0.buffer));
const a2 = new Uint8Array (await crypto.subtle.sign('HMAC', Secret_CryptoKey, a1.buffer));
const p1 = new Uint8Array (await crypto.subtle.sign('HMAC', Secret_CryptoKey, concatTA(a1,seed).buffer));
const p2 = new Uint8Array (await crypto.subtle.sign('HMAC', Secret_CryptoKey, concatTA(a2,seed).buffer));
const ms = concatTA(p1, p2).slice(0,48);
const MS_CryptoKey = await crypto.subtle.importKey('raw', ms.buffer, {name: 'HMAC', hash:'SHA-256'}, true, ['sign']);
// Expand keys
const eseed = concatTA(str2ba('key expansion'), sr, cr);
const ea0 = eseed;
const ea1 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, ea0.buffer));
const ea2 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, ea1.buffer));
const ep1 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, concatTA(ea1,eseed).buffer));
const ep2 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, concatTA(ea2,eseed).buffer));
const ek = concatTA(ep1, ep2).slice(0,40);
// GCM doesnt need MAC keys
const client_write_key = ek.slice(0, 16);
const server_write_key = ek.slice(16, 32);
const client_write_IV = ek.slice(32, 36);
const server_write_IV = ek.slice(36, 40);
return [client_write_key, server_write_key, client_write_IV, server_write_IV, MS_CryptoKey];
}
if (typeof module !== 'undefined'){ // we are in node.js environment
module.exports={
computeCommitHash,
decrypt_tls_responseV5,
start_audit,
getExpandedKeys
};
}

145
core/TLSNotarySession.js Normal file
View File

@@ -0,0 +1,145 @@
import {TWOPC} from './twopc/TWOPC.js';
import {TLS} from './TLS.js';
import {concatTA, sha256, assert, eq, xor, str2ba, pubkeyPEM2raw} from './utils.js';
// class TLSNotarySession impements one notarization session
// (one client request followed by one server response) using the TLSNotary protocol.
export class TLSNotarySession{
// twopc is an instance of class TWOPC. Used to speak to the notary.
twopc;
// tls is an instance of class TLS. Used to speak to the webserver.
tls;
// probeTLS is used to probe the webserver to see if it supports TLSNotary,
// before we start any time-intensive 2PC
probeTLS;
options;
request;
notary;
pm;
constructor(server, port, request, notary, sessionOptions, circuits, progressMonitor){
this.twopc = new TWOPC(notary, request.length, circuits, progressMonitor);
this.tls = new TLS(server, port, request, sessionOptions);
this.probeTLS = new TLS(server, port, request, sessionOptions);
this.request = str2ba(request);
this.notary = notary;
this.pm = progressMonitor;
}
async start(){
await this.probeTLS.buildAndSendClientHello();
await this.probeTLS.receiveAndParseServerHello();
await this.probeTLS.sckt.close();
await this.twopc.preCompute();
await this.tls.buildAndSendClientHello();
const serverEcPubkey = await this.tls.receiveAndParseServerHello();
const serverX = serverEcPubkey.slice(1,33);
const serverY = serverEcPubkey.slice(33,65);
this.pm.update('last_stage', {'current': 3, 'total': 10});
const [pmsShare, cpubBytes] = await this.twopc.getECDHShare(serverX, serverY);
this.pm.update('last_stage', {'current': 4, 'total': 10});
await this.tls.buildClientKeyExchange(cpubBytes);
const [cr, sr] = await this.tls.getRandoms();
const [encCF, tagCF, vdCF] = await this.twopc.run(cr, sr, this.tls.getAllHandshakes(), pmsShare);
// Finished (0x14) with length 12 (0x0c)
this.tls.updateAllHandshakes(concatTA(new Uint8Array([0x14, 0x00, 0x00, 0x0c]), vdCF));
await this.tls.sendClientFinished(encCF, tagCF);
const encSF = await this.tls.receiveServerFinished();
await this.twopc.checkServerFinished(encSF, this.tls.getAllHandshakes());
this.pm.update('last_stage', {'current': 5, 'total': 10});
const encCountersForRequest = await this.twopc.getEncryptedCounters();
this.pm.update('last_stage', {'current': 9, 'total': 10});
const encRequestBlocks = this.encryptRequest(this.request, encCountersForRequest);
const gctrBlocks = await this.twopc.getGctrBlocks();
this.pm.update('last_stage', {'current': 10, 'total': 10});
const [ghashOutputs, ghashInputsBlob] = await this.twopc.getTagFromPowersOfH(encRequestBlocks);
await this.tls.buildAndSendRequest(gctrBlocks, ghashOutputs, encRequestBlocks);
const serverRecords = await this.tls.receiveServerResponse();
await this.tls.sckt.close();
const commitHash = await TLSNotarySession.computeCommitHash(serverRecords);
const [cwkShare, civShare, swkShare, sivShare] = this.twopc.getKeyShares();
const keyShareHash = await sha256(concatTA(cwkShare, civShare, swkShare, sivShare));
const pmsShareHash = await sha256(pmsShare);
const data5 = await this.twopc.send('commitHash', concatTA(
commitHash,
keyShareHash,
pmsShareHash));
this.twopc.destroy();
let o = 0; // offset
const signature = data5.slice(o, o+=64);
const notaryPMSShare = data5.slice(o, o+=32);
const notaryCwkShare = data5.slice(o, o+=16);
const notaryCivShare = data5.slice(o, o+=4);
const notarySwkShare = data5.slice(o, o+=16);
const notarySivShare = data5.slice(o, o+=4);
const timeBytes = data5.slice(o, o+=8);
assert(data5.length == o);
const [eKey, eValidFrom, eValidUntil, eSigByMasterKey] = this.twopc.getEphemeralKey();
// convert certPath to DER.
const certPath = this.tls.getCertPath();
var certs = [];
for (let cert of certPath){
certs.push(new Uint8Array(cert.toSchema(true).toBER(false)));
}
return {
'certificates': certs,
'notarization time': timeBytes,
'server RSA sig': this.tls.getRSAsignature(),
'server pubkey for ECDHE': serverEcPubkey,
'notary PMS share': notaryPMSShare,
'client PMS share': pmsShare,
'client random': cr,
'server random': sr,
'notary client_write_key share': notaryCwkShare,
'notary client_write_iv share': notaryCivShare,
'notary server_write_key share': notarySwkShare,
'notary server_write_iv share': notarySivShare,
'client client_write_key share': cwkShare,
'client client_write_iv share': civShare,
'client server_write_key share': swkShare,
'client server_write_iv share': sivShare,
'client request ciphertext': ghashInputsBlob,
'server response records': serverRecords,
'session signature': signature,
'ephemeral pubkey': eKey,
'ephemeral valid from': eValidFrom,
'ephemeral valid until': eValidUntil,
'ephemeral signed by master key': eSigByMasterKey,
};
}
// encryptRequest encrypts (i.e. XORs) the plaintext request with encrypted counter blocks.
// (This is how AES-GCM encryption works - first counter blocks are AES-ECB-encrypted,
// then encrypted counter blocks are XORes with the plaintext to get the ciphertext.)
encryptRequest(request, encCounterBlocks){
assert(Math.ceil(request.length/16) === encCounterBlocks.length);
const encRequestBlocks = [];
for (let i=0; i < encCounterBlocks.length; i++){
if (i == encCounterBlocks.length-1){
// last block, make sure arrays are of the same length before xoring
let lastBlockLen = this.request.length - i*16;
encRequestBlocks.push(xor(encCounterBlocks[i].slice(0,lastBlockLen), this.request.slice(i*16)));
}
else {
encRequestBlocks.push(xor(encCounterBlocks[i], this.request.slice(i*16, i*16+16)));
}
}
return encRequestBlocks;
}
// computeCommitHash computes a hash over all TLS records with MACs
static async computeCommitHash(encRecords){
return await sha256(concatTA(...encRecords));
}
}

30
core/globals.js Normal file
View File

@@ -0,0 +1,30 @@
export const global = {
// defaultNotaryIP/Port is the default IP address/port of the notary server.
// If this IP address becomes unavailable, Pagesigner will query backupUrl for
// a new notary's IP address and will save the new IP address in the preferences.
// defaultNotaryIP: '127.0.0.1',
defaultNotaryIP: '3.235.172.232',
defaultNotaryPort: 10011,
// backupUrl is the URL to query to get the IP address of another notary
// server in case if defaultNotaryIP is unreachable
backupUrl: 'https://tlsnotary.org/backup_oracle',
// use python for raw socket communication
usePythonBackend: false,
sessionOptions: {
// Future use: use max_fragment_length TLS extension
'useMaxFragmentLength': false,
// can be set to false during debugging to be able to work with self-signed certs
'mustVerifyCert': true
},
// if useHTTP11 is set to true then we use HTTP/1.1 in the request, otherwise
// we use HTTP/1.0. Using HTTP/1.0 is the only way to prevent a webserver from using
// chunked transfer encoding. This may be useful e.g. when webserver response is used
// inside zk proofs and we want simpler parsing without de-chunking.
useHTTP11: true,
// appId is Chrome-only: the id of Chrome app used to send raw TCP packets.
appId: 'oclohfdjoojomkfddjclanpogcnjhemd',
// if useNotaryNoSandbox is set to true, then we fetch notary's pubkey by
// querying /getPubKey and trust it. This is only useful when notary runs
// in a non-sandbox environment.
useNotaryNoSandbox: false
};

319
core/indexeddb.js Normal file
View File

@@ -0,0 +1,319 @@
let db;
let db_blobs;
export async function init_db() {
await new Promise(function(resolve, reject) {
const dbReq = indexedDB.open('PageSigner', 1);
dbReq.onupgradeneeded = function (event) {
// Set the db variable to our database so we can use it!
db = event.target.result;
if (!db.objectStoreNames.contains('sessions')) {
db.createObjectStore('sessions', { keyPath: 'creationTime', autoIncrement: true });
}
if (!db.objectStoreNames.contains('preferences')) {
const preferences = db.createObjectStore('preferences', { keyPath: 'name', autoIncrement: true });
preferences.add({name:'testing', value:false});
preferences.add({name:'verbose', value:true});
preferences.add({name:'firstTimeInitCompleted', value:false});
preferences.add({name:'parsedCircuits', value:{}});
preferences.add({name:'trustedOracle', value:{}});
}
};
dbReq.onsuccess = function (event) {
db = event.target.result;
resolve();
};
dbReq.onerror = function (event) {
alert('error opening database ' + event.target.errorCode);
};
});
await new Promise(function(resolve, reject) {
// We must create a separate DB for blobs. Having a separate store is not a solution because
// i/o operations a very slow when there's blobs in a db
const dbReq2 = indexedDB.open('PageSigner_blobs', 1);
dbReq2.onupgradeneeded = function (event) {
// Set the db variable to our database so we can use it!
db_blobs = event.target.result;
if (!db_blobs.objectStoreNames.contains('sessions')) {
db_blobs.createObjectStore('sessions', { keyPath: 'creationTime', autoIncrement: true });
}
};
dbReq2.onsuccess = function (event) {
db_blobs = event.target.result;
resolve();
};
dbReq2.onerror = function (event) {
alert('error opening database ' + event.target.errorCode);
};
});
}
export async function addNewPreference(key, value){
// check if the preference already exists
const allPreferences = await new Promise(function(resolve, reject) {
const tx = db.transaction(['preferences'], 'readonly');
const store = tx.objectStore('preferences');
const req = store.getAll();
req.onsuccess = function(event) {
// The result of req.onsuccess is an array
resolve(event.target.result);
};
req.onerror = function(event) {
alert('error in cursor request ' + event.target.errorCode);
reject('error in cursor request ' + event.target.errorCode);
};
});
for (let pref of allPreferences){
if (pref['name'] == key){
return;
}
}
// preference does not exist, add it
await new Promise(function(resolve, reject) {
const tx = db.transaction(['preferences'], 'readwrite');
const store = tx.objectStore('preferences');
store.add({name:key, value:value});
tx.oncomplete = function() {
resolve();
};
tx.onerror = function(event) {
alert('error storing ' + event.target.errorCode);
reject();
};
});
}
export async function deleteSession(session) {
await new Promise(function(resolve, reject) {
const tx = db.transaction(['sessions'], 'readwrite');
const sessions = tx.objectStore('sessions');
const req = sessions.delete(session);
req.onsuccess = function(event) {
resolve();
};
});
await new Promise(function(resolve, reject) {
const tx = db_blobs.transaction(['sessions'], 'readwrite');
const sessions = tx.objectStore('sessions');
const req = sessions.delete(session);
req.onsuccess = function(event) {
resolve();
};
});
}
export async function getAllSessions() {
return await new Promise(function(resolve, reject) {
console.log('begin db');
const tx = db.transaction(['sessions'], 'readonly');
const store = tx.objectStore('sessions');
const req = store.getAll();
req.onsuccess = function(event) {
// The result of req.onsuccess is an array
resolve(event.target.result);
console.log('end db');
};
req.onerror = function(event) {
alert('error in cursor request ' + event.target.errorCode);
reject('error in cursor request ' + event.target.errorCode);
};
});
}
export async function saveNewSession(date, host, request, response, pgsg, options){
await new Promise(function(resolve, reject) {
const tx = db.transaction(['sessions'], 'readwrite');
const store = tx.objectStore('sessions');
let isImported = false;
let isEdited = false;
if (options != undefined){
if (options.indexOf('imported') > -1) isImported = true;
if (options.indexOf('edited') > -1) isEdited = true;
}
// sessionName can be changed by the user in the manager window
store.add({
creationTime: date,
sessionName: host,
serverName: host,
isImported: isImported,
isEdited: isEdited,
version: 6});
tx.oncomplete = function() {
resolve();
};
tx.onerror = function(event) {
alert('error storing note ' + event.target.errorCode);
reject();
};
});
await new Promise(function(resolve, reject) {
const tx2 = db_blobs.transaction(['sessions'], 'readwrite');
const store2 = tx2.objectStore('sessions');
store2.add({
creationTime: date,
serverName:host,
request:request,
response:response,
pgsg:pgsg});
tx2.oncomplete = function() {
resolve();
};
tx2.onerror = function(event) {
alert('error storing note ' + event.target.errorCode);
reject();
};
});
}
export async function getSession(idx){
return await new Promise(function(resolve, reject) {
const tx = db.transaction(['sessions'], 'readonly');
const store = tx.objectStore('sessions');
const req = store.get(idx);
req.onsuccess = function(event) {
const entry = event.target.result;
if (entry) {
console.log(entry);
resolve(entry);
} else {
console.log('entry 1 not found');
resolve(null);
}
};
req.onerror = function(event) {
console.log('error getting entry 1 ' + event.target.errorCode);
reject('error getting entry 1 ' + event.target.errorCode);
};
});
}
// get data from blob store
export async function getSessionBlob(idx){
return await new Promise(function(resolve, reject) {
const tx = db_blobs.transaction(['sessions'], 'readonly');
const store = tx.objectStore('sessions');
const req = store.get(idx);
req.onsuccess = function(event) {
const entry = event.target.result;
if (entry) {
resolve(entry);
} else {
console.log('note 1 not found');
resolve(null);
}
};
req.onerror = function(event) {
console.log('error getting entry 1 ' + event.target.errorCode);
reject('error getting entry 1 ' + event.target.errorCode);
};
});
}
export async function getPref(pref){
return await new Promise(function(resolve, reject) {
let tx = db.transaction(['preferences'], 'readonly');
let store = tx.objectStore('preferences');
let req = store.get(pref);
req.onsuccess = function(event) {
let entry = event.target.result;
if (entry) {
console.log(entry);
resolve(entry.value);
} else {
resolve(null);
}
};
req.onerror = function(event) {
console.log('error getting entry 1 ' + event.target.errorCode);
reject('error getting entry 1 ' + event.target.errorCode);
};
});
}
export async function setPref(pref, newvalue) {
await new Promise(function(resolve, reject) {
const tx = db.transaction(['preferences'], 'readwrite');
const store = tx.objectStore('preferences');
const request = store.get(pref);
request.onsuccess = function(event) {
// Get the old value that we want to update
const data = event.target.result;
// update the value(s) in the object that you want to change
data.value = newvalue;
// Put this updated object back into the database.
const requestUpdate = store.put(data);
requestUpdate.onerror = function(event) {
// Do something with the error
reject();
};
requestUpdate.onsuccess = function(event) {
// Success - the data is updated!
resolve();
};
};
});
}
export async function renameSession(id, newname) {
await new Promise(function(resolve, reject) {
// Start a database transaction and get the notes object store
const tx = db.transaction(['sessions'], 'readwrite');
const sessions = tx.objectStore('sessions');
const request = sessions.get(id);
request.onsuccess = function(event) {
// Get the old value that we want to update
const data = event.target.result;
// update the value(s) in the object that you want to change
data.sessionName = newname;
// Put this updated object back into the database.
const requestUpdate = sessions.put(data);
requestUpdate.onerror = function(event) {
// Do something with the error
reject();
};
requestUpdate.onsuccess = function(event) {
// Success - the data is updated!
resolve();
};
};
});
}

342
core/oracles.js Normal file
View File

@@ -0,0 +1,342 @@
import {global} from './globals.js';
import {ba2str, b64decode, assert, ba2int, verifyAttestationDoc, ba2hex, eq,
sha256} from './utils.js';
// rootOfTrust id the id of an EBS snapshot
// essentially the whole oracle verification procedure boils down to proving that an EC2 instance was
// launched from an AMI which was created from the "rootOfTrust" snapshot id.
const rootOfTrust = 'snap-0ccb00d0e0fb4d4da';
// URLFetcher trusted enclave measurements, see
// https://github.com/tlsnotary/URLFetcher
const URLFetcherPCR0 = 'f70217239e8a1cb0f3c010b842a279e2b8d30d3700d7e4722fef22291763479a13783dc76d5219fabbd7e5aa92a7b255';
const URLFetcherPCR1 = 'c35e620586e91ed40ca5ce360eedf77ba673719135951e293121cb3931220b00f87b5a15e94e25c01fecd08fc9139342';
const URLFetcherPCR2 = 'efba114128ccd6af1d1366a12c1ac89e4a4ca5ea1434d779efadfd3ec0d1da5b7c0d8525239fac29ffde2946e07d1c16';
// assuming both events happened on the same day, get the time
// difference between them in seconds
// the time string looks like "2015-04-15T19:00:59.000Z"
function getSecondsDelta(later, sooner) {
assert(later.length == 24);
if (later.slice(0, 11) !== sooner.slice(0, 11)) {
return 999999; // not on the same day
}
const laterTime = later.slice(11, 19).split(':');
const soonerTime = sooner.slice(11, 19).split(':');
const laterSecs = parseInt(laterTime[0]) * 3600 + parseInt(laterTime[1]) * 60 + parseInt(laterTime[2]);
const soonerSecs = parseInt(soonerTime[0]) * 3600 + parseInt(soonerTime[1]) * 60 + parseInt(soonerTime[2]);
return laterSecs - soonerSecs;
}
function checkDescribeInstances(xmlDoc, instanceId, imageId, volumeId) {
try {
assert(xmlDoc.getElementsByTagName('DescribeInstancesResponse').length == 1);
const rs = xmlDoc.getElementsByTagName('reservationSet');
assert(rs.length === 1);
const rs_items = rs[0].children;
assert(rs_items.length === 1);
var ownerId = rs_items[0].getElementsByTagName('ownerId')[0].textContent;
const isets = rs_items[0].getElementsByTagName('instancesSet');
assert(isets.length === 1);
const instances = isets[0].children;
assert(instances.length === 1);
const parent = instances[0];
assert(parent.getElementsByTagName('instanceId')[0].textContent === instanceId);
assert(parent.getElementsByTagName('imageId')[0].textContent === imageId);
assert(parent.getElementsByTagName('instanceState')[0].getElementsByTagName('name')[0].textContent === 'running');
var launchTime = parent.getElementsByTagName('launchTime')[0].textContent;
assert(parent.getElementsByTagName('rootDeviceType')[0].textContent === 'ebs');
assert(parent.getElementsByTagName('rootDeviceName')[0].textContent === '/dev/sda1');
const devices = parent.getElementsByTagName('blockDeviceMapping')[0].getElementsByTagName('item');
assert(devices.length === 1);
assert(devices[0].getElementsByTagName('deviceName')[0].textContent === '/dev/sda1');
assert(devices[0].getElementsByTagName('ebs')[0].getElementsByTagName('status')[0].textContent === 'attached');
var volAttachTime = devices[0].getElementsByTagName('ebs')[0].getElementsByTagName('attachTime')[0].textContent;
assert(devices[0].getElementsByTagName('ebs')[0].getElementsByTagName('volumeId')[0].textContent === volumeId);
// get seconds from "2015-04-15T19:00:59.000Z"
assert(getSecondsDelta(volAttachTime, launchTime) <= 2);
assert(parent.getElementsByTagName('virtualizationType')[0].textContent === 'hvm');
} catch (e) {
throw('checkDescribeInstances exception');
}
return {
'ownerId': ownerId,
'volAttachTime': volAttachTime,
'launchTime': launchTime
};
}
function checkDescribeVolumes(xmlDoc, instanceId, volumeId, volAttachTime, snapshotId) {
try {
assert(xmlDoc.getElementsByTagName('DescribeVolumesResponse').length == 1);
const volumes = xmlDoc.getElementsByTagName('volumeSet')[0].children;
assert(volumes.length === 1);
const volume = volumes[0];
assert(volume.getElementsByTagName('volumeId')[0].textContent === volumeId);
assert(volume.getElementsByTagName('snapshotId')[0].textContent === snapshotId);
assert(volume.getElementsByTagName('status')[0].textContent === 'in-use');
const volCreateTime = volume.getElementsByTagName('createTime')[0].textContent;
const attVolumes = volume.getElementsByTagName('attachmentSet')[0].getElementsByTagName('item');
assert(attVolumes.length === 1);
const attVolume = attVolumes[0];
assert(attVolume.getElementsByTagName('volumeId')[0].textContent === volumeId);
assert(attVolume.getElementsByTagName('instanceId')[0].textContent === instanceId);
assert(attVolume.getElementsByTagName('device')[0].textContent === '/dev/sda1');
assert(attVolume.getElementsByTagName('status')[0].textContent === 'attached');
const attTime = attVolume.getElementsByTagName('attachTime')[0].textContent;
assert(volAttachTime === attTime);
// Crucial: volume was created from snapshot and attached at the same instant
// this guarantees that there was no time window to modify it
assert(getSecondsDelta(attTime, volCreateTime) === 0);
} catch (e) {
throw('checkDescribeVolumes exception');
}
return true;
}
function checkGetConsoleOutput(xmlDoc, instanceId) {
try {
assert(xmlDoc.getElementsByTagName('GetConsoleOutputResponse').length == 1);
assert(xmlDoc.getElementsByTagName('instanceId')[0].textContent === instanceId);
const b64data = xmlDoc.getElementsByTagName('output')[0].textContent;
const logstr = ba2str(b64decode(b64data));
// the only nvme* strings allowed are: nvme, nvme0, nvme0n1, nvme0n1p1
// this ensures that instance has only one disk device. This is
// a redundant check, because the patched ramdisk must halt the boot process if
// it detects more than one disk device.
const allowedSet = ['nvme', 'nvme0', 'nvme0n1', 'nvme0n1p1'];
// match all substrings starting with nvme, folowed by a count of from 0 to 7 symbols from
// the ranges 0-9 and a-z
for (const match of [...logstr.matchAll(/nvme[0-9a-z]{0,7}/g)]){
assert(match.length == 1);
assert(allowedSet.includes(match[0]), 'disallowed nvme* string present in log');
}
const sigmark = 'PageSigner public key for verification';
const pkstartmark = '-----BEGIN PUBLIC KEY-----';
const pkendmark = '-----END PUBLIC KEY-----';
const mark_start = logstr.search(sigmark);
assert(mark_start !== -1);
const pubkey_start = mark_start + logstr.slice(mark_start).search(pkstartmark);
const pubkey_end = pubkey_start + logstr.slice(pubkey_start).search(pkendmark) + pkendmark.length;
const chunk = logstr.slice(pubkey_start, pubkey_end);
const lines = chunk.split('\n');
let pk = pkstartmark + '\n';
for (let i = 1; i < lines.length-1; i++) {
const words = lines[i].split(' ');
pk = pk + words[words.length-1] + '\n';
}
pk = pk + pkendmark;
assert(pk.length > 0);
const pubkeyPEM = pk.split('\r\n').join('\n');
return pubkeyPEM;
} catch (e) {
throw('checkGetConsoleOutput exception');
}
}
// "userData" allows to pass an arbitrary script to the instance at launch. It MUST be empty.
// This is a sanity check because the instance is stripped of the code which parses userData.
function checkDescribeInstanceAttributeUserdata(xmlDoc, instanceId) {
try {
assert(xmlDoc.getElementsByTagName('DescribeInstanceAttributeResponse').length == 1);
assert(xmlDoc.getElementsByTagName('instanceId')[0].textContent === instanceId);
assert(xmlDoc.getElementsByTagName('userData')[0].textContent === '');
} catch (e) {
throw('checkDescribeInstanceAttributeUserdata exception');
}
return true;
}
function checkDescribeInstanceAttributeKernel(xmlDoc, instanceId) {
try {
assert(xmlDoc.getElementsByTagName('DescribeInstanceAttributeResponse').length == 1);
assert(xmlDoc.getElementsByTagName('instanceId')[0].textContent === instanceId);
assert(xmlDoc.getElementsByTagName('kernel')[0].textContent === '');
} catch (e) {
throw('checkDescribeInstanceAttributeKernel exception');
}
return true;
}
function checkDescribeInstanceAttributeRamdisk(xmlDoc, instanceId) {
try {
assert(xmlDoc.getElementsByTagName('DescribeInstanceAttributeResponse').length == 1);
assert(xmlDoc.getElementsByTagName('instanceId')[0].textContent === instanceId);
assert(xmlDoc.getElementsByTagName('ramdisk')[0].textContent === '');
} catch (e) {
throw('checkDescribeInstanceAttributeRamdisk exception');
}
return true;
}
function checkGetUser(xmlDoc, ownerId) {
try {
assert(xmlDoc.getElementsByTagName('GetUserResponse').length == 1);
assert(xmlDoc.getElementsByTagName('UserId')[0].textContent === ownerId);
assert(xmlDoc.getElementsByTagName('Arn')[0].textContent.slice(-(ownerId.length + ':root'.length)) === ownerId + ':root');
} catch (e) {
throw('checkGetUser exception');
}
return true;
}
function checkDescribeImages(xmlDoc, imageId, snapshotId){
try {
assert(xmlDoc.getElementsByTagName('DescribeImagesResponse').length == 1);
const images = xmlDoc.getElementsByTagName('imagesSet')[0].children;
assert(images.length == 1);
const image = images[0];
assert(image.getElementsByTagName('imageId')[0].textContent == imageId);
assert(image.getElementsByTagName('rootDeviceName')[0].textContent == '/dev/sda1');
const devices = image.getElementsByTagName('blockDeviceMapping')[0].children;
assert(devices.length == 1);
const device = devices[0];
const ebs = device.getElementsByTagName('ebs')[0];
assert(ebs.getElementsByTagName('snapshotId')[0].textContent == snapshotId);
} catch (e) {
throw('checkDescribeImages exception');
}
return true;
}
async function fetch_and_parse(obj){
// we don't fetch it ourselves anymore. URLFetcher already did that for us.
// const req = await fetch(obj.request);
// const text = await req.text();
const xmlDoc = new DOMParser().parseFromString(obj.response, 'text/xml');
return xmlDoc;
}
export async function getURLFetcherDoc(IP){
// get URLFetcher document containing attestation for AWS HTTP API URLs needed to
// verify that the oracle was correctly set up.
// https://github.com/tlsnotary/URLFetcher
const resp = await fetch('http://' + IP + ':' + global.defaultNotaryPort + '/getURLFetcherDoc', {
method: 'POST',
mode: 'cors',
cache: 'no-store',
});
return new Uint8Array(await resp.arrayBuffer());
}
export async function verify_oracle(URLFetcherDoc) {
// URLFetcherDoc is a concatenation of 4-byte transcript length | transcript | attestation doc
const transcriptLen = ba2int(URLFetcherDoc.slice(0,4));
const transcript = URLFetcherDoc.slice(4,4+transcriptLen);
const attestation = URLFetcherDoc.slice(4+transcriptLen);
// transcript is a JSON array for each request[ {"request":<URL>, "response":<text>} , {...}]
const transJSON = JSON.parse(ba2str(transcript));
// find which URL corresponds to which API call
const markers = [
{'DescribeInstances': 'DI'},
{'DescribeVolumes': 'DV'},
{'GetConsoleOutput': 'GCO'},
{'GetUser': 'GU'},
{'userData': 'DIAud'},
{'kernel': 'DIAk'},
{'ramdisk': 'DIAr'},
{'DescribeImages': 'DImg'}];
const o = {};
for (let i=0; i < markers.length; i++){
const key = Object.keys(markers[i]);
for (let j=0; j < transJSON.length; j++){
if (transJSON[j].request.indexOf(key) > -1){
o[markers[i][key]] = transJSON[j];
}
}
}
assert(Object.keys(o).length === markers.length);
// check that the URLs are formatted in a canonical way
// Note that AWS expects URL params to be sorted alphabetically. If we put them in
// arbitrary order, the query will be rejected
// "AWSAccessKeyId" should be the same in all URLs to prove that the queries are made
// on behalf of AWS user "root". Otherwise, a potential attack opens up when AWS APi calls
// are made on behalf of a user with limited privileges for whom the API report only
// partial information.
const AWSAccessKeyId = o.DI.request.match(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId=[A-Z0-9]{20}'))[0].split('=')[1];
// We only allow oracles instantiated from TLSNotary's AWS account.
assert(AWSAccessKeyId === 'AKIAI2NJVYXCCAQDCC5Q');
assert(AWSAccessKeyId.length === 20);
const instanceId = o.DI.request.match(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=DescribeInstances&Expires=2030-01-01&InstanceId=i-[a-f0-9]{17}'))[0].split('=')[4];
assert(instanceId.length === 19);
const volumeId = o.DV.request.match(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=DescribeVolumes&Expires=2030-01-01&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&VolumeId=vol-[a-f0-9]{17}'))[0].split('=')[7];
assert(volumeId.length === 21);
const amiId = o.DImg.request.match(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=DescribeImages&Expires=2030-01-01&ImageId.1=ami-[a-f0-9]{17}'))[0].split('=')[4];
assert(amiId.length === 21);
assert(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=DescribeInstances&Expires=2030-01-01&InstanceId='+instanceId+'&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=[a-zA-Z0-9%]{46,56}$').test(o.DI.request));
assert(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=DescribeVolumes&Expires=2030-01-01&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&VolumeId='+volumeId+'&Signature=[a-zA-Z0-9%]{46,56}$').test(o.DV.request));
assert(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=GetConsoleOutput&Expires=2030-01-01&InstanceId='+instanceId+'&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=[a-zA-Z0-9%]{46,56}$').test(o.GCO.request));
assert(new RegExp('^https://iam.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=GetUser&Expires=2030-01-01&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2010-05-08&Signature=[a-zA-Z0-9%]{46,56}$').test(o.GU.request));
assert(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=DescribeInstanceAttribute&Attribute=userData&Expires=2030-01-01&InstanceId='+instanceId+'&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=[a-zA-Z0-9%]{46,56}$').test(o.DIAud.request));
assert(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=DescribeInstanceAttribute&Attribute=kernel&Expires=2030-01-01&InstanceId='+instanceId+'&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=[a-zA-Z0-9%]{46,56}$').test(o.DIAk.request));
assert(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=DescribeInstanceAttribute&Attribute=ramdisk&Expires=2030-01-01&InstanceId='+instanceId+'&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=[a-zA-Z0-9%]{46,56}$').test(o.DIAr.request));
assert(new RegExp('^https://ec2.us-east-1.amazonaws.com/\\?AWSAccessKeyId='+AWSAccessKeyId+'&Action=DescribeImages&Expires=2030-01-01&ImageId.1='+amiId+'&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=[a-zA-Z0-9%]{46,56}$').test(o.DImg.request));
const xmlDocDI = await fetch_and_parse(o.DI);
const rv = checkDescribeInstances(xmlDocDI, instanceId, amiId, volumeId);
const volAttachTime = rv.volAttachTime;
const ownerId = rv.ownerId;
const xmlDocDV = await fetch_and_parse(o.DV);
checkDescribeVolumes(xmlDocDV, instanceId, volumeId, volAttachTime, rootOfTrust);
const xmlDocGU = await fetch_and_parse(o.GU);
checkGetUser(xmlDocGU, ownerId);
const xmlDocGCO = await fetch_and_parse(o.GCO);
const pubkeyPEM = checkGetConsoleOutput(xmlDocGCO, instanceId);
const xmlDocDIAud = await fetch_and_parse(o.DIAud);
checkDescribeInstanceAttributeUserdata(xmlDocDIAud, instanceId);
const xmlDocDIAk = await fetch_and_parse(o.DIAk);
checkDescribeInstanceAttributeKernel(xmlDocDIAk, instanceId);
const xmlDocDIAr = await fetch_and_parse(o.DIAr);
checkDescribeInstanceAttributeRamdisk(xmlDocDIAr, instanceId);
const xmlDocDImg = await fetch_and_parse(o.DImg);
checkDescribeImages(xmlDocDImg, amiId, rootOfTrust);
// verify the attestation document
const attestRV = await verifyAttestationDoc(attestation);
assert(eq(attestRV[0], await sha256(transcript)));
assert(URLFetcherPCR0 === ba2hex(attestRV[1]));
assert(URLFetcherPCR1 === ba2hex(attestRV[2]));
assert(URLFetcherPCR2 === ba2hex(attestRV[3]));
console.log('oracle verification successfully finished');
return pubkeyPEM;
}
if (typeof module !== 'undefined'){ // we are in node.js environment
module.exports={
check_oracle: verify_oracle,
oracle,
};
}

32
core/third-party/SOURCES.third-party vendored Normal file
View File

@@ -0,0 +1,32 @@
pkijs folder contains files from the following sources:
bytestream.js from https://github.com/PeculiarVentures/ByteStream.js/blob/681f2d08b1102dcd39b8a57e989dcdeadf34623d/src/bytestream.js
asn1.js from https://github.com/PeculiarVentures/ASN1.js/blob/5c64632d8d08955f07a9f80b9518a84d76f605cd/src/asn1.js
pvutils.js from https://github.com/PeculiarVentures/pvutils/blob/2d15272c34f05581eb268802c5bcfab7c4c04e03/src/utils.js
the rest of the *.js files are from
https://github.com/PeculiarVentures/PKI.js/tree/fa83e1ef003c8b450832b8c878cd36b73aaa0dee/src
After putting all the files into the same folder, we changed the paths with
sed -i 's#from "asn1js"#from "./asn1.js"#g' *
sed -i 's#from "pvutils"#from "./pvutils.js"#g' *
sed -i 's#from "bytestreamjs"#from "./bytestream.js"#g' *
bigint-crypto-utils.esm.js from
https://github.com/juanelas/bigint-crypto-utils/tree/954321199d4a8038c3d27113aec825ff2e5bf544/dist/bundles
https://github.com/Azero123/simple-js-ec-math
browserify simple-js-ec-math/src/index.js --standalone ECSimple > simple-js-ec-math.js
https://raw.githubusercontent.com/dchest/fast-sha256-js/master/sha256.js > fastsha256.js
cbor.js and cose.js are used to verify the enclave attestation document
cbor.js is from https://github.com/paroga/cbor-js/blob/master/cbor.js
cose.js was built with browserify coseverify.js --standalone COSE > cose.js
pako.js ---> SOURCE???
certs.txt is Mozilla'a root store taken from
https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReportPEMCSV

406
core/third-party/cbor.js vendored Normal file
View File

@@ -0,0 +1,406 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Patrick Gansterer <paroga@paroga.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
(function(global, undefined) { "use strict";
var POW_2_24 = 5.960464477539063e-8,
POW_2_32 = 4294967296,
POW_2_53 = 9007199254740992;
function encode(value) {
var data = new ArrayBuffer(256);
var dataView = new DataView(data);
var lastLength;
var offset = 0;
function prepareWrite(length) {
var newByteLength = data.byteLength;
var requiredLength = offset + length;
while (newByteLength < requiredLength)
newByteLength <<= 1;
if (newByteLength !== data.byteLength) {
var oldDataView = dataView;
data = new ArrayBuffer(newByteLength);
dataView = new DataView(data);
var uint32count = (offset + 3) >> 2;
for (var i = 0; i < uint32count; ++i)
dataView.setUint32(i << 2, oldDataView.getUint32(i << 2));
}
lastLength = length;
return dataView;
}
function commitWrite() {
offset += lastLength;
}
function writeFloat64(value) {
commitWrite(prepareWrite(8).setFloat64(offset, value));
}
function writeUint8(value) {
commitWrite(prepareWrite(1).setUint8(offset, value));
}
function writeUint8Array(value) {
var dataView = prepareWrite(value.length);
for (var i = 0; i < value.length; ++i)
dataView.setUint8(offset + i, value[i]);
commitWrite();
}
function writeUint16(value) {
commitWrite(prepareWrite(2).setUint16(offset, value));
}
function writeUint32(value) {
commitWrite(prepareWrite(4).setUint32(offset, value));
}
function writeUint64(value) {
var low = value % POW_2_32;
var high = (value - low) / POW_2_32;
var dataView = prepareWrite(8);
dataView.setUint32(offset, high);
dataView.setUint32(offset + 4, low);
commitWrite();
}
function writeTypeAndLength(type, length) {
if (length < 24) {
writeUint8(type << 5 | length);
} else if (length < 0x100) {
writeUint8(type << 5 | 24);
writeUint8(length);
} else if (length < 0x10000) {
writeUint8(type << 5 | 25);
writeUint16(length);
} else if (length < 0x100000000) {
writeUint8(type << 5 | 26);
writeUint32(length);
} else {
writeUint8(type << 5 | 27);
writeUint64(length);
}
}
function encodeItem(value) {
var i;
if (value === false)
return writeUint8(0xf4);
if (value === true)
return writeUint8(0xf5);
if (value === null)
return writeUint8(0xf6);
if (value === undefined)
return writeUint8(0xf7);
switch (typeof value) {
case "number":
if (Math.floor(value) === value) {
if (0 <= value && value <= POW_2_53)
return writeTypeAndLength(0, value);
if (-POW_2_53 <= value && value < 0)
return writeTypeAndLength(1, -(value + 1));
}
writeUint8(0xfb);
return writeFloat64(value);
case "string":
var utf8data = [];
for (i = 0; i < value.length; ++i) {
var charCode = value.charCodeAt(i);
if (charCode < 0x80) {
utf8data.push(charCode);
} else if (charCode < 0x800) {
utf8data.push(0xc0 | charCode >> 6);
utf8data.push(0x80 | charCode & 0x3f);
} else if (charCode < 0xd800) {
utf8data.push(0xe0 | charCode >> 12);
utf8data.push(0x80 | (charCode >> 6) & 0x3f);
utf8data.push(0x80 | charCode & 0x3f);
} else {
charCode = (charCode & 0x3ff) << 10;
charCode |= value.charCodeAt(++i) & 0x3ff;
charCode += 0x10000;
utf8data.push(0xf0 | charCode >> 18);
utf8data.push(0x80 | (charCode >> 12) & 0x3f);
utf8data.push(0x80 | (charCode >> 6) & 0x3f);
utf8data.push(0x80 | charCode & 0x3f);
}
}
writeTypeAndLength(3, utf8data.length);
return writeUint8Array(utf8data);
default:
var length;
if (Array.isArray(value)) {
length = value.length;
writeTypeAndLength(4, length);
for (i = 0; i < length; ++i)
encodeItem(value[i]);
} else if (value instanceof Uint8Array) {
writeTypeAndLength(2, value.length);
writeUint8Array(value);
} else {
var keys = Object.keys(value);
length = keys.length;
writeTypeAndLength(5, length);
for (i = 0; i < length; ++i) {
var key = keys[i];
encodeItem(key);
encodeItem(value[key]);
}
}
}
}
encodeItem(value);
if ("slice" in data)
return data.slice(0, offset);
var ret = new ArrayBuffer(offset);
var retView = new DataView(ret);
for (var i = 0; i < offset; ++i)
retView.setUint8(i, dataView.getUint8(i));
return ret;
}
function decode(data, tagger, simpleValue) {
var dataView = new DataView(data);
var offset = 0;
if (typeof tagger !== "function")
tagger = function(value) { return value; };
if (typeof simpleValue !== "function")
simpleValue = function() { return undefined; };
function commitRead(length, value) {
offset += length;
return value;
}
function readArrayBuffer(length) {
return commitRead(length, new Uint8Array(data, offset, length));
}
function readFloat16() {
var tempArrayBuffer = new ArrayBuffer(4);
var tempDataView = new DataView(tempArrayBuffer);
var value = readUint16();
var sign = value & 0x8000;
var exponent = value & 0x7c00;
var fraction = value & 0x03ff;
if (exponent === 0x7c00)
exponent = 0xff << 10;
else if (exponent !== 0)
exponent += (127 - 15) << 10;
else if (fraction !== 0)
return (sign ? -1 : 1) * fraction * POW_2_24;
tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13);
return tempDataView.getFloat32(0);
}
function readFloat32() {
return commitRead(4, dataView.getFloat32(offset));
}
function readFloat64() {
return commitRead(8, dataView.getFloat64(offset));
}
function readUint8() {
return commitRead(1, dataView.getUint8(offset));
}
function readUint16() {
return commitRead(2, dataView.getUint16(offset));
}
function readUint32() {
return commitRead(4, dataView.getUint32(offset));
}
function readUint64() {
return readUint32() * POW_2_32 + readUint32();
}
function readBreak() {
if (dataView.getUint8(offset) !== 0xff)
return false;
offset += 1;
return true;
}
function readLength(additionalInformation) {
if (additionalInformation < 24)
return additionalInformation;
if (additionalInformation === 24)
return readUint8();
if (additionalInformation === 25)
return readUint16();
if (additionalInformation === 26)
return readUint32();
if (additionalInformation === 27)
return readUint64();
if (additionalInformation === 31)
return -1;
throw "Invalid length encoding";
}
function readIndefiniteStringLength(majorType) {
var initialByte = readUint8();
if (initialByte === 0xff)
return -1;
var length = readLength(initialByte & 0x1f);
if (length < 0 || (initialByte >> 5) !== majorType)
throw "Invalid indefinite length element";
return length;
}
function appendUtf16Data(utf16data, length) {
for (var i = 0; i < length; ++i) {
var value = readUint8();
if (value & 0x80) {
if (value < 0xe0) {
value = (value & 0x1f) << 6
| (readUint8() & 0x3f);
length -= 1;
} else if (value < 0xf0) {
value = (value & 0x0f) << 12
| (readUint8() & 0x3f) << 6
| (readUint8() & 0x3f);
length -= 2;
} else {
value = (value & 0x0f) << 18
| (readUint8() & 0x3f) << 12
| (readUint8() & 0x3f) << 6
| (readUint8() & 0x3f);
length -= 3;
}
}
if (value < 0x10000) {
utf16data.push(value);
} else {
value -= 0x10000;
utf16data.push(0xd800 | (value >> 10));
utf16data.push(0xdc00 | (value & 0x3ff));
}
}
}
function decodeItem() {
var initialByte = readUint8();
var majorType = initialByte >> 5;
var additionalInformation = initialByte & 0x1f;
var i;
var length;
if (majorType === 7) {
switch (additionalInformation) {
case 25:
return readFloat16();
case 26:
return readFloat32();
case 27:
return readFloat64();
}
}
length = readLength(additionalInformation);
if (length < 0 && (majorType < 2 || 6 < majorType))
throw "Invalid length";
switch (majorType) {
case 0:
return length;
case 1:
return -1 - length;
case 2:
if (length < 0) {
var elements = [];
var fullArrayLength = 0;
while ((length = readIndefiniteStringLength(majorType)) >= 0) {
fullArrayLength += length;
elements.push(readArrayBuffer(length));
}
var fullArray = new Uint8Array(fullArrayLength);
var fullArrayOffset = 0;
for (i = 0; i < elements.length; ++i) {
fullArray.set(elements[i], fullArrayOffset);
fullArrayOffset += elements[i].length;
}
return fullArray;
}
return readArrayBuffer(length);
case 3:
var utf16data = [];
if (length < 0) {
while ((length = readIndefiniteStringLength(majorType)) >= 0)
appendUtf16Data(utf16data, length);
} else
appendUtf16Data(utf16data, length);
return String.fromCharCode.apply(null, utf16data);
case 4:
var retArray;
if (length < 0) {
retArray = [];
while (!readBreak())
retArray.push(decodeItem());
} else {
retArray = new Array(length);
for (i = 0; i < length; ++i)
retArray[i] = decodeItem();
}
return retArray;
case 5:
var retObject = {};
for (i = 0; i < length || length < 0 && !readBreak(); ++i) {
var key = decodeItem();
retObject[key] = decodeItem();
}
return retObject;
case 6:
return tagger(decodeItem(), length);
case 7:
switch (length) {
case 20:
return false;
case 21:
return true;
case 22:
return null;
case 23:
return undefined;
default:
return simpleValue(length);
}
}
}
var ret = decodeItem();
if (offset !== data.byteLength)
throw "Remaining bytes";
return ret;
}
var obj = { encode: encode, decode: decode };
if (typeof define === "function" && define.amd)
define("cbor/cbor", obj);
else if (typeof module !== "undefined" && module.exports)
module.exports = obj;
else if (!global.CBOR)
global.CBOR = obj;
})(this);

3803
core/third-party/certs.txt vendored Normal file

File diff suppressed because it is too large Load Diff

76
core/third-party/chacha.js vendored Normal file
View File

@@ -0,0 +1,76 @@
/* This is free and unencumbered software released into the public domain. */
"use strict";
const CHACHA_ROUNDS = 20;
const CHACHA_KEYSIZE = 32;
const CHACHA_IVSIZE = 8;
/**
* Create a new ChaCha generator seeded from the given KEY and IV.
* Both KEY and IV must be ArrayBuffer objects of at least the
* appropriate byte lengths (CHACHA_KEYSIZE, CHACHA_IVSIZE). Bytes
* beyond the specified length are ignored.
*
* Returns a closure that takes no arguments. This closure, when
* invoked, returns a 64-byte ArrayBuffer of the next 64 bytes of
* output. Note: This buffer object is "owned" by the closure, and
* the same buffer object is always returned, each time filled with
* fresh output.
*/
function ChaCha(key, iv) {
if (key.byteLength < CHACHA_KEYSIZE)
throw new Error('key too short');
if (iv.byteLength < CHACHA_IVSIZE)
throw new Error('IV too short');
const state = new Uint32Array(16);
let view = new DataView(key);
state[ 0] = 0x61707865; // "expand 32-byte k"
state[ 1] = 0x3320646e; //
state[ 2] = 0x79622d32; //
state[ 3] = 0x6b206574; //
state[ 4] = view.getUint32( 0, true);
state[ 5] = view.getUint32( 4, true);
state[ 6] = view.getUint32( 8, true);
state[ 7] = view.getUint32(12, true);
state[ 8] = view.getUint32(16, true);
state[ 9] = view.getUint32(20, true);
state[10] = view.getUint32(24, true);
state[11] = view.getUint32(28, true);
view = new DataView(iv);
state[14] = view.getUint32( 0, true);
state[15] = view.getUint32( 4, true);
/* Generator */
const output = new Uint32Array(16);
const outview = new DataView(output.buffer);
return function() {
function quarterround(x, a, b, c, d) {
function rotate(v, n) { return (v << n) | (v >>> (32 - n)); }
x[a] += x[b]; x[d] = rotate(x[d] ^ x[a], 16);
x[c] += x[d]; x[b] = rotate(x[b] ^ x[c], 12);
x[a] += x[b]; x[d] = rotate(x[d] ^ x[a], 8);
x[c] += x[d]; x[b] = rotate(x[b] ^ x[c], 7);
}
output.set(state);
for (let i = 0; i < CHACHA_ROUNDS; i += 2) {
quarterround(output, 0, 4, 8, 12);
quarterround(output, 1, 5, 9, 13);
quarterround(output, 2, 6, 10, 14);
quarterround(output, 3, 7, 11, 15);
quarterround(output, 0, 5, 10, 15);
quarterround(output, 1, 6, 11, 12);
quarterround(output, 2, 7, 8, 13);
quarterround(output, 3, 4, 9, 14);
}
for (let i = 0; i < 16; i++)
outview.setUint32(i * 4, output[i] + state[i], true);
state[12]++;
if (state[12] == 0) {
state[13]++;
if (state[13] == 0) {
/* One zebibyte of output reached! */
throw new Error('output exhausted');
}
}
return output.buffer;
}
}

36781
core/third-party/cose.js vendored Normal file

File diff suppressed because it is too large Load Diff

31
core/third-party/coseverify.js vendored Normal file
View File

@@ -0,0 +1,31 @@
// this file is not used by the extension, it serves as an input to to create cose.js with:
// browserify coseverify.js --standalone COSE > cose.js
const cose = require('cose-js');
// x,y,doc is an ArrayBuffer
const verify = function (x, y, doc){
const verifier = {
'key': {
'x': Buffer.from(x),
'y': Buffer.from(y)
}
};
cose.sign.verify(
Buffer.from(doc),
verifier,
{defaultType: 18})
.then((buf) => {
console.log("Verification successful")
//console.log('Verified message: ' + buf.toString('utf8'));
}).catch((error) => {
console.log(error);
});
}
if (typeof module !== 'undefined'){ //we are in node.js environment
module.exports={
verify
}
}

427
core/third-party/fastsha256.js vendored Normal file
View File

@@ -0,0 +1,427 @@
(function (root, factory) {
// Hack to make all exports of this module sha256 function object properties.
var exports = {};
factory(exports);
var fastsha256 = exports["default"];
for (var k in exports) {
fastsha256[k] = exports[k];
}
if (typeof module === 'object' && typeof module.exports === 'object') {
module.exports = fastsha256;
} else if (typeof define === 'function' && define.amd) {
define(function() { return fastsha256; });
} else {
root.fastsha256 = fastsha256;
}
})(this, function(exports) {
"use strict";
exports.__esModule = true;
// SHA-256 (+ HMAC and PBKDF2) for JavaScript.
//
// Written in 2014-2016 by Dmitry Chestnykh.
// Public domain, no warranty.
//
// Functions (accept and return Uint8Arrays):
//
// sha256(message) -> hash
// sha256.hmac(key, message) -> mac
// sha256.pbkdf2(password, salt, rounds, dkLen) -> dk
//
// Classes:
//
// new sha256.Hash()
// new sha256.HMAC(key)
//
exports.digestLength = 32;
exports.blockSize = 64;
// SHA-256 constants
var K = new Uint32Array([
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]);
function hashBlocks(w, v, p, pos, len) {
var a, b, c, d, e, f, g, h, u, i, j, t1, t2;
while (len >= 64) {
a = v[0];
b = v[1];
c = v[2];
d = v[3];
e = v[4];
f = v[5];
g = v[6];
h = v[7];
for (i = 0; i < 16; i++) {
j = pos + i * 4;
w[i] = (((p[j] & 0xff) << 24) | ((p[j + 1] & 0xff) << 16) |
((p[j + 2] & 0xff) << 8) | (p[j + 3] & 0xff));
}
for (i = 16; i < 64; i++) {
u = w[i - 2];
t1 = (u >>> 17 | u << (32 - 17)) ^ (u >>> 19 | u << (32 - 19)) ^ (u >>> 10);
u = w[i - 15];
t2 = (u >>> 7 | u << (32 - 7)) ^ (u >>> 18 | u << (32 - 18)) ^ (u >>> 3);
w[i] = (t1 + w[i - 7] | 0) + (t2 + w[i - 16] | 0);
}
for (i = 0; i < 64; i++) {
t1 = (((((e >>> 6 | e << (32 - 6)) ^ (e >>> 11 | e << (32 - 11)) ^
(e >>> 25 | e << (32 - 25))) + ((e & f) ^ (~e & g))) | 0) +
((h + ((K[i] + w[i]) | 0)) | 0)) | 0;
t2 = (((a >>> 2 | a << (32 - 2)) ^ (a >>> 13 | a << (32 - 13)) ^
(a >>> 22 | a << (32 - 22))) + ((a & b) ^ (a & c) ^ (b & c))) | 0;
h = g;
g = f;
f = e;
e = (d + t1) | 0;
d = c;
c = b;
b = a;
a = (t1 + t2) | 0;
}
v[0] += a;
v[1] += b;
v[2] += c;
v[3] += d;
v[4] += e;
v[5] += f;
v[6] += g;
v[7] += h;
pos += 64;
len -= 64;
}
return pos;
}
// Hash implements SHA256 hash algorithm.
var Hash = /** @class */ (function () {
function Hash() {
this.digestLength = exports.digestLength;
this.blockSize = exports.blockSize;
// Note: Int32Array is used instead of Uint32Array for performance reasons.
this.state = new Int32Array(8); // hash state
this.temp = new Int32Array(64); // temporary state
this.buffer = new Uint8Array(128); // buffer for data to hash
this.bufferLength = 0; // number of bytes in buffer
this.bytesHashed = 0; // number of total bytes hashed
this.finished = false; // indicates whether the hash was finalized
this.reset();
}
// Resets hash state making it possible
// to re-use this instance to hash other data.
Hash.prototype.reset = function () {
this.state[0] = 0x6a09e667;
this.state[1] = 0xbb67ae85;
this.state[2] = 0x3c6ef372;
this.state[3] = 0xa54ff53a;
this.state[4] = 0x510e527f;
this.state[5] = 0x9b05688c;
this.state[6] = 0x1f83d9ab;
this.state[7] = 0x5be0cd19;
this.bufferLength = 0;
this.bytesHashed = 0;
this.finished = false;
return this;
};
// Cleans internal buffers and re-initializes hash state.
Hash.prototype.clean = function () {
for (var i = 0; i < this.buffer.length; i++) {
this.buffer[i] = 0;
}
for (var i = 0; i < this.temp.length; i++) {
this.temp[i] = 0;
}
this.reset();
};
// Updates hash state with the given data.
//
// Optionally, length of the data can be specified to hash
// fewer bytes than data.length.
//
// Throws error when trying to update already finalized hash:
// instance must be reset to use it again.
Hash.prototype.update = function (data, dataLength) {
if (dataLength === void 0) { dataLength = data.length; }
if (this.finished) {
throw new Error("SHA256: can't update because hash was finished.");
}
var dataPos = 0;
this.bytesHashed += dataLength;
if (this.bufferLength > 0) {
while (this.bufferLength < 64 && dataLength > 0) {
this.buffer[this.bufferLength++] = data[dataPos++];
dataLength--;
}
if (this.bufferLength === 64) {
hashBlocks(this.temp, this.state, this.buffer, 0, 64);
this.bufferLength = 0;
}
}
if (dataLength >= 64) {
dataPos = hashBlocks(this.temp, this.state, data, dataPos, dataLength);
dataLength %= 64;
}
while (dataLength > 0) {
this.buffer[this.bufferLength++] = data[dataPos++];
dataLength--;
}
return this;
};
// Finalizes hash state and puts hash into out.
//
// If hash was already finalized, puts the same value.
Hash.prototype.finish = function (out) {
if (!this.finished) {
var bytesHashed = this.bytesHashed;
var left = this.bufferLength;
var bitLenHi = (bytesHashed / 0x20000000) | 0;
var bitLenLo = bytesHashed << 3;
var padLength = (bytesHashed % 64 < 56) ? 64 : 128;
this.buffer[left] = 0x80;
for (var i = left + 1; i < padLength - 8; i++) {
this.buffer[i] = 0;
}
this.buffer[padLength - 8] = (bitLenHi >>> 24) & 0xff;
this.buffer[padLength - 7] = (bitLenHi >>> 16) & 0xff;
this.buffer[padLength - 6] = (bitLenHi >>> 8) & 0xff;
this.buffer[padLength - 5] = (bitLenHi >>> 0) & 0xff;
this.buffer[padLength - 4] = (bitLenLo >>> 24) & 0xff;
this.buffer[padLength - 3] = (bitLenLo >>> 16) & 0xff;
this.buffer[padLength - 2] = (bitLenLo >>> 8) & 0xff;
this.buffer[padLength - 1] = (bitLenLo >>> 0) & 0xff;
hashBlocks(this.temp, this.state, this.buffer, 0, padLength);
this.finished = true;
}
for (var i = 0; i < 8; i++) {
out[i * 4 + 0] = (this.state[i] >>> 24) & 0xff;
out[i * 4 + 1] = (this.state[i] >>> 16) & 0xff;
out[i * 4 + 2] = (this.state[i] >>> 8) & 0xff;
out[i * 4 + 3] = (this.state[i] >>> 0) & 0xff;
}
return this;
};
// Returns the final hash digest.
Hash.prototype.digest = function () {
var out = new Uint8Array(this.digestLength);
this.finish(out);
return out;
};
// Internal function for use in HMAC for optimization.
Hash.prototype._saveState = function (out) {
for (var i = 0; i < this.state.length; i++) {
out[i] = this.state[i];
}
};
// Internal function for use in HMAC for optimization.
Hash.prototype._restoreState = function (from, bytesHashed) {
for (var i = 0; i < this.state.length; i++) {
this.state[i] = from[i];
}
this.bytesHashed = bytesHashed;
this.finished = false;
this.bufferLength = 0;
};
return Hash;
}());
exports.Hash = Hash;
// HMAC implements HMAC-SHA256 message authentication algorithm.
var HMAC = /** @class */ (function () {
function HMAC(key) {
this.inner = new Hash();
this.outer = new Hash();
this.blockSize = this.inner.blockSize;
this.digestLength = this.inner.digestLength;
var pad = new Uint8Array(this.blockSize);
if (key.length > this.blockSize) {
(new Hash()).update(key).finish(pad).clean();
}
else {
for (var i = 0; i < key.length; i++) {
pad[i] = key[i];
}
}
for (var i = 0; i < pad.length; i++) {
pad[i] ^= 0x36;
}
this.inner.update(pad);
for (var i = 0; i < pad.length; i++) {
pad[i] ^= 0x36 ^ 0x5c;
}
this.outer.update(pad);
this.istate = new Uint32Array(8);
this.ostate = new Uint32Array(8);
this.inner._saveState(this.istate);
this.outer._saveState(this.ostate);
for (var i = 0; i < pad.length; i++) {
pad[i] = 0;
}
}
// Returns HMAC state to the state initialized with key
// to make it possible to run HMAC over the other data with the same
// key without creating a new instance.
HMAC.prototype.reset = function () {
this.inner._restoreState(this.istate, this.inner.blockSize);
this.outer._restoreState(this.ostate, this.outer.blockSize);
return this;
};
// Cleans HMAC state.
HMAC.prototype.clean = function () {
for (var i = 0; i < this.istate.length; i++) {
this.ostate[i] = this.istate[i] = 0;
}
this.inner.clean();
this.outer.clean();
};
// Updates state with provided data.
HMAC.prototype.update = function (data) {
this.inner.update(data);
return this;
};
// Finalizes HMAC and puts the result in out.
HMAC.prototype.finish = function (out) {
if (this.outer.finished) {
this.outer.finish(out);
}
else {
this.inner.finish(out);
this.outer.update(out, this.digestLength).finish(out);
}
return this;
};
// Returns message authentication code.
HMAC.prototype.digest = function () {
var out = new Uint8Array(this.digestLength);
this.finish(out);
return out;
};
return HMAC;
}());
exports.HMAC = HMAC;
// Returns SHA256 hash of data.
function hash(data) {
var h = (new Hash()).update(data);
var digest = h.digest();
h.clean();
return digest;
}
exports.hash = hash;
// Function hash is both available as module.hash and as default export.
exports["default"] = hash;
// Returns HMAC-SHA256 of data under the key.
function hmac(key, data) {
var h = (new HMAC(key)).update(data);
var digest = h.digest();
h.clean();
return digest;
}
exports.hmac = hmac;
// Fills hkdf buffer like this:
// T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
function fillBuffer(buffer, hmac, info, counter) {
// Counter is a byte value: check if it overflowed.
var num = counter[0];
if (num === 0) {
throw new Error("hkdf: cannot expand more");
}
// Prepare HMAC instance for new data with old key.
hmac.reset();
// Hash in previous output if it was generated
// (i.e. counter is greater than 1).
if (num > 1) {
hmac.update(buffer);
}
// Hash in info if it exists.
if (info) {
hmac.update(info);
}
// Hash in the counter.
hmac.update(counter);
// Output result to buffer and clean HMAC instance.
hmac.finish(buffer);
// Increment counter inside typed array, this works properly.
counter[0]++;
}
var hkdfSalt = new Uint8Array(exports.digestLength); // Filled with zeroes.
function hkdf(key, salt, info, length) {
if (salt === void 0) { salt = hkdfSalt; }
if (length === void 0) { length = 32; }
var counter = new Uint8Array([1]);
// HKDF-Extract uses salt as HMAC key, and key as data.
var okm = hmac(salt, key);
// Initialize HMAC for expanding with extracted key.
// Ensure no collisions with `hmac` function.
var hmac_ = new HMAC(okm);
// Allocate buffer.
var buffer = new Uint8Array(hmac_.digestLength);
var bufpos = buffer.length;
var out = new Uint8Array(length);
for (var i = 0; i < length; i++) {
if (bufpos === buffer.length) {
fillBuffer(buffer, hmac_, info, counter);
bufpos = 0;
}
out[i] = buffer[bufpos++];
}
hmac_.clean();
buffer.fill(0);
counter.fill(0);
return out;
}
exports.hkdf = hkdf;
// Derives a key from password and salt using PBKDF2-HMAC-SHA256
// with the given number of iterations.
//
// The number of bytes returned is equal to dkLen.
//
// (For better security, avoid dkLen greater than hash length - 32 bytes).
function pbkdf2(password, salt, iterations, dkLen) {
var prf = new HMAC(password);
var len = prf.digestLength;
var ctr = new Uint8Array(4);
var t = new Uint8Array(len);
var u = new Uint8Array(len);
var dk = new Uint8Array(dkLen);
for (var i = 0; i * len < dkLen; i++) {
var c = i + 1;
ctr[0] = (c >>> 24) & 0xff;
ctr[1] = (c >>> 16) & 0xff;
ctr[2] = (c >>> 8) & 0xff;
ctr[3] = (c >>> 0) & 0xff;
prf.reset();
prf.update(salt);
prf.update(ctr);
prf.finish(u);
for (var j = 0; j < len; j++) {
t[j] = u[j];
}
for (var j = 2; j <= iterations; j++) {
prf.reset();
prf.update(u).finish(u);
for (var k = 0; k < len; k++) {
t[k] ^= u[k];
}
}
for (var j = 0; j < len && i * len + j < dkLen; j++) {
dk[i * len + j] = t[j];
}
}
for (var i = 0; i < len; i++) {
t[i] = u[i] = 0;
}
for (var i = 0; i < 4; i++) {
ctr[i] = 0;
}
prf.clean();
return dk;
}
exports.pbkdf2 = pbkdf2;
});

852
core/third-party/math.js vendored Normal file

File diff suppressed because one or more lines are too long

2391
core/third-party/nacl-fast.js vendored Normal file

File diff suppressed because it is too large Load Diff

2391
core/third-party/nacl-fast_orig.js vendored Normal file

File diff suppressed because it is too large Load Diff

6373
core/third-party/pako.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import GeneralName from "./GeneralName.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class AccessDescription
{
//**********************************************************************************
/**
* Constructor for AccessDescription class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc The type and format of the information are specified by the accessMethod field. This profile defines two accessMethod OIDs: id-ad-caIssuers and id-ad-ocsp
*/
this.accessMethod = getParametersValue(parameters, "accessMethod", AccessDescription.defaultValues("accessMethod"));
/**
* @type {GeneralName}
* @desc The accessLocation field specifies the location of the information
*/
this.accessLocation = getParametersValue(parameters, "accessLocation", AccessDescription.defaultValues("accessLocation"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "accessMethod":
return "";
case "accessLocation":
return new GeneralName();
default:
throw new Error(`Invalid member name for AccessDescription class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* AccessDescription ::= SEQUENCE {
* accessMethod OBJECT IDENTIFIER,
* accessLocation GeneralName }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [accessMethod]
* @property {string} [accessLocation]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.accessMethod || "") }),
GeneralName.schema(names.accessLocation || {})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"accessMethod",
"accessLocation"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
AccessDescription.schema({
names: {
accessMethod: "accessMethod",
accessLocation: {
names: {
blockName: "accessLocation"
}
}
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for AccessDescription");
//endregion
//region Get internal properties from parsed schema
this.accessMethod = asn1.result.accessMethod.valueBlock.toString();
this.accessLocation = new GeneralName({ schema: asn1.result.accessLocation });
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.accessMethod }),
this.accessLocation.toSchema()
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
accessMethod: this.accessMethod,
accessLocation: this.accessLocation.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

249
core/third-party/pkijs/Accuracy.js vendored Normal file
View File

@@ -0,0 +1,249 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC3161. Accuracy represents the time deviation around the UTC time contained in GeneralizedTime.
*/
export default class Accuracy
{
//**********************************************************************************
/**
* Constructor for Accuracy class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
if("seconds" in parameters)
/**
* @type {number}
* @desc seconds
*/
this.seconds = getParametersValue(parameters, "seconds", Accuracy.defaultValues("seconds"));
if("millis" in parameters)
/**
* @type {number}
* @desc millis
*/
this.millis = getParametersValue(parameters, "millis", Accuracy.defaultValues("millis"));
if("micros" in parameters)
/**
* @type {number}
* @desc micros
*/
this.micros = getParametersValue(parameters, "micros", Accuracy.defaultValues("micros"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "seconds":
case "millis":
case "micros":
return 0;
default:
throw new Error(`Invalid member name for Accuracy class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "seconds":
case "millis":
case "micros":
return (memberValue === Accuracy.defaultValues(memberName));
default:
throw new Error(`Invalid member name for Accuracy class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* Accuracy ::= SEQUENCE {
* seconds INTEGER OPTIONAL,
* millis [0] INTEGER (1..999) OPTIONAL,
* micros [1] INTEGER (1..999) OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [seconds]
* @property {string} [millis]
* @property {string} [micros]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
optional: true,
value: [
new asn1js.Integer({
optional: true,
name: (names.seconds || "")
}),
new asn1js.Primitive({
name: (names.millis || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
}
}),
new asn1js.Primitive({
name: (names.micros || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
}
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"seconds",
"millis",
"micros"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
Accuracy.schema({
names: {
seconds: "seconds",
millis: "millis",
micros: "micros"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for Accuracy");
//endregion
//region Get internal properties from parsed schema
if("seconds" in asn1.result)
this.seconds = asn1.result.seconds.valueBlock.valueDec;
if("millis" in asn1.result)
{
const intMillis = new asn1js.Integer({ valueHex: asn1.result.millis.valueBlock.valueHex });
this.millis = intMillis.valueBlock.valueDec;
}
if("micros" in asn1.result)
{
const intMicros = new asn1js.Integer({ valueHex: asn1.result.micros.valueBlock.valueHex });
this.micros = intMicros.valueBlock.valueDec;
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array of output sequence
const outputArray = [];
if("seconds" in this)
outputArray.push(new asn1js.Integer({ value: this.seconds }));
if("millis" in this)
{
const intMillis = new asn1js.Integer({ value: this.millis });
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
valueHex: intMillis.valueBlock.valueHex
}));
}
if("micros" in this)
{
const intMicros = new asn1js.Integer({ value: this.micros });
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
valueHex: intMicros.valueBlock.valueHex
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {};
if("seconds" in this)
_object.seconds = this.seconds;
if("millis" in this)
_object.millis = this.millis;
if("micros" in this)
_object.micros = this.micros;
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,212 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class AlgorithmIdentifier
{
//**********************************************************************************
/**
* Constructor for AlgorithmIdentifier class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
* @property {string} [algorithmId] ObjectIdentifier for algorithm (string representation)
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc ObjectIdentifier for algorithm (string representation)
*/
this.algorithmId = getParametersValue(parameters, "algorithmId", AlgorithmIdentifier.defaultValues("algorithmId"));
if("algorithmParams" in parameters)
/**
* @type {Object}
* @desc Any algorithm parameters
*/
this.algorithmParams = getParametersValue(parameters, "algorithmParams", AlgorithmIdentifier.defaultValues("algorithmParams"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "algorithmId":
return "";
case "algorithmParams":
return new asn1js.Any();
default:
throw new Error(`Invalid member name for AlgorithmIdentifier class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "algorithmId":
return (memberValue === "");
case "algorithmParams":
return (memberValue instanceof asn1js.Any);
default:
throw new Error(`Invalid member name for AlgorithmIdentifier class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* AlgorithmIdentifier ::= Sequence {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} algorithmIdentifier ObjectIdentifier for the algorithm
* @property {string} algorithmParams Any algorithm parameters
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
optional: (names.optional || false),
value: [
new asn1js.ObjectIdentifier({ name: (names.algorithmIdentifier || "") }),
new asn1js.Any({ name: (names.algorithmParams || ""), optional: true })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"algorithm",
"params"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
AlgorithmIdentifier.schema({
names: {
algorithmIdentifier: "algorithm",
algorithmParams: "params"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for AlgorithmIdentifier");
//endregion
//region Get internal properties from parsed schema
this.algorithmId = asn1.result.algorithm.valueBlock.toString();
if("params" in asn1.result)
this.algorithmParams = asn1.result.params;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(new asn1js.ObjectIdentifier({ value: this.algorithmId }));
if(("algorithmParams" in this) && ((this.algorithmParams instanceof asn1js.Any) === false))
outputArray.push(this.algorithmParams);
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
algorithmId: this.algorithmId
};
if(("algorithmParams" in this) && ((this.algorithmParams instanceof asn1js.Any) === false))
object.algorithmParams = this.algorithmParams.toJSON();
return object;
}
//**********************************************************************************
/**
* Check that two "AlgorithmIdentifiers" are equal
* @param {AlgorithmIdentifier} algorithmIdentifier
* @returns {boolean}
*/
isEqual(algorithmIdentifier)
{
//region Check input type
if((algorithmIdentifier instanceof AlgorithmIdentifier) === false)
return false;
//endregion
//region Check "algorithm_id"
if(this.algorithmId !== algorithmIdentifier.algorithmId)
return false;
//endregion
//region Check "algorithm_params"
if("algorithmParams" in this)
{
if("algorithmParams" in algorithmIdentifier)
return JSON.stringify(this.algorithmParams) === JSON.stringify(algorithmIdentifier.algorithmParams);
return false;
}
if("algorithmParams" in algorithmIdentifier)
return false;
//endregion
return true;
}
//**********************************************************************************
}
//**************************************************************************************

135
core/third-party/pkijs/AltName.js vendored Normal file
View File

@@ -0,0 +1,135 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import GeneralName from "./GeneralName.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class AltName
{
//**********************************************************************************
/**
* Constructor for AltName class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Array.<GeneralName>}
* @desc Array of alternative names in GeneralName type
*/
this.altNames = getParametersValue(parameters, "altNames", AltName.defaultValues("altNames"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "altNames":
return [];
default:
throw new Error(`Invalid member name for AltName class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* AltName ::= GeneralNames
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [altNames]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Repeated({
name: (names.altNames || ""),
value: GeneralName.schema()
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"altNames"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
AltName.schema({
names: {
altNames: "altNames"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for AltName");
//endregion
//region Get internal properties from parsed schema
if("altNames" in asn1.result)
this.altNames = Array.from(asn1.result.altNames, element => new GeneralName({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: Array.from(this.altNames, element => element.toSchema())
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
altNames: Array.from(this.altNames, element => element.toJSON())
};
}
//**********************************************************************************
}
//**************************************************************************************

177
core/third-party/pkijs/Attribute.js vendored Normal file
View File

@@ -0,0 +1,177 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC2986
*/
export default class Attribute {
//**********************************************************************************
/**
* Constructor for Attribute class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc ObjectIdentifier for attribute (string representation)
*/
this.type = getParametersValue(parameters, "type", Attribute.defaultValues("type"));
/**
* @type {Array}
* @desc Any attribute values
*/
this.values = getParametersValue(parameters, "values", Attribute.defaultValues("values"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "type":
return "";
case "values":
return [];
default:
throw new Error(`Invalid member name for Attribute class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "type":
return (memberValue === "");
case "values":
return (memberValue.length === 0);
default:
throw new Error(`Invalid member name for Attribute class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
* type ATTRIBUTE.&id({IOSet}),
* values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [type]
* @property {string} [setName]
* @property {string} [values]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.type || "") }),
new asn1js.Set({
name: (names.setName || ""),
value: [
new asn1js.Repeated({
name: (names.values || ""),
value: new asn1js.Any()
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"type",
"values"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
Attribute.schema({
names: {
type: "type",
values: "values"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for Attribute");
//endregion
//region Get internal properties from parsed schema
this.type = asn1.result.type.valueBlock.toString();
this.values = asn1.result.values;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.type }),
new asn1js.Set({
value: this.values
})
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
type: this.type,
values: Array.from(this.values, element => element.toJSON())
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,871 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import GeneralNames from "./GeneralNames.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import Attribute from "./Attribute.js";
import Extensions from "./Extensions.js";
//**************************************************************************************
/**
* Class from RFC5755
*/
export class AttCertValidityPeriod
{
//**********************************************************************************
/**
* Constructor for AttCertValidityPeriod class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {GeneralizedTime}
* @desc notBeforeTime
*/
this.notBeforeTime = getParametersValue(parameters, "notBeforeTime", AttCertValidityPeriod.defaultValues("notBeforeTime"));
/**
* @type {GeneralizedTime}
* @desc notAfterTime
*/
this.notAfterTime = getParametersValue(parameters, "notAfterTime", AttCertValidityPeriod.defaultValues("notAfterTime"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "notBeforeTime":
case "notAfterTime":
return new Date(0, 0, 0);
default:
throw new Error(`Invalid member name for AttCertValidityPeriod class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* AttCertValidityPeriod ::= SEQUENCE {
* notBeforeTime GeneralizedTime,
* notAfterTime GeneralizedTime
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [notBeforeTime]
* @property {string} [notAfterTime]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.GeneralizedTime({ name: (names.notBeforeTime || "") }),
new asn1js.GeneralizedTime({ name: (names.notAfterTime || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"notBeforeTime",
"notAfterTime"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
AttCertValidityPeriod.schema({
names: {
notBeforeTime: "notBeforeTime",
notAfterTime: "notAfterTime"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for AttCertValidityPeriod");
//endregion
//region Get internal properties from parsed schema
this.notBeforeTime = asn1.result.notBeforeTime.toDate();
this.notAfterTime = asn1.result.notAfterTime.toDate();
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.GeneralizedTime({ valueDate: this.notBeforeTime }),
new asn1js.GeneralizedTime({ valueDate: this.notAfterTime }),
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
notBeforeTime: this.notBeforeTime,
notAfterTime: this.notAfterTime
};
}
//**********************************************************************************
}
//**************************************************************************************
/**
* Class from RFC5755
*/
export class IssuerSerial
{
//**********************************************************************************
/**
* Constructor for IssuerSerial class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {RelativeDistinguishedNames}
* @desc issuer
*/
this.issuer = getParametersValue(parameters, "issuer", IssuerSerial.defaultValues("issuer"));
/**
* @type {Integer}
* @desc serialNumber
*/
this.serialNumber = getParametersValue(parameters, "serialNumber", IssuerSerial.defaultValues("serialNumber"));
if("issuerUID" in parameters)
/**
* @type {BitString}
* @desc issuerUID
*/
this.issuerUID = getParametersValue(parameters, "issuerUID", IssuerSerial.defaultValues("issuerUID"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "issuer":
return new GeneralNames();
case "serialNumber":
return new asn1js.Integer();
case "issuerUID":
return new asn1js.BitString();
default:
throw new Error(`Invalid member name for IssuerSerial class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* IssuerSerial ::= SEQUENCE {
* issuer GeneralNames,
* serial CertificateSerialNumber,
* issuerUID UniqueIdentifier OPTIONAL
* }
*
* CertificateSerialNumber ::= INTEGER
* UniqueIdentifier ::= BIT STRING
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [issuer]
* @property {string} [serialNumber]
* @property {string} [issuerUID]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
GeneralNames.schema(names.issuer || {}),
new asn1js.Integer({ name: (names.serialNumber || "") }),
new asn1js.BitString({
optional: true,
name: (names.issuerUID || "")
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"issuer",
"serialNumber",
"issuerUID"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
IssuerSerial.schema({
names: {
issuer: {
names: {
blockName: "issuer"
}
},
serialNumber: "serialNumber",
issuerUID: "issuerUID"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for IssuerSerial");
//endregion
//region Get internal properties from parsed schema
this.issuer = new GeneralNames({ schema: asn1.result.issuer });
this.serialNumber = asn1.result.serialNumber;
if("issuerUID" in asn1.result)
this.issuerUID = asn1.result.issuerUID;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
const result = new asn1js.Sequence({
value: [
this.issuer.toSchema(),
this.serialNumber
]
});
if("issuerUID" in this)
result.valueBlock.value.push(this.issuerUID);
//region Construct and return new ASN.1 schema for this object
return result;
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const result = {
issuer: this.issuer.toJSON(),
serialNumber: this.serialNumber.toJSON()
};
if("issuerUID" in this)
result.issuerUID = this.issuerUID.toJSON();
return result;
}
//**********************************************************************************
}
//**************************************************************************************
/**
* Class from RFC5755
*/
export class AttributeCertificateInfoV1
{
//**********************************************************************************
/**
* Constructor for AttributeCertificateInfoV1 class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", AttributeCertificateInfoV1.defaultValues("version"));
if("baseCertificateID" in parameters)
/**
* @type {IssuerSerial}
* @desc baseCertificateID
*/
this.baseCertificateID = getParametersValue(parameters, "baseCertificateID", AttributeCertificateInfoV1.defaultValues("baseCertificateID"));
if("subjectName" in parameters)
/**
* @type {GeneralNames}
* @desc subjectName
*/
this.subjectName = getParametersValue(parameters, "subjectName", AttributeCertificateInfoV1.defaultValues("subjectName"));
/**
* @type {GeneralNames}
* @desc issuer
*/
this.issuer = getParametersValue(parameters, "issuer", AttributeCertificateInfoV1.defaultValues("issuer"));
/**
* @type {AlgorithmIdentifier}
* @desc signature
*/
this.signature = getParametersValue(parameters, "signature", AttributeCertificateInfoV1.defaultValues("signature"));
/**
* @type {Integer}
* @desc serialNumber
*/
this.serialNumber = getParametersValue(parameters, "serialNumber", AttributeCertificateInfoV1.defaultValues("serialNumber"));
/**
* @type {AttCertValidityPeriod}
* @desc attrCertValidityPeriod
*/
this.attrCertValidityPeriod = getParametersValue(parameters, "attrCertValidityPeriod", AttributeCertificateInfoV1.defaultValues("attrCertValidityPeriod"));
/**
* @type {Array.<Attribute>}
* @desc attributes
*/
this.attributes = getParametersValue(parameters, "attributes", AttributeCertificateInfoV1.defaultValues("attributes"));
if("issuerUniqueID" in parameters)
/**
* @type {BitString}
* @desc issuerUniqueID
*/
this.issuerUniqueID = getParametersValue(parameters, "issuerUniqueID", AttributeCertificateInfoV1.defaultValues("issuerUniqueID"));
if("extensions" in parameters)
/**
* @type {Extensions}
* @desc extensions
*/
this.extensions = getParametersValue(parameters, "extensions", AttributeCertificateInfoV1.defaultValues("extensions"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "version":
return 0;
case "baseCertificateID":
return new IssuerSerial();
case "subjectName":
return new GeneralNames();
case "issuer":
return {};
case "signature":
return new AlgorithmIdentifier();
case "serialNumber":
return new asn1js.Integer();
case "attrCertValidityPeriod":
return new AttCertValidityPeriod();
case "attributes":
return [];
case "issuerUniqueID":
return new asn1js.BitString();
case "extensions":
return new Extensions();
default:
throw new Error(`Invalid member name for AttributeCertificateInfoV1 class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* AttributeCertificateInfo ::= SEQUENCE {
* version Version DEFAULT v1,
* subject CHOICE {
* baseCertificateID [0] IssuerSerial, -- associated with a Public Key Certificate
* subjectName [1] GeneralNames }, -- associated with a name
* issuer GeneralNames, -- CA issuing the attribute certificate
* signature AlgorithmIdentifier,
* serialNumber CertificateSerialNumber,
* attrCertValidityPeriod AttCertValidityPeriod,
* attributes SEQUENCE OF Attribute,
* issuerUniqueID UniqueIdentifier OPTIONAL,
* extensions Extensions OPTIONAL
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [issuer]
* @property {string} [serialNumber]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.version || "") }),
new asn1js.Choice({
value: [
new asn1js.Constructed({
name: (names.baseCertificateID || ""),
idBlock: {
tagClass: 3,
tagNumber: 0 // [0]
},
value: IssuerSerial.schema().valueBlock.value
}),
new asn1js.Constructed({
name: (names.subjectName || ""),
idBlock: {
tagClass: 3,
tagNumber: 1 // [2]
},
value: GeneralNames.schema().valueBlock.value
}),
]
}),
GeneralNames.schema({
names: {
blockName: (names.issuer || "")
}
}),
AlgorithmIdentifier.schema(names.signature || {}),
new asn1js.Integer({ name: (names.serialNumber || "") }),
AttCertValidityPeriod.schema(names.attrCertValidityPeriod || {}),
new asn1js.Sequence({
name: (names.attributes || ""),
value: [
new asn1js.Repeated({
value: Attribute.schema()
})
]
}),
new asn1js.BitString({
optional: true,
name: (names.issuerUniqueID || "")
}),
Extensions.schema(names.extensions || {}, true)
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"version",
"baseCertificateID",
"subjectName",
"issuer",
"signature",
"serialNumber",
"attrCertValidityPeriod",
"attributes",
"issuerUniqueID",
"extensions"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
AttributeCertificateInfoV1.schema({
names: {
version: "version",
baseCertificateID: "baseCertificateID",
subjectName: "subjectName",
issuer: "issuer",
signature: {
names: {
blockName: "signature"
}
},
serialNumber: "serialNumber",
attrCertValidityPeriod: {
names: {
blockName: "attrCertValidityPeriod"
}
},
attributes: "attributes",
issuerUniqueID: "issuerUniqueID",
extensions: {
names: {
blockName: "extensions"
}
}
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for AttributeCertificateInfoV1");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result.version.valueBlock.valueDec;
if("baseCertificateID" in asn1.result)
{
this.baseCertificateID = new IssuerSerial({
schema: new asn1js.Sequence({
value: asn1.result.baseCertificateID.valueBlock.value
})
});
}
if("subjectName" in asn1.result)
{
this.subjectName = new GeneralNames({
schema: new asn1js.Sequence({
value: asn1.result.subjectName.valueBlock.value
})
});
}
this.issuer = asn1.result.issuer;
this.signature = new AlgorithmIdentifier({ schema: asn1.result.signature });
this.serialNumber = asn1.result.serialNumber;
this.attrCertValidityPeriod = new AttCertValidityPeriod({ schema: asn1.result.attrCertValidityPeriod });
this.attributes = Array.from(asn1.result.attributes.valueBlock.value, element => new Attribute({ schema: element }));
if("issuerUniqueID" in asn1.result)
this.issuerUniqueID = asn1.result.issuerUniqueID;
if("extensions" in asn1.result)
this.extensions = new Extensions({ schema: asn1.result.extensions });
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
const result = new asn1js.Sequence({
value: [new asn1js.Integer({ value: this.version })]
});
if("baseCertificateID" in this)
{
result.valueBlock.value.push(new asn1js.Constructed({
idBlock: {
tagClass: 3,
tagNumber: 0 // [0]
},
value: this.baseCertificateID.toSchema().valueBlock.value
}));
}
if("subjectName" in this)
{
result.valueBlock.value.push(new asn1js.Constructed({
idBlock: {
tagClass: 3,
tagNumber: 1 // [1]
},
value: this.subjectName.toSchema().valueBlock.value
}));
}
result.valueBlock.value.push(this.issuer.toSchema());
result.valueBlock.value.push(this.signature.toSchema());
result.valueBlock.value.push(this.serialNumber);
result.valueBlock.value.push(this.attrCertValidityPeriod.toSchema());
result.valueBlock.value.push(new asn1js.Sequence({
value: Array.from(this.attributes, element => element.toSchema())
}));
if("issuerUniqueID" in this)
result.valueBlock.value.push(this.issuerUniqueID);
if("extensions" in this)
result.valueBlock.value.push(this.extensions.toSchema());
return result;
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const result = {
version: this.version
};
if("baseCertificateID" in this)
result.baseCertificateID = this.baseCertificateID.toJSON();
if("subjectName" in this)
result.subjectName = this.subjectName.toJSON();
result.issuer = this.issuer.toJSON();
result.signature = this.signature.toJSON();
result.serialNumber = this.serialNumber.toJSON();
result.attrCertValidityPeriod = this.attrCertValidityPeriod.toJSON();
result.attributes = Array.from(this.attributes, element => element.toJSON());
if("issuerUniqueID" in this)
result.issuerUniqueID = this.issuerUniqueID.toJSON();
if("extensions" in this)
result.extensions = this.extensions.toJSON();
return result;
}
//**********************************************************************************
}
//**************************************************************************************
/**
* Class from X.509:1997
*/
export default class AttributeCertificateV1
{
//**********************************************************************************
/**
* Constructor for AttributeCertificateV1 class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {AttributeCertificateInfoV1}
* @desc acinfo
*/
this.acinfo = getParametersValue(parameters, "acinfo", AttributeCertificateV1.defaultValues("acinfo"));
/**
* @type {AlgorithmIdentifier}
* @desc signatureAlgorithm
*/
this.signatureAlgorithm = getParametersValue(parameters, "signatureAlgorithm", AttributeCertificateV1.defaultValues("signatureAlgorithm"));
/**
* @type {BitString}
* @desc signatureValue
*/
this.signatureValue = getParametersValue(parameters, "signatureValue", AttributeCertificateV1.defaultValues("signatureValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "acinfo":
return new AttributeCertificateInfoV1();
case "signatureAlgorithm":
return new AlgorithmIdentifier();
case "signatureValue":
return new asn1js.BitString();
default:
throw new Error(`Invalid member name for AttributeCertificateV1 class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* AttributeCertificate ::= SEQUENCE {
* acinfo AttributeCertificateInfoV1,
* signatureAlgorithm AlgorithmIdentifier,
* signatureValue BIT STRING
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {Object} [acinfo]
* @property {Object} [signatureAlgorithm]
* @property {string} [signatureValue]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
AttributeCertificateInfoV1.schema(names.acinfo || {}),
AlgorithmIdentifier.schema(names.signatureAlgorithm || {}),
new asn1js.BitString({ name: (names.signatureValue || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"acinfo",
"signatureValue",
"signatureAlgorithm"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
AttributeCertificateV1.schema({
names: {
acinfo: {
names: {
blockName: "acinfo"
}
},
signatureAlgorithm: {
names: {
blockName: "signatureAlgorithm"
}
},
signatureValue: "signatureValue"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for AttributeCertificateV1");
//endregion
//region Get internal properties from parsed schema
this.acinfo = new AttributeCertificateInfoV1({ schema: asn1.result.acinfo });
this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.signatureAlgorithm });
this.signatureValue = asn1.result.signatureValue;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
return (new asn1js.Sequence({
value: [
this.acinfo.toSchema(),
this.signatureAlgorithm.toSchema(),
this.signatureValue
]
}));
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
acinfo: this.acinfo.toJSON(),
signatureAlgorithm: this.signatureAlgorithm.toJSON(),
signatureValue: this.signatureValue.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, isEqualBuffer, clearProps } from "./pvutils.js";
import { stringPrep } from "./common.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class AttributeTypeAndValue
{
//**********************************************************************************
/**
* Constructor for AttributeTypeAndValue class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc type
*/
this.type = getParametersValue(parameters, "type", AttributeTypeAndValue.defaultValues("type"));
/**
* @type {Object}
* @desc Value of the AttributeTypeAndValue class
*/
this.value = getParametersValue(parameters, "value", AttributeTypeAndValue.defaultValues("value"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "type":
return "";
case "value":
return {};
default:
throw new Error(`Invalid member name for AttributeTypeAndValue class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* AttributeTypeAndValue ::= Sequence {
* type AttributeType,
* value AttributeValue }
*
* AttributeType ::= OBJECT IDENTIFIER
*
* AttributeValue ::= ANY -- DEFINED BY AttributeType
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName] Name for entire block
* @property {string} [type] Name for "type" element
* @property {string} [value] Name for "value" element
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.type || "") }),
new asn1js.Any({ name: (names.value || "") })
]
}));
}
//**********************************************************************************
static blockName()
{
return "AttributeTypeAndValue";
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"type",
"typeValue"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
AttributeTypeAndValue.schema({
names: {
type: "type",
value: "typeValue"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for AttributeTypeAndValue");
//endregion
//region Get internal properties from parsed schema
this.type = asn1.result.type.valueBlock.toString();
// noinspection JSUnresolvedVariable
this.value = asn1.result.typeValue;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.type }),
this.value
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
type: this.type
};
if(Object.keys(this.value).length !== 0)
_object.value = this.value.toJSON();
else
_object.value = this.value;
return _object;
}
//**********************************************************************************
/**
* Compare two AttributeTypeAndValue values, or AttributeTypeAndValue with ArrayBuffer value
* @param {(AttributeTypeAndValue|ArrayBuffer)} compareTo The value compare to current
* @returns {boolean}
*/
isEqual(compareTo)
{
const stringBlockNames = [
asn1js.Utf8String.blockName(),
asn1js.BmpString.blockName(),
asn1js.UniversalString.blockName(),
asn1js.NumericString.blockName(),
asn1js.PrintableString.blockName(),
asn1js.TeletexString.blockName(),
asn1js.VideotexString.blockName(),
asn1js.IA5String.blockName(),
asn1js.GraphicString.blockName(),
asn1js.VisibleString.blockName(),
asn1js.GeneralString.blockName(),
asn1js.CharacterString.blockName()
];
if(compareTo.constructor.blockName() === AttributeTypeAndValue.blockName())
{
if(this.type !== compareTo.type)
return false;
//region Check we do have both strings
let isString = false;
const thisName = this.value.constructor.blockName();
if(thisName === compareTo.value.constructor.blockName())
{
for(const name of stringBlockNames)
{
if(thisName === name)
{
isString = true;
break;
}
}
}
//endregion
if(isString)
{
const value1 = stringPrep(this.value.valueBlock.value);
const value2 = stringPrep(compareTo.value.valueBlock.value);
if(value1.localeCompare(value2) !== 0)
return false;
}
else // Comparing as two ArrayBuffers
{
if(isEqualBuffer(this.value.valueBeforeDecode, compareTo.value.valueBeforeDecode) === false)
return false;
}
return true;
}
if(compareTo instanceof ArrayBuffer)
return isEqualBuffer(this.value.valueBeforeDecode, compareTo);
return false;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,513 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, utilConcatBuf, clearProps } from "./pvutils.js";
import ContentInfo from "./ContentInfo.js";
import SafeContents from "./SafeContents.js";
import EnvelopedData from "./EnvelopedData.js";
import EncryptedData from "./EncryptedData.js";
//**************************************************************************************
/**
* Class from RFC7292
*/
export default class AuthenticatedSafe
{
//**********************************************************************************
/**
* Constructor for AuthenticatedSafe class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Array.<ContentInfo>}
* @desc safeContents
*/
this.safeContents = getParametersValue(parameters, "safeContents", AuthenticatedSafe.defaultValues("safeContents"));
if("parsedValue" in parameters)
/**
* @type {*}
* @desc parsedValue
*/
this.parsedValue = getParametersValue(parameters, "parsedValue", AuthenticatedSafe.defaultValues("parsedValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "safeContents":
return [];
case "parsedValue":
return {};
default:
throw new Error(`Invalid member name for AuthenticatedSafe class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "safeContents":
return (memberValue.length === 0);
case "parsedValue":
return ((memberValue instanceof Object) && (Object.keys(memberValue).length === 0));
default:
throw new Error(`Invalid member name for AuthenticatedSafe class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* AuthenticatedSafe ::= SEQUENCE OF ContentInfo
* -- Data if unencrypted
* -- EncryptedData if password-encrypted
* -- EnvelopedData if public key-encrypted
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [contentInfos]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Repeated({
name: (names.contentInfos || ""),
value: ContentInfo.schema()
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"contentInfos"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
AuthenticatedSafe.schema({
names: {
contentInfos: "contentInfos"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for AuthenticatedSafe");
//endregion
//region Get internal properties from parsed schema
this.safeContents = Array.from(asn1.result.contentInfos, element => new ContentInfo({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: Array.from(this.safeContents, element => element.toSchema())
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
safeContents: Array.from(this.safeContents, element => element.toJSON())
};
}
//**********************************************************************************
parseInternalValues(parameters)
{
//region Check input data from "parameters"
if((parameters instanceof Object) === false)
return Promise.reject("The \"parameters\" must has \"Object\" type");
if(("safeContents" in parameters) === false)
return Promise.reject("Absent mandatory parameter \"safeContents\"");
if((parameters.safeContents instanceof Array) === false)
return Promise.reject("The \"parameters.safeContents\" must has \"Array\" type");
if(parameters.safeContents.length !== this.safeContents.length)
return Promise.reject("Length of \"parameters.safeContents\" must be equal to \"this.safeContents.length\"");
//endregion
//region Initial variables
let sequence = Promise.resolve();
//endregion
//region Create value for "this.parsedValue.authenticatedSafe"
this.parsedValue = {
safeContents: []
};
for(const [index, content] of this.safeContents.entries())
{
switch(content.contentType)
{
//region data
case "1.2.840.113549.1.7.1":
{
//region Check that we do have OCTETSTRING as "content"
if((content.content instanceof asn1js.OctetString) === false)
return Promise.reject("Wrong type of \"this.safeContents[j].content\"");
//endregion
//region Check we have "constructive encoding" for AuthSafe content
let authSafeContent = new ArrayBuffer(0);
if(content.content.valueBlock.isConstructed)
{
for(const contentValue of content.content.valueBlock.value)
authSafeContent = utilConcatBuf(authSafeContent, contentValue.valueBlock.valueHex);
}
else
authSafeContent = content.content.valueBlock.valueHex;
//endregion
//region Parse internal ASN.1 data
const asn1 = asn1js.fromBER(authSafeContent);
if(asn1.offset === (-1))
return Promise.reject("Error during parsing of ASN.1 data inside \"content.content\"");
//endregion
//region Finilly initialize initial values of "SafeContents" type
this.parsedValue.safeContents.push({
privacyMode: 0, // No privacy, clear data
value: new SafeContents({ schema: asn1.result })
});
//endregion
}
break;
//endregion
//region envelopedData
case "1.2.840.113549.1.7.3":
{
//region Initial variables
const cmsEnveloped = new EnvelopedData({ schema: content.content });
//endregion
//region Check mandatory parameters
if(("recipientCertificate" in parameters.safeContents[index]) === false)
return Promise.reject("Absent mandatory parameter \"recipientCertificate\" in \"parameters.safeContents[j]\"");
const recipientCertificate = parameters.safeContents[index].recipientCertificate;
if(("recipientKey" in parameters.safeContents[index]) === false)
return Promise.reject("Absent mandatory parameter \"recipientKey\" in \"parameters.safeContents[j]\"");
// noinspection JSUnresolvedVariable
const recipientKey = parameters.safeContents[index].recipientKey;
//endregion
//region Decrypt CMS EnvelopedData using first recipient information
sequence = sequence.then(
() => cmsEnveloped.decrypt(0, {
recipientCertificate,
recipientPrivateKey: recipientKey
})
);
sequence = sequence.then(
/**
* @param {ArrayBuffer} result
*/
result =>
{
const asn1 = asn1js.fromBER(result);
if(asn1.offset === (-1))
return Promise.reject("Error during parsing of decrypted data");
this.parsedValue.safeContents.push({
privacyMode: 2, // Public-key privacy mode
value: new SafeContents({ schema: asn1.result })
});
return Promise.resolve();
}
);
//endregion
}
break;
//endregion
//region encryptedData
case "1.2.840.113549.1.7.6":
{
//region Initial variables
const cmsEncrypted = new EncryptedData({ schema: content.content });
//endregion
//region Check mandatory parameters
if(("password" in parameters.safeContents[index]) === false)
return Promise.reject("Absent mandatory parameter \"password\" in \"parameters.safeContents[j]\"");
const password = parameters.safeContents[index].password;
//endregion
//region Decrypt CMS EncryptedData using password
sequence = sequence.then(
() => cmsEncrypted.decrypt({
password
}),
error => Promise.reject(error)
);
//endregion
//region Initialize internal data
sequence = sequence.then(
/**
* @param {ArrayBuffer} result
*/
result =>
{
const asn1 = asn1js.fromBER(result);
if(asn1.offset === (-1))
return Promise.reject("Error during parsing of decrypted data");
this.parsedValue.safeContents.push({
privacyMode: 1, // Password-based privacy mode
value: new SafeContents({ schema: asn1.result })
});
return Promise.resolve();
},
error => Promise.reject(error)
);
//endregion
}
break;
//endregion
//region default
default:
throw new Error(`Unknown "contentType" for AuthenticatedSafe: " ${content.contentType}`);
//endregion
}
}
//endregion
return sequence;
}
//**********************************************************************************
makeInternalValues(parameters)
{
//region Check data in "parsedValue"
if(("parsedValue" in this) === false)
return Promise.reject("Please run \"parseValues\" first or add \"parsedValue\" manually");
if((this.parsedValue instanceof Object) === false)
return Promise.reject("The \"this.parsedValue\" must has \"Object\" type");
if((this.parsedValue.safeContents instanceof Array) === false)
return Promise.reject("The \"this.parsedValue.safeContents\" must has \"Array\" type");
//endregion
//region Check input data from "parameters"
if((parameters instanceof Object) === false)
return Promise.reject("The \"parameters\" must has \"Object\" type");
if(("safeContents" in parameters) === false)
return Promise.reject("Absent mandatory parameter \"safeContents\"");
if((parameters.safeContents instanceof Array) === false)
return Promise.reject("The \"parameters.safeContents\" must has \"Array\" type");
if(parameters.safeContents.length !== this.parsedValue.safeContents.length)
return Promise.reject("Length of \"parameters.safeContents\" must be equal to \"this.parsedValue.safeContents\"");
//endregion
//region Initial variables
let sequence = Promise.resolve();
//endregion
//region Create internal values from already parsed values
this.safeContents = [];
for(const [index, content] of this.parsedValue.safeContents.entries())
{
//region Check current "content" value
if(("privacyMode" in content) === false)
return Promise.reject("The \"privacyMode\" is a mandatory parameter for \"content\"");
if(("value" in content) === false)
return Promise.reject("The \"value\" is a mandatory parameter for \"content\"");
if((content.value instanceof SafeContents) === false)
return Promise.reject("The \"content.value\" must has \"SafeContents\" type");
//endregion
switch(content.privacyMode)
{
//region No privacy
case 0:
{
const contentBuffer = content.value.toSchema().toBER(false);
sequence = sequence.then(
() =>
{
this.safeContents.push(new ContentInfo({
contentType: "1.2.840.113549.1.7.1",
content: new asn1js.OctetString({ valueHex: contentBuffer })
}));
});
}
break;
//endregion
//region Privacy with password
case 1:
{
//region Initial variables
const cmsEncrypted = new EncryptedData();
const currentParameters = parameters.safeContents[index];
currentParameters.contentToEncrypt = content.value.toSchema().toBER(false);
//endregion
//region Encrypt CMS EncryptedData using password
sequence = sequence.then(
() => cmsEncrypted.encrypt(currentParameters),
error => Promise.reject(error)
);
//endregion
//region Store result content in CMS_CONTENT_INFO type
sequence = sequence.then(
() =>
{
this.safeContents.push(new ContentInfo({
contentType: "1.2.840.113549.1.7.6",
content: cmsEncrypted.toSchema()
}));
},
error => Promise.reject(error)
);
//endregion
}
break;
//endregion
//region Privacy with public key
case 2:
{
//region Initial variables
const cmsEnveloped = new EnvelopedData();
const contentToEncrypt = content.value.toSchema().toBER(false);
//endregion
//region Check mandatory parameters
if(("encryptingCertificate" in parameters.safeContents[index]) === false)
return Promise.reject("Absent mandatory parameter \"encryptingCertificate\" in \"parameters.safeContents[i]\"");
if(("encryptionAlgorithm" in parameters.safeContents[index]) === false)
return Promise.reject("Absent mandatory parameter \"encryptionAlgorithm\" in \"parameters.safeContents[i]\"");
switch(true)
{
case (parameters.safeContents[index].encryptionAlgorithm.name.toLowerCase() === "aes-cbc"):
case (parameters.safeContents[index].encryptionAlgorithm.name.toLowerCase() === "aes-gcm"):
break;
default:
return Promise.reject(`Incorrect parameter "encryptionAlgorithm" in "parameters.safeContents[i]": ${parameters.safeContents[index].encryptionAlgorithm}`);
}
switch(true)
{
case (parameters.safeContents[index].encryptionAlgorithm.length === 128):
case (parameters.safeContents[index].encryptionAlgorithm.length === 192):
case (parameters.safeContents[index].encryptionAlgorithm.length === 256):
break;
default:
return Promise.reject(`Incorrect parameter "encryptionAlgorithm.length" in "parameters.safeContents[i]": ${parameters.safeContents[index].encryptionAlgorithm.length}`);
}
//endregion
//region Making correct "encryptionAlgorithm" variable
const encryptionAlgorithm = parameters.safeContents[index].encryptionAlgorithm;
//endregion
//region Append recipient for enveloped data
cmsEnveloped.addRecipientByCertificate(parameters.safeContents[index].encryptingCertificate);
//endregion
//region Making encryption
sequence = sequence.then(
() => cmsEnveloped.encrypt(encryptionAlgorithm, contentToEncrypt)
);
sequence = sequence.then(
() =>
{
this.safeContents.push(new ContentInfo({
contentType: "1.2.840.113549.1.7.3",
content: cmsEnveloped.toSchema()
}));
}
);
//endregion
}
break;
//endregion
//region default
default:
return Promise.reject(`Incorrect value for "content.privacyMode": ${content.privacyMode}`);
//endregion
}
}
//endregion
//region Return result of the function
return sequence.then(
() => this,
error => Promise.reject(`Error during parsing: ${error}`)
);
//endregion
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,244 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import GeneralName from "./GeneralName.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class AuthorityKeyIdentifier
{
//**********************************************************************************
/**
* Constructor for AuthorityKeyIdentifier class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
if("keyIdentifier" in parameters)
/**
* @type {OctetString}
* @desc keyIdentifier
*/
this.keyIdentifier = getParametersValue(parameters, "keyIdentifier", AuthorityKeyIdentifier.defaultValues("keyIdentifier"));
if("authorityCertIssuer" in parameters)
/**
* @type {Array.<GeneralName>}
* @desc authorityCertIssuer
*/
this.authorityCertIssuer = getParametersValue(parameters, "authorityCertIssuer", AuthorityKeyIdentifier.defaultValues("authorityCertIssuer"));
if("authorityCertSerialNumber" in parameters)
/**
* @type {Integer}
* @desc authorityCertIssuer
*/
this.authorityCertSerialNumber = getParametersValue(parameters, "authorityCertSerialNumber", AuthorityKeyIdentifier.defaultValues("authorityCertSerialNumber"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "keyIdentifier":
return new asn1js.OctetString();
case "authorityCertIssuer":
return [];
case "authorityCertSerialNumber":
return new asn1js.Integer();
default:
throw new Error(`Invalid member name for AuthorityKeyIdentifier class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* AuthorityKeyIdentifier OID ::= 2.5.29.35
*
* AuthorityKeyIdentifier ::= SEQUENCE {
* keyIdentifier [0] KeyIdentifier OPTIONAL,
* authorityCertIssuer [1] GeneralNames OPTIONAL,
* authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
*
* KeyIdentifier ::= OCTET STRING
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [keyIdentifier]
* @property {string} [authorityCertIssuer]
* @property {string} [authorityCertSerialNumber]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Primitive({
name: (names.keyIdentifier || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
}
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [
new asn1js.Repeated({
name: (names.authorityCertIssuer || ""),
value: GeneralName.schema()
})
]
}),
new asn1js.Primitive({
name: (names.authorityCertSerialNumber || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
}
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"keyIdentifier",
"authorityCertIssuer",
"authorityCertSerialNumber"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
AuthorityKeyIdentifier.schema({
names: {
keyIdentifier: "keyIdentifier",
authorityCertIssuer: "authorityCertIssuer",
authorityCertSerialNumber: "authorityCertSerialNumber"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for AuthorityKeyIdentifier");
//endregion
//region Get internal properties from parsed schema
if("keyIdentifier" in asn1.result)
this.keyIdentifier = new asn1js.OctetString({ valueHex: asn1.result.keyIdentifier.valueBlock.valueHex });
if("authorityCertIssuer" in asn1.result)
this.authorityCertIssuer = Array.from(asn1.result.authorityCertIssuer, element => new GeneralName({ schema: element }));
if("authorityCertSerialNumber" in asn1.result)
this.authorityCertSerialNumber = new asn1js.Integer({ valueHex: asn1.result.authorityCertSerialNumber.valueBlock.valueHex });
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
if("keyIdentifier" in this)
{
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
valueHex: this.keyIdentifier.valueBlock.valueHex
}));
}
if("authorityCertIssuer" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: Array.from(this.authorityCertIssuer, element => element.toSchema())
}));
}
if("authorityCertSerialNumber" in this)
{
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
},
valueHex: this.authorityCertSerialNumber.valueBlock.valueHex
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {};
if("keyIdentifier" in this)
object.keyIdentifier = this.keyIdentifier.toJSON();
if("authorityCertIssuer" in this)
object.authorityCertIssuer = Array.from(this.authorityCertIssuer, element => element.toJSON());
if("authorityCertSerialNumber" in this)
object.authorityCertSerialNumber = this.authorityCertSerialNumber.toJSON();
return object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,186 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class BasicConstraints
{
//**********************************************************************************
/**
* Constructor for BasicConstraints class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
* @property {Object} [cA]
* @property {Object} [pathLenConstraint]
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {boolean}
* @desc cA
*/
this.cA = getParametersValue(parameters, "cA", false);
if("pathLenConstraint" in parameters)
/**
* @type {number|Integer}
* @desc pathLenConstraint
*/
this.pathLenConstraint = getParametersValue(parameters, "pathLenConstraint", 0);
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "cA":
return false;
default:
throw new Error(`Invalid member name for BasicConstraints class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* BasicConstraints ::= SEQUENCE {
* cA BOOLEAN DEFAULT FALSE,
* pathLenConstraint INTEGER (0..MAX) OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [cA]
* @property {string} [pathLenConstraint]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Boolean({
optional: true,
name: (names.cA || "")
}),
new asn1js.Integer({
optional: true,
name: (names.pathLenConstraint || "")
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"cA",
"pathLenConstraint"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
BasicConstraints.schema({
names: {
cA: "cA",
pathLenConstraint: "pathLenConstraint"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for BasicConstraints");
//endregion
//region Get internal properties from parsed schema
if("cA" in asn1.result)
this.cA = asn1.result.cA.valueBlock.value;
if("pathLenConstraint" in asn1.result)
{
if(asn1.result.pathLenConstraint.valueBlock.isHexOnly)
this.pathLenConstraint = asn1.result.pathLenConstraint;
else
this.pathLenConstraint = asn1.result.pathLenConstraint.valueBlock.valueDec;
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
if(this.cA !== BasicConstraints.defaultValues("cA"))
outputArray.push(new asn1js.Boolean({ value: this.cA }));
if("pathLenConstraint" in this)
{
if(this.pathLenConstraint instanceof asn1js.Integer)
outputArray.push(this.pathLenConstraint);
else
outputArray.push(new asn1js.Integer({ value: this.pathLenConstraint }));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {};
if(this.cA !== BasicConstraints.defaultValues("cA"))
object.cA = this.cA;
if("pathLenConstraint" in this)
{
if(this.pathLenConstraint instanceof asn1js.Integer)
object.pathLenConstraint = this.pathLenConstraint.toJSON();
else
object.pathLenConstraint = this.pathLenConstraint;
}
return object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,562 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, isEqualBuffer, clearProps } from "./pvutils.js";
import { getAlgorithmByOID, getCrypto, getEngine } from "./common.js";
import ResponseData from "./ResponseData.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import Certificate from "./Certificate.js";
import CertID from "./CertID.js";
import RelativeDistinguishedNames from "./RelativeDistinguishedNames.js";
import CertificateChainValidationEngine from "./CertificateChainValidationEngine.js";
//**************************************************************************************
/**
* Class from RFC6960
*/
export default class BasicOCSPResponse
{
//**********************************************************************************
/**
* Constructor for BasicOCSPResponse class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {ResponseData}
* @desc tbsResponseData
*/
this.tbsResponseData = getParametersValue(parameters, "tbsResponseData", BasicOCSPResponse.defaultValues("tbsResponseData"));
/**
* @type {AlgorithmIdentifier}
* @desc signatureAlgorithm
*/
this.signatureAlgorithm = getParametersValue(parameters, "signatureAlgorithm", BasicOCSPResponse.defaultValues("signatureAlgorithm"));
/**
* @type {BitString}
* @desc signature
*/
this.signature = getParametersValue(parameters, "signature", BasicOCSPResponse.defaultValues("signature"));
if("certs" in parameters)
/**
* @type {Array.<Certificate>}
* @desc certs
*/
this.certs = getParametersValue(parameters, "certs", BasicOCSPResponse.defaultValues("certs"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "tbsResponseData":
return new ResponseData();
case "signatureAlgorithm":
return new AlgorithmIdentifier();
case "signature":
return new asn1js.BitString();
case "certs":
return [];
default:
throw new Error(`Invalid member name for BasicOCSPResponse class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "type":
{
// noinspection OverlyComplexBooleanExpressionJS
let comparisonResult = ((ResponseData.compareWithDefault("tbs", memberValue.tbs)) &&
(ResponseData.compareWithDefault("responderID", memberValue.responderID)) &&
(ResponseData.compareWithDefault("producedAt", memberValue.producedAt)) &&
(ResponseData.compareWithDefault("responses", memberValue.responses)));
if("responseExtensions" in memberValue)
comparisonResult = comparisonResult && (ResponseData.compareWithDefault("responseExtensions", memberValue.responseExtensions));
return comparisonResult;
}
case "signatureAlgorithm":
return ((memberValue.algorithmId === "") && (("algorithmParams" in memberValue) === false));
case "signature":
return (memberValue.isEqual(BasicOCSPResponse.defaultValues(memberName)));
case "certs":
return (memberValue.length === 0);
default:
throw new Error(`Invalid member name for BasicOCSPResponse class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* BasicOCSPResponse ::= SEQUENCE {
* tbsResponseData ResponseData,
* signatureAlgorithm AlgorithmIdentifier,
* signature BIT STRING,
* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [tbsResponseData]
* @property {string} [signatureAlgorithm]
* @property {string} [signature]
* @property {string} [certs]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || "BasicOCSPResponse"),
value: [
ResponseData.schema(names.tbsResponseData || {
names: {
blockName: "BasicOCSPResponse.tbsResponseData"
}
}),
AlgorithmIdentifier.schema(names.signatureAlgorithm || {
names: {
blockName: "BasicOCSPResponse.signatureAlgorithm"
}
}),
new asn1js.BitString({ name: (names.signature || "BasicOCSPResponse.signature") }),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Sequence({
value: [new asn1js.Repeated({
name: "BasicOCSPResponse.certs",
value: Certificate.schema(names.certs || {})
})]
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"BasicOCSPResponse.tbsResponseData",
"BasicOCSPResponse.signatureAlgorithm",
"BasicOCSPResponse.signature",
"BasicOCSPResponse.certs"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
BasicOCSPResponse.schema()
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for BasicOCSPResponse");
//endregion
//region Get internal properties from parsed schema
this.tbsResponseData = new ResponseData({ schema: asn1.result["BasicOCSPResponse.tbsResponseData"] });
this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result["BasicOCSPResponse.signatureAlgorithm"] });
this.signature = asn1.result["BasicOCSPResponse.signature"];
if("BasicOCSPResponse.certs" in asn1.result)
this.certs = Array.from(asn1.result["BasicOCSPResponse.certs"], element => new Certificate({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(this.tbsResponseData.toSchema());
outputArray.push(this.signatureAlgorithm.toSchema());
outputArray.push(this.signature);
//region Create array of certificates
if("certs" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Sequence({
value: Array.from(this.certs, element => element.toSchema())
})
]
}));
}
//endregion
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
tbsResponseData: this.tbsResponseData.toJSON(),
signatureAlgorithm: this.signatureAlgorithm.toJSON(),
signature: this.signature.toJSON()
};
if("certs" in this)
_object.certs = Array.from(this.certs, element => element.toJSON());
return _object;
}
//**********************************************************************************
/**
* Get OCSP response status for specific certificate
* @param {Certificate} certificate Certificate to be checked
* @param {Certificate} issuerCertificate Certificate of issuer for certificate to be checked
* @returns {Promise}
*/
getCertificateStatus(certificate, issuerCertificate)
{
//region Initial variables
let sequence = Promise.resolve();
const result = {
isForCertificate: false,
status: 2 // 0 = good, 1 = revoked, 2 = unknown
};
const hashesObject = {};
const certIDs = [];
const certIDPromises = [];
//endregion
//region Create all "certIDs" for input certificates
for(const response of this.tbsResponseData.responses)
{
const hashAlgorithm = getAlgorithmByOID(response.certID.hashAlgorithm.algorithmId);
if(("name" in hashAlgorithm) === false)
return Promise.reject(`Wrong CertID hashing algorithm: ${response.certID.hashAlgorithm.algorithmId}`);
if((hashAlgorithm.name in hashesObject) === false)
{
hashesObject[hashAlgorithm.name] = 1;
const certID = new CertID();
certIDs.push(certID);
certIDPromises.push(certID.createForCertificate(certificate, {
hashAlgorithm: hashAlgorithm.name,
issuerCertificate
}));
}
}
sequence = sequence.then(() =>
Promise.all(certIDPromises)
);
//endregion
//region Compare all response's "certIDs" with identifiers for input certificate
sequence = sequence.then(() =>
{
for(const response of this.tbsResponseData.responses)
{
for(const id of certIDs)
{
if(response.certID.isEqual(id))
{
result.isForCertificate = true;
try
{
switch(response.certStatus.idBlock.isConstructed)
{
case true:
if(response.certStatus.idBlock.tagNumber === 1)
result.status = 1; // revoked
break;
case false:
switch(response.certStatus.idBlock.tagNumber)
{
case 0: // good
result.status = 0;
break;
case 2: // unknown
result.status = 2;
break;
default:
}
break;
default:
}
}
catch(ex)
{
}
return result;
}
}
}
return result;
});
//endregion
return sequence;
}
//**********************************************************************************
/**
* Make signature for current OCSP Basic Response
* @param {Object} privateKey Private key for "subjectPublicKeyInfo" structure
* @param {string} [hashAlgorithm="SHA-1"] Hashing algorithm. Default SHA-1
* @returns {Promise}
*/
sign(privateKey, hashAlgorithm = "SHA-1")
{
//region Initial checking
//region Get a private key from function parameter
if(typeof privateKey === "undefined")
return Promise.reject("Need to provide a private key for signing");
//endregion
//endregion
//region Initial variables
let sequence = Promise.resolve();
let parameters;
const engine = getEngine();
//endregion
//region Get a "default parameters" for current algorithm and set correct signature algorithm
sequence = sequence.then(() => engine.subtle.getSignatureParameters(privateKey, hashAlgorithm));
sequence = sequence.then(result =>
{
parameters = result.parameters;
this.signatureAlgorithm = result.signatureAlgorithm;
});
//endregion
//region Create TBS data for signing
sequence = sequence.then(() =>
{
this.tbsResponseData.tbs = this.tbsResponseData.toSchema(true).toBER(false);
});
//endregion
//region Signing TBS data on provided private key
sequence = sequence.then(() => engine.subtle.signWithPrivateKey(this.tbsResponseData.tbs, privateKey, parameters));
sequence = sequence.then(result =>
{
this.signature = new asn1js.BitString({ valueHex: result });
});
//endregion
return sequence;
}
//**********************************************************************************
/**
* Verify existing OCSP Basic Response
* @param {Object} parameters Additional parameters
* @returns {Promise}
*/
verify(parameters = {})
{
//region Initial variables
let signerCert = null;
let certIndex = -1;
let sequence = Promise.resolve();
let trustedCerts = [];
const _this = this;
const engine = getEngine();
//endregion
//region Check amount of certificates
if(("certs" in this) === false)
return Promise.reject("No certificates attached to the BasicOCSPResponce");
//endregion
//region Get input values
if("trustedCerts" in parameters)
trustedCerts = parameters.trustedCerts;
//endregion
//region Aux functions
/**
* Check CA flag for the certificate
* @param {Certificate} cert Certificate to find CA flag for
* @returns {*}
*/
function checkCA(cert)
{
//region Do not include signer's certificate
if((cert.issuer.isEqual(signerCert.issuer) === true) && (cert.serialNumber.isEqual(signerCert.serialNumber) === true))
return null;
//endregion
let isCA = false;
for(const extension of cert.extensions)
{
if(extension.extnID === "2.5.29.19") // BasicConstraints
{
if("cA" in extension.parsedValue)
{
if(extension.parsedValue.cA === true)
isCA = true;
}
}
}
if(isCA)
return cert;
return null;
}
//endregion
//region Get a "crypto" extension
const crypto = getCrypto();
if(typeof crypto === "undefined")
return Promise.reject("Unable to create WebCrypto object");
//endregion
//region Find correct value for "responderID"
switch(true)
{
case (this.tbsResponseData.responderID instanceof RelativeDistinguishedNames): // [1] Name
sequence = sequence.then(() =>
{
for(const [index, certificate] of _this.certs.entries())
{
if(certificate.subject.isEqual(_this.tbsResponseData.responderID))
{
certIndex = index;
break;
}
}
});
break;
case (this.tbsResponseData.responderID instanceof asn1js.OctetString): // [2] KeyHash
sequence = sequence.then(() => Promise.all(Array.from(_this.certs, element =>
crypto.digest({ name: "sha-1" }, new Uint8Array(element.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex)))).then(results =>
{
for(const [index, ] of _this.certs.entries())
{
if(isEqualBuffer(results[index], _this.tbsResponseData.responderID.valueBlock.valueHex))
{
certIndex = index;
break;
}
}
}));
break;
default:
return Promise.reject("Wrong value for responderID");
}
//endregion
//region Make additional verification for signer's certificate
sequence = sequence.then(() =>
{
if(certIndex === (-1))
return Promise.reject("Correct certificate was not found in OCSP response");
signerCert = this.certs[certIndex];
return Promise.all(Array.from(_this.certs, element => checkCA(element))).then(promiseResults =>
{
const additionalCerts = [];
additionalCerts.push(signerCert);
for(const promiseResult of promiseResults)
{
if(promiseResult !== null)
additionalCerts.push(promiseResult);
}
const certChain = new CertificateChainValidationEngine({
certs: additionalCerts,
trustedCerts
});
return certChain.verify().then(verificationResult =>
{
if(verificationResult.result === true)
return Promise.resolve();
return Promise.reject("Validation of signer's certificate failed");
}, error =>
Promise.reject(`Validation of signer's certificate failed with error: ${((error instanceof Object) ? error.resultMessage : error)}`)
);
}, promiseError =>
Promise.reject(`Error during checking certificates for CA flag: ${promiseError}`)
);
});
//endregion
sequence = sequence.then(() => engine.subtle.verifyWithPublicKey(this.tbsResponseData.tbs, this.signature, this.certs[certIndex].subjectPublicKeyInfo, this.signatureAlgorithm));
return sequence;
}
//**********************************************************************************
}
//**************************************************************************************

181
core/third-party/pkijs/CAVersion.js vendored Normal file
View File

@@ -0,0 +1,181 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, utilConcatBuf } from "./pvutils.js";
//**************************************************************************************
/**
* Class from https://docs.microsoft.com/en-us/windows/desktop/seccrypto/certification-authority-renewal
*/
export default class CAVersion
{
//**********************************************************************************
/**
* Constructor for CAVersion class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc certificateIndex
*/
this.certificateIndex = getParametersValue(parameters, "certificateIndex", CAVersion.defaultValues("certificateIndex"));
/**
* @type {number}
* @desc keyIndex
*/
this.keyIndex = getParametersValue(parameters, "keyIndex", CAVersion.defaultValues("keyIndex"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "certificateIndex":
case "keyIndex":
return 0;
default:
throw new Error(`Invalid member name for CAVersion class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* CAVersion ::= INTEGER
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
return (new asn1js.Integer());
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Check the schema is valid
if(schema.constructor.blockName() !== asn1js.Integer.blockName())
throw new Error("Object's schema was not verified against input data for CAVersion");
//endregion
//region Check length of the input value and correct it if needed
let value = schema.valueBlock.valueHex.slice(0);
const valueView = new Uint8Array(value);
switch(true)
{
case (value.byteLength < 4):
{
const tempValue = new ArrayBuffer(4);
const tempValueView = new Uint8Array(tempValue);
tempValueView.set(valueView, 4 - value.byteLength);
value = tempValue.slice(0);
}
break;
case (value.byteLength > 4):
{
const tempValue = new ArrayBuffer(4);
const tempValueView = new Uint8Array(tempValue);
tempValueView.set(valueView.slice(0, 4));
value = tempValue.slice(0);
}
break;
default:
}
//endregion
//region Get internal properties from parsed schema
const keyIndexBuffer = value.slice(0, 2);
const keyIndexView8 = new Uint8Array(keyIndexBuffer);
let temp = keyIndexView8[0];
keyIndexView8[0] = keyIndexView8[1];
keyIndexView8[1] = temp;
const keyIndexView16 = new Uint16Array(keyIndexBuffer);
this.keyIndex = keyIndexView16[0];
const certificateIndexBuffer = value.slice(2);
const certificateIndexView8 = new Uint8Array(certificateIndexBuffer);
temp = certificateIndexView8[0];
certificateIndexView8[0] = certificateIndexView8[1];
certificateIndexView8[1] = temp;
const certificateIndexView16 = new Uint16Array(certificateIndexBuffer);
this.certificateIndex = certificateIndexView16[0];
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create raw values
const certificateIndexBuffer = new ArrayBuffer(2);
const certificateIndexView = new Uint16Array(certificateIndexBuffer);
certificateIndexView[0] = this.certificateIndex;
const certificateIndexView8 = new Uint8Array(certificateIndexBuffer);
let temp = certificateIndexView8[0];
certificateIndexView8[0] = certificateIndexView8[1];
certificateIndexView8[1] = temp;
const keyIndexBuffer = new ArrayBuffer(2);
const keyIndexView = new Uint16Array(keyIndexBuffer);
keyIndexView[0] = this.keyIndex;
const keyIndexView8 = new Uint8Array(keyIndexBuffer);
temp = keyIndexView8[0];
keyIndexView8[0] = keyIndexView8[1];
keyIndexView8[1] = temp;
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Integer({
valueHex: utilConcatBuf(keyIndexBuffer, certificateIndexBuffer)
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
certificateIndex: this.certificateIndex,
keyIndex: this.keyIndex
};
}
//**********************************************************************************
}
//**************************************************************************************

209
core/third-party/pkijs/CRLBag.js vendored Normal file
View File

@@ -0,0 +1,209 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import CertificateRevocationList from "./CertificateRevocationList.js";
//**************************************************************************************
/**
* Class from RFC7292
*/
export default class CRLBag
{
//**********************************************************************************
/**
* Constructor for CRLBag class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc crlId
*/
this.crlId = getParametersValue(parameters, "crlId", CRLBag.defaultValues("crlId"));
/**
* @type {*}
* @desc crlValue
*/
this.crlValue = getParametersValue(parameters, "crlValue", CRLBag.defaultValues("crlValue"));
if("parsedValue" in parameters)
/**
* @type {*}
* @desc parsedValue
*/
this.parsedValue = getParametersValue(parameters, "parsedValue", CRLBag.defaultValues("parsedValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "crlId":
return "";
case "crlValue":
return (new asn1js.Any());
case "parsedValue":
return {};
default:
throw new Error(`Invalid member name for CRLBag class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "crlId":
return (memberValue === "");
case "crlValue":
return (memberValue instanceof asn1js.Any);
case "parsedValue":
return ((memberValue instanceof Object) && (Object.keys(memberValue).length === 0));
default:
throw new Error(`Invalid member name for CRLBag class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* CRLBag ::= SEQUENCE {
* crlId BAG-TYPE.&id ({CRLTypes}),
* crlValue [0] EXPLICIT BAG-TYPE.&Type ({CRLTypes}{@crlId})
*}
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [id]
* @property {string} [value]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.id || "id") }),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [new asn1js.Any({ name: (names.value || "value") })] // EXPLICIT ANY value
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"crlId",
"crlValue"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
CRLBag.schema({
names: {
id: "crlId",
value: "crlValue"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for CRLBag");
//endregion
//region Get internal properties from parsed schema
this.crlId = asn1.result.crlId.valueBlock.toString();
this.crlValue = asn1.result.crlValue;
switch(this.crlId)
{
case "1.2.840.113549.1.9.23.1": // x509CRL
{
const asn1Inner = asn1js.fromBER(this.certValue.valueBlock.valueHex);
this.parsedValue = new CertificateRevocationList({ schema: asn1Inner.result });
}
break;
default:
throw new Error(`Incorrect "crlId" value in CRLBag: ${this.crlId}`);
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
if("parsedValue" in this)
{
this.certId = "1.2.840.113549.1.9.23.1";
this.certValue = new asn1js.OctetString({ valueHex: this.parsedValue.toSchema().toBER(false) });
}
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.crlId }),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [this.crlValue.toSchema()]
})
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
crlId: this.crlId,
crlValue: this.crlValue.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,134 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import DistributionPoint from "./DistributionPoint.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class CRLDistributionPoints
{
//**********************************************************************************
/**
* Constructor for CRLDistributionPoints class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Array.<DistributionPoint>}
* @desc distributionPoints
*/
this.distributionPoints = getParametersValue(parameters, "distributionPoints", CRLDistributionPoints.defaultValues("distributionPoints"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "distributionPoints":
return [];
default:
throw new Error(`Invalid member name for CRLDistributionPoints class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [distributionPoints]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Repeated({
name: (names.distributionPoints || ""),
value: DistributionPoint.schema()
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"distributionPoints"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
CRLDistributionPoints.schema({
names: {
distributionPoints: "distributionPoints"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for CRLDistributionPoints");
//endregion
//region Get internal properties from parsed schema
this.distributionPoints = Array.from(asn1.result.distributionPoints, element => new DistributionPoint({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: Array.from(this.distributionPoints, element => element.toSchema())
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
distributionPoints: Array.from(this.distributionPoints, element => element.toJSON())
};
}
//**********************************************************************************
}
//**************************************************************************************

229
core/third-party/pkijs/CertBag.js vendored Normal file
View File

@@ -0,0 +1,229 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import Certificate from "./Certificate.js";
import AttributeCertificateV2 from "./AttributeCertificateV2.js";
//**************************************************************************************
/**
* Class from RFC7292
*/
export default class CertBag
{
//**********************************************************************************
/**
* Constructor for CertBag class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc certId
*/
this.certId = getParametersValue(parameters, "certId", CertBag.defaultValues("certId"));
/**
* @type {*}
* @desc certValue
*/
this.certValue = getParametersValue(parameters, "certValue", CertBag.defaultValues("certValue"));
if("parsedValue" in parameters)
/**
* @type {*}
* @desc parsedValue
*/
this.parsedValue = getParametersValue(parameters, "parsedValue", CertBag.defaultValues("parsedValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "certId":
return "";
case "certValue":
return (new asn1js.Any());
case "parsedValue":
return {};
default:
throw new Error(`Invalid member name for CertBag class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "certId":
return (memberValue === "");
case "certValue":
return (memberValue instanceof asn1js.Any);
case "parsedValue":
return ((memberValue instanceof Object) && (Object.keys(memberValue).length === 0));
default:
throw new Error(`Invalid member name for CertBag class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* CertBag ::= SEQUENCE {
* certId BAG-TYPE.&id ({CertTypes}),
* certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId})
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [id]
* @property {string} [value]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.id || "id") }),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [new asn1js.Any({ name: (names.value || "value") })] // EXPLICIT ANY value
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"certId",
"certValue"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
CertBag.schema({
names: {
id: "certId",
value: "certValue"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for CertBag");
//endregion
//region Get internal properties from parsed schema
this.certId = asn1.result.certId.valueBlock.toString();
this.certValue = asn1.result.certValue;
switch(this.certId)
{
case "1.2.840.113549.1.9.22.1": // x509Certificate
{
const asn1Inner = asn1js.fromBER(this.certValue.valueBlock.valueHex);
try
{
this.parsedValue = new Certificate({ schema: asn1Inner.result });
}
catch(ex) // In some realizations the same OID used for attribute certificates
{
this.parsedValue = new AttributeCertificateV2({ schema: asn1Inner.result });
}
}
break;
case "1.2.840.113549.1.9.22.3": // attributeCertificate - (!!!) THIS OID IS SUBJECT FOR CHANGE IN FUTURE (!!!)
{
const asn1Inner = asn1js.fromBER(this.certValue.valueBlock.valueHex);
this.parsedValue = new AttributeCertificateV2({ schema: asn1Inner.result });
}
break;
case "1.2.840.113549.1.9.22.2": // sdsiCertificate
default:
throw new Error(`Incorrect "certId" value in CertBag: ${this.certId}`);
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
if("parsedValue" in this)
{
if("acinfo" in this.parsedValue) // attributeCertificate
this.certId = "1.2.840.113549.1.9.22.3";
else // x509Certificate
this.certId = "1.2.840.113549.1.9.22.1";
this.certValue = new asn1js.OctetString({ valueHex: this.parsedValue.toSchema().toBER(false) });
}
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.certId }),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [(("toSchema" in this.certValue) ? this.certValue.toSchema() : this.certValue)]
})
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
certId: this.certId,
certValue: this.certValue.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

306
core/third-party/pkijs/CertID.js vendored Normal file
View File

@@ -0,0 +1,306 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, isEqualBuffer, clearProps } from "./pvutils.js";
import { getCrypto, getOIDByAlgorithm } from "./common.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
//**************************************************************************************
/**
* Class from RFC6960
*/
export default class CertID
{
//**********************************************************************************
/**
* Constructor for CertID class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {AlgorithmIdentifier}
* @desc hashAlgorithm
*/
this.hashAlgorithm = getParametersValue(parameters, "hashAlgorithm", CertID.defaultValues("hashAlgorithm"));
/**
* @type {OctetString}
* @desc issuerNameHash
*/
this.issuerNameHash = getParametersValue(parameters, "issuerNameHash", CertID.defaultValues("issuerNameHash"));
/**
* @type {OctetString}
* @desc issuerKeyHash
*/
this.issuerKeyHash = getParametersValue(parameters, "issuerKeyHash", CertID.defaultValues("issuerKeyHash"));
/**
* @type {Integer}
* @desc serialNumber
*/
this.serialNumber = getParametersValue(parameters, "serialNumber", CertID.defaultValues("serialNumber"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "hashAlgorithm":
return new AlgorithmIdentifier();
case "issuerNameHash":
case "issuerKeyHash":
return new asn1js.OctetString();
case "serialNumber":
return new asn1js.Integer();
default:
throw new Error(`Invalid member name for CertID class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "hashAlgorithm":
return ((memberValue.algorithmId === "") && (("algorithmParams" in memberValue) === false));
case "issuerNameHash":
case "issuerKeyHash":
case "serialNumber":
return (memberValue.isEqual(CertID.defaultValues(memberName)));
default:
throw new Error(`Invalid member name for CertID class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* CertID ::= SEQUENCE {
* hashAlgorithm AlgorithmIdentifier,
* issuerNameHash OCTET STRING, -- Hash of issuer's DN
* issuerKeyHash OCTET STRING, -- Hash of issuer's public key
* serialNumber CertificateSerialNumber }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [hashAlgorithm]
* @property {string} [hashAlgorithmObject]
* @property {string} [issuerNameHash]
* @property {string} [issuerKeyHash]
* @property {string} [serialNumber]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
AlgorithmIdentifier.schema(names.hashAlgorithmObject || {
names: {
blockName: (names.hashAlgorithm || "")
}
}),
new asn1js.OctetString({ name: (names.issuerNameHash || "") }),
new asn1js.OctetString({ name: (names.issuerKeyHash || "") }),
new asn1js.Integer({ name: (names.serialNumber || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"hashAlgorithm",
"issuerNameHash",
"issuerKeyHash",
"serialNumber"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
CertID.schema({
names: {
hashAlgorithm: "hashAlgorithm",
issuerNameHash: "issuerNameHash",
issuerKeyHash: "issuerKeyHash",
serialNumber: "serialNumber"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for CertID");
//endregion
//region Get internal properties from parsed schema
this.hashAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.hashAlgorithm });
this.issuerNameHash = asn1.result.issuerNameHash;
this.issuerKeyHash = asn1.result.issuerKeyHash;
this.serialNumber = asn1.result.serialNumber;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
this.hashAlgorithm.toSchema(),
this.issuerNameHash,
this.issuerKeyHash,
this.serialNumber
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
hashAlgorithm: this.hashAlgorithm.toJSON(),
issuerNameHash: this.issuerNameHash.toJSON(),
issuerKeyHash: this.issuerKeyHash.toJSON(),
serialNumber: this.serialNumber.toJSON()
};
}
//**********************************************************************************
/**
* Check that two "CertIDs" are equal
* @param {CertID} certificateID Identifier of the certificate to be checked
* @returns {boolean}
*/
isEqual(certificateID)
{
//region Check "hashAlgorithm"
if(!this.hashAlgorithm.algorithmId === certificateID.hashAlgorithm.algorithmId)
return false;
//endregion
//region Check "issuerNameHash"
if(isEqualBuffer(this.issuerNameHash.valueBlock.valueHex, certificateID.issuerNameHash.valueBlock.valueHex) === false)
return false;
//endregion
//region Check "issuerKeyHash"
if(isEqualBuffer(this.issuerKeyHash.valueBlock.valueHex, certificateID.issuerKeyHash.valueBlock.valueHex) === false)
return false;
//endregion
//region Check "serialNumber"
if(!this.serialNumber.isEqual(certificateID.serialNumber))
return false;
//endregion
return true;
}
//**********************************************************************************
/**
* Making OCSP certificate identifier for specific certificate
* @param {Certificate} certificate Certificate making OCSP Request for
* @param {Object} parameters Additional parameters
* @returns {Promise}
*/
createForCertificate(certificate, parameters)
{
//region Initial variables
let sequence = Promise.resolve();
let issuerCertificate;
//endregion
//region Get a "crypto" extension
const crypto = getCrypto();
if(typeof crypto === "undefined")
return Promise.reject("Unable to create WebCrypto object");
//endregion
//region Check input parameters
if(("hashAlgorithm" in parameters) === false)
return Promise.reject("Parameter \"hashAlgorithm\" is mandatory for \"OCSP_REQUEST.createForCertificate\"");
const hashOID = getOIDByAlgorithm({ name: parameters.hashAlgorithm });
if(hashOID === "")
return Promise.reject(`Incorrect "hashAlgorithm": ${this.hashAlgorithm}`);
this.hashAlgorithm = new AlgorithmIdentifier({
algorithmId: hashOID,
algorithmParams: new asn1js.Null()
});
if("issuerCertificate" in parameters)
issuerCertificate = parameters.issuerCertificate;
else
return Promise.reject("Parameter \"issuerCertificate\" is mandatory for \"OCSP_REQUEST.createForCertificate\"");
//endregion
//region Initialize "serialNumber" field
this.serialNumber = certificate.serialNumber;
//endregion
//region Create "issuerNameHash"
sequence = sequence.then(() =>
crypto.digest({ name: parameters.hashAlgorithm }, issuerCertificate.subject.toSchema().toBER(false)),
error =>
Promise.reject(error)
);
//endregion
//region Create "issuerKeyHash"
sequence = sequence.then(result =>
{
this.issuerNameHash = new asn1js.OctetString({ valueHex: result });
const issuerKeyBuffer = issuerCertificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex;
return crypto.digest({ name: parameters.hashAlgorithm }, issuerKeyBuffer);
}, error =>
Promise.reject(error)
).then(result =>
{
this.issuerKeyHash = new asn1js.OctetString({ valueHex: result });
}, error =>
Promise.reject(error)
);
//endregion
return sequence;
}
//**********************************************************************************
}
//**************************************************************************************

625
core/third-party/pkijs/Certificate.js vendored Normal file
View File

@@ -0,0 +1,625 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, bufferToHexCodes, clearProps } from "./pvutils.js";
import { getCrypto, getEngine } from "./common.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import RelativeDistinguishedNames from "./RelativeDistinguishedNames.js";
import Time from "./Time.js";
import PublicKeyInfo from "./PublicKeyInfo.js";
import Extension from "./Extension.js";
import Extensions from "./Extensions.js";
//**************************************************************************************
function tbsCertificate(parameters = {})
{
//TBSCertificate ::= SEQUENCE {
// version [0] EXPLICIT Version DEFAULT v1,
// serialNumber CertificateSerialNumber,
// signature AlgorithmIdentifier,
// issuer Name,
// validity Validity,
// subject Name,
// subjectPublicKeyInfo SubjectPublicKeyInfo,
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
// -- If present, version MUST be v2 or v3
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
// -- If present, version MUST be v2 or v3
// extensions [3] EXPLICIT Extensions OPTIONAL
// -- If present, version MUST be v3
//}
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [tbsCertificateVersion]
* @property {string} [tbsCertificateSerialNumber]
* @property {string} [signature]
* @property {string} [issuer]
* @property {string} [tbsCertificateValidity]
* @property {string} [notBefore]
* @property {string} [notAfter]
* @property {string} [subject]
* @property {string} [subjectPublicKeyInfo]
* @property {string} [tbsCertificateIssuerUniqueID]
* @property {string} [tbsCertificateSubjectUniqueID]
* @property {string} [extensions]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || "tbsCertificate"),
value: [
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Integer({ name: (names.tbsCertificateVersion || "tbsCertificate.version") }) // EXPLICIT integer value
]
}),
new asn1js.Integer({ name: (names.tbsCertificateSerialNumber || "tbsCertificate.serialNumber") }),
AlgorithmIdentifier.schema(names.signature || {
names: {
blockName: "tbsCertificate.signature"
}
}),
RelativeDistinguishedNames.schema(names.issuer || {
names: {
blockName: "tbsCertificate.issuer"
}
}),
new asn1js.Sequence({
name: (names.tbsCertificateValidity || "tbsCertificate.validity"),
value: [
Time.schema(names.notBefore || {
names: {
utcTimeName: "tbsCertificate.notBefore",
generalTimeName: "tbsCertificate.notBefore"
}
}),
Time.schema(names.notAfter || {
names: {
utcTimeName: "tbsCertificate.notAfter",
generalTimeName: "tbsCertificate.notAfter"
}
})
]
}),
RelativeDistinguishedNames.schema(names.subject || {
names: {
blockName: "tbsCertificate.subject"
}
}),
PublicKeyInfo.schema(names.subjectPublicKeyInfo || {
names: {
blockName: "tbsCertificate.subjectPublicKeyInfo"
}
}),
new asn1js.Primitive({
name: (names.tbsCertificateIssuerUniqueID || "tbsCertificate.issuerUniqueID"),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
}
}), // IMPLICIT bistring value
new asn1js.Primitive({
name: (names.tbsCertificateSubjectUniqueID || "tbsCertificate.subjectUniqueID"),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
}
}), // IMPLICIT bistring value
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 3 // [3]
},
value: [Extensions.schema(names.extensions || {
names: {
blockName: "tbsCertificate.extensions"
}
})]
}) // EXPLICIT SEQUENCE value
]
}));
}
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class Certificate
{
//**********************************************************************************
/**
* Constructor for Certificate class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {ArrayBuffer}
* @desc ToBeSigned (TBS) part of the certificate
*/
this.tbs = getParametersValue(parameters, "tbs", Certificate.defaultValues("tbs"));
/**
* @type {number}
* @desc Version number
*/
this.version = getParametersValue(parameters, "version", Certificate.defaultValues("version"));
/**
* @type {Integer}
* @desc Serial number of the certificate
*/
this.serialNumber = getParametersValue(parameters, "serialNumber", Certificate.defaultValues("serialNumber"));
/**
* @type {AlgorithmIdentifier}
* @desc This field contains the algorithm identifier for the algorithm used by the CA to sign the certificate
*/
this.signature = getParametersValue(parameters, "signature", Certificate.defaultValues("signature"));
/**
* @type {RelativeDistinguishedNames}
* @desc The issuer field identifies the entity that has signed and issued the certificate
*/
this.issuer = getParametersValue(parameters, "issuer", Certificate.defaultValues("issuer"));
/**
* @type {Time}
* @desc The date on which the certificate validity period begins
*/
this.notBefore = getParametersValue(parameters, "notBefore", Certificate.defaultValues("notBefore"));
/**
* @type {Time}
* @desc The date on which the certificate validity period ends
*/
this.notAfter = getParametersValue(parameters, "notAfter", Certificate.defaultValues("notAfter"));
/**
* @type {RelativeDistinguishedNames}
* @desc The subject field identifies the entity associated with the public key stored in the subject public key field
*/
this.subject = getParametersValue(parameters, "subject", Certificate.defaultValues("subject"));
/**
* @type {PublicKeyInfo}
* @desc This field is used to carry the public key and identify the algorithm with which the key is used
*/
this.subjectPublicKeyInfo = getParametersValue(parameters, "subjectPublicKeyInfo", Certificate.defaultValues("subjectPublicKeyInfo"));
if("issuerUniqueID" in parameters)
/**
* @type {ArrayBuffer}
* @desc The subject and issuer unique identifiers are present in the certificate to handle the possibility of reuse of subject and/or issuer names over time
*/
this.issuerUniqueID = getParametersValue(parameters, "issuerUniqueID", Certificate.defaultValues("issuerUniqueID"));
if("subjectUniqueID" in parameters)
/**
* @type {ArrayBuffer}
* @desc The subject and issuer unique identifiers are present in the certificate to handle the possibility of reuse of subject and/or issuer names over time
*/
this.subjectUniqueID = getParametersValue(parameters, "subjectUniqueID", Certificate.defaultValues("subjectUniqueID"));
if("extensions" in parameters)
/**
* @type {Array}
* @desc If present, this field is a SEQUENCE of one or more certificate extensions
*/
this.extensions = getParametersValue(parameters, "extensions", Certificate.defaultValues("extensions"));
/**
* @type {AlgorithmIdentifier}
* @desc The signatureAlgorithm field contains the identifier for the cryptographic algorithm used by the CA to sign this certificate
*/
this.signatureAlgorithm = getParametersValue(parameters, "signatureAlgorithm", Certificate.defaultValues("signatureAlgorithm"));
/**
* @type {BitString}
* @desc The signatureValue field contains a digital signature computed upon the ASN.1 DER encoded tbsCertificate
*/
this.signatureValue = getParametersValue(parameters, "signatureValue", Certificate.defaultValues("signatureValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "tbs":
return new ArrayBuffer(0);
case "version":
return 0;
case "serialNumber":
return new asn1js.Integer();
case "signature":
return new AlgorithmIdentifier();
case "issuer":
return new RelativeDistinguishedNames();
case "notBefore":
return new Time();
case "notAfter":
return new Time();
case "subject":
return new RelativeDistinguishedNames();
case "subjectPublicKeyInfo":
return new PublicKeyInfo();
case "issuerUniqueID":
return new ArrayBuffer(0);
case "subjectUniqueID":
return new ArrayBuffer(0);
case "extensions":
return [];
case "signatureAlgorithm":
return new AlgorithmIdentifier();
case "signatureValue":
return new asn1js.BitString();
default:
throw new Error(`Invalid member name for Certificate class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* Certificate ::= SEQUENCE {
* tbsCertificate TBSCertificate,
* signatureAlgorithm AlgorithmIdentifier,
* signatureValue BIT STRING }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [tbsCertificate]
* @property {string} [signatureAlgorithm]
* @property {string} [signatureValue]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
tbsCertificate(names.tbsCertificate),
AlgorithmIdentifier.schema(names.signatureAlgorithm || {
names: {
blockName: "signatureAlgorithm"
}
}),
new asn1js.BitString({ name: (names.signatureValue || "signatureValue") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"tbsCertificate",
"tbsCertificate.extensions",
"tbsCertificate.version",
"tbsCertificate.serialNumber",
"tbsCertificate.signature",
"tbsCertificate.issuer",
"tbsCertificate.notBefore",
"tbsCertificate.notAfter",
"tbsCertificate.subject",
"tbsCertificate.subjectPublicKeyInfo",
"tbsCertificate.issuerUniqueID",
"tbsCertificate.subjectUniqueID",
"signatureAlgorithm",
"signatureValue"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
Certificate.schema({
names: {
tbsCertificate: {
names: {
extensions: {
names: {
extensions: "tbsCertificate.extensions"
}
}
}
}
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for Certificate");
//endregion
//region Get internal properties from parsed schema
this.tbs = asn1.result.tbsCertificate.valueBeforeDecode;
if("tbsCertificate.version" in asn1.result)
this.version = asn1.result["tbsCertificate.version"].valueBlock.valueDec;
this.serialNumber = asn1.result["tbsCertificate.serialNumber"];
this.signature = new AlgorithmIdentifier({ schema: asn1.result["tbsCertificate.signature"] });
this.issuer = new RelativeDistinguishedNames({ schema: asn1.result["tbsCertificate.issuer"] });
this.notBefore = new Time({ schema: asn1.result["tbsCertificate.notBefore"] });
this.notAfter = new Time({ schema: asn1.result["tbsCertificate.notAfter"] });
this.subject = new RelativeDistinguishedNames({ schema: asn1.result["tbsCertificate.subject"] });
this.subjectPublicKeyInfo = new PublicKeyInfo({ schema: asn1.result["tbsCertificate.subjectPublicKeyInfo"] });
if("tbsCertificate.issuerUniqueID" in asn1.result)
this.issuerUniqueID = asn1.result["tbsCertificate.issuerUniqueID"].valueBlock.valueHex;
if("tbsCertificate.subjectUniqueID" in asn1.result)
this.subjectUniqueID = asn1.result["tbsCertificate.subjectUniqueID"].valueBlock.valueHex;
if("tbsCertificate.extensions" in asn1.result)
this.extensions = Array.from(asn1.result["tbsCertificate.extensions"], element => new Extension({ schema: element }));
this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.signatureAlgorithm });
this.signatureValue = asn1.result.signatureValue;
//endregion
}
//**********************************************************************************
/**
* Create ASN.1 schema for existing values of TBS part for the certificate
*/
encodeTBS()
{
//region Create array for output sequence
const outputArray = [];
if(("version" in this) && (this.version !== Certificate.defaultValues("version")))
{
outputArray.push(new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Integer({ value: this.version }) // EXPLICIT integer value
]
}));
}
outputArray.push(this.serialNumber);
outputArray.push(this.signature.toSchema());
outputArray.push(this.issuer.toSchema());
outputArray.push(new asn1js.Sequence({
value: [
this.notBefore.toSchema(),
this.notAfter.toSchema()
]
}));
outputArray.push(this.subject.toSchema());
outputArray.push(this.subjectPublicKeyInfo.toSchema());
if("issuerUniqueID" in this)
{
outputArray.push(new asn1js.Primitive({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
valueHex: this.issuerUniqueID
}));
}
if("subjectUniqueID" in this)
{
outputArray.push(new asn1js.Primitive({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
},
valueHex: this.subjectUniqueID
}));
}
if("extensions" in this)
{
outputArray.push(new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 3 // [3]
},
value: [new asn1js.Sequence({
value: Array.from(this.extensions, element => element.toSchema())
})]
}));
}
//endregion
//region Create and return output sequence
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema(encodeFlag = false)
{
let tbsSchema = {};
//region Decode stored TBS value
if(encodeFlag === false)
{
if(this.tbs.length === 0) // No stored certificate TBS part
return Certificate.schema().value[0];
tbsSchema = asn1js.fromBER(this.tbs).result;
}
//endregion
//region Create TBS schema via assembling from TBS parts
else
tbsSchema = this.encodeTBS();
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
tbsSchema,
this.signatureAlgorithm.toSchema(),
this.signatureValue
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
tbs: bufferToHexCodes(this.tbs, 0, this.tbs.byteLength),
serialNumber: this.serialNumber.toJSON(),
signature: this.signature.toJSON(),
issuer: this.issuer.toJSON(),
notBefore: this.notBefore.toJSON(),
notAfter: this.notAfter.toJSON(),
subject: this.subject.toJSON(),
subjectPublicKeyInfo: this.subjectPublicKeyInfo.toJSON(),
signatureAlgorithm: this.signatureAlgorithm.toJSON(),
signatureValue: this.signatureValue.toJSON()
};
if(("version" in this) && (this.version !== Certificate.defaultValues("version")))
object.version = this.version;
if("issuerUniqueID" in this)
object.issuerUniqueID = bufferToHexCodes(this.issuerUniqueID, 0, this.issuerUniqueID.byteLength);
if("subjectUniqueID" in this)
object.subjectUniqueID = bufferToHexCodes(this.subjectUniqueID, 0, this.subjectUniqueID.byteLength);
if("extensions" in this)
object.extensions = Array.from(this.extensions, element => element.toJSON());
return object;
}
//**********************************************************************************
/**
* Importing public key for current certificate
*/
getPublicKey(parameters = null)
{
return getEngine().subtle.getPublicKey(this.subjectPublicKeyInfo, this.signatureAlgorithm, parameters);
}
//**********************************************************************************
/**
* Get hash value for subject public key (default SHA-1)
* @param {String} [hashAlgorithm=SHA-1] Hashing algorithm name
*/
getKeyHash(hashAlgorithm = "SHA-1")
{
//region Get a "crypto" extension
const crypto = getCrypto();
if(typeof crypto === "undefined")
return Promise.reject("Unable to create WebCrypto object");
//endregion
return crypto.digest({ name: hashAlgorithm }, new Uint8Array(this.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex));
}
//**********************************************************************************
/**
* Make a signature for current value from TBS section
* @param {Object} privateKey Private key for "subjectPublicKeyInfo" structure
* @param {string} [hashAlgorithm="SHA-1"] Hashing algorithm
*/
sign(privateKey, hashAlgorithm = "SHA-1")
{
//region Initial checking
//region Check private key
if(typeof privateKey === "undefined")
return Promise.reject("Need to provide a private key for signing");
//endregion
//endregion
//region Initial variables
let sequence = Promise.resolve();
let parameters;
const engine = getEngine();
//endregion
//region Get a "default parameters" for current algorithm and set correct signature algorithm
sequence = sequence.then(() => engine.subtle.getSignatureParameters(privateKey, hashAlgorithm));
sequence = sequence.then(result =>
{
parameters = result.parameters;
this.signature = result.signatureAlgorithm;
this.signatureAlgorithm = result.signatureAlgorithm;
});
//endregion
//region Create TBS data for signing
sequence = sequence.then(() =>
{
this.tbs = this.encodeTBS().toBER(false);
});
//endregion
//region Signing TBS data on provided private key
sequence = sequence.then(() => engine.subtle.signWithPrivateKey(this.tbs, privateKey, parameters));
sequence = sequence.then(result =>
{
this.signatureValue = new asn1js.BitString({ valueHex: result });
});
//endregion
return sequence;
}
//**********************************************************************************
verify(issuerCertificate = null)
{
//region Global variables
let subjectPublicKeyInfo = {};
//endregion
//region Set correct "subjectPublicKeyInfo" value
if(issuerCertificate !== null)
subjectPublicKeyInfo = issuerCertificate.subjectPublicKeyInfo;
else
{
if(this.issuer.isEqual(this.subject)) // Self-signed certificate
subjectPublicKeyInfo = this.subjectPublicKeyInfo;
}
if((subjectPublicKeyInfo instanceof PublicKeyInfo) === false)
return Promise.reject("Please provide issuer certificate as a parameter");
//endregion
return getEngine().subtle.verifyWithPublicKey(this.tbs, this.signatureValue, subjectPublicKeyInfo, this.signatureAlgorithm);
}
//**********************************************************************************
}
//**************************************************************************************

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,134 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import PolicyInformation from "./PolicyInformation.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class CertificatePolicies
{
//**********************************************************************************
/**
* Constructor for CertificatePolicies class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Array.<PolicyInformation>}
* @desc certificatePolicies
*/
this.certificatePolicies = getParametersValue(parameters, "certificatePolicies", CertificatePolicies.defaultValues("certificatePolicies"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "certificatePolicies":
return [];
default:
throw new Error(`Invalid member name for CertificatePolicies class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [certificatePolicies]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Repeated({
name: (names.certificatePolicies || ""),
value: PolicyInformation.schema()
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"certificatePolicies"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
CertificatePolicies.schema({
names: {
certificatePolicies: "certificatePolicies"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for CertificatePolicies");
//endregion
//region Get internal properties from parsed schema
this.certificatePolicies = Array.from(asn1.result.certificatePolicies, element => new PolicyInformation({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: Array.from(this.certificatePolicies, element => element.toSchema())
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
certificatePolicies: Array.from(this.certificatePolicies, element => element.toJSON())
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,538 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, bufferToHexCodes, clearProps } from "./pvutils.js";
import { getEngine } from "./common.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import RelativeDistinguishedNames from "./RelativeDistinguishedNames.js";
import Time from "./Time.js";
import RevokedCertificate from "./RevokedCertificate.js";
import Extensions from "./Extensions.js";
//**************************************************************************************
function tbsCertList(parameters = {})
{
//TBSCertList ::= SEQUENCE {
// version Version OPTIONAL,
// -- if present, MUST be v2
// signature AlgorithmIdentifier,
// issuer Name,
// thisUpdate Time,
// nextUpdate Time OPTIONAL,
// revokedCertificates SEQUENCE OF SEQUENCE {
// userCertificate CertificateSerialNumber,
// revocationDate Time,
// crlEntryExtensions Extensions OPTIONAL
// -- if present, version MUST be v2
// } OPTIONAL,
// crlExtensions [0] EXPLICIT Extensions OPTIONAL
// -- if present, version MUST be v2
//}
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [tbsCertListVersion]
* @property {string} [signature]
* @property {string} [issuer]
* @property {string} [tbsCertListThisUpdate]
* @property {string} [tbsCertListNextUpdate]
* @property {string} [tbsCertListRevokedCertificates]
* @property {string} [crlExtensions]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || "tbsCertList"),
value: [
new asn1js.Integer({
optional: true,
name: (names.tbsCertListVersion || "tbsCertList.version"),
value: 2
}), // EXPLICIT integer value (v2)
AlgorithmIdentifier.schema(names.signature || {
names: {
blockName: "tbsCertList.signature"
}
}),
RelativeDistinguishedNames.schema(names.issuer || {
names: {
blockName: "tbsCertList.issuer"
}
}),
Time.schema(names.tbsCertListThisUpdate || {
names: {
utcTimeName: "tbsCertList.thisUpdate",
generalTimeName: "tbsCertList.thisUpdate"
}
}),
Time.schema(names.tbsCertListNextUpdate || {
names: {
utcTimeName: "tbsCertList.nextUpdate",
generalTimeName: "tbsCertList.nextUpdate"
}
}, true),
new asn1js.Sequence({
optional: true,
value: [
new asn1js.Repeated({
name: (names.tbsCertListRevokedCertificates || "tbsCertList.revokedCertificates"),
value: new asn1js.Sequence({
value: [
new asn1js.Integer(),
Time.schema(),
Extensions.schema({}, true)
]
})
})
]
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [Extensions.schema(names.crlExtensions || {
names: {
blockName: "tbsCertList.extensions"
}
})]
}) // EXPLICIT SEQUENCE value
]
}));
}
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class CertificateRevocationList {
//**********************************************************************************
/**
* Constructor for Attribute class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {ArrayBuffer}
* @desc tbs
*/
this.tbs = getParametersValue(parameters, "tbs", CertificateRevocationList.defaultValues("tbs"));
/**
* @type {number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", CertificateRevocationList.defaultValues("version"));
/**
* @type {AlgorithmIdentifier}
* @desc signature
*/
this.signature = getParametersValue(parameters, "signature", CertificateRevocationList.defaultValues("signature"));
/**
* @type {RelativeDistinguishedNames}
* @desc issuer
*/
this.issuer = getParametersValue(parameters, "issuer", CertificateRevocationList.defaultValues("issuer"));
/**
* @type {Time}
* @desc thisUpdate
*/
this.thisUpdate = getParametersValue(parameters, "thisUpdate", CertificateRevocationList.defaultValues("thisUpdate"));
if("nextUpdate" in parameters)
/**
* @type {Time}
* @desc nextUpdate
*/
this.nextUpdate = getParametersValue(parameters, "nextUpdate", CertificateRevocationList.defaultValues("nextUpdate"));
if("revokedCertificates" in parameters)
/**
* @type {Array.<RevokedCertificate>}
* @desc revokedCertificates
*/
this.revokedCertificates = getParametersValue(parameters, "revokedCertificates", CertificateRevocationList.defaultValues("revokedCertificates"));
if("crlExtensions" in parameters)
/**
* @type {Extensions}
* @desc crlExtensions
*/
this.crlExtensions = getParametersValue(parameters, "crlExtensions", CertificateRevocationList.defaultValues("crlExtensions"));
/**
* @type {AlgorithmIdentifier}
* @desc signatureAlgorithm
*/
this.signatureAlgorithm = getParametersValue(parameters, "signatureAlgorithm", CertificateRevocationList.defaultValues("signatureAlgorithm"));
/**
* @type {BitString}
* @desc signatureValue
*/
this.signatureValue = getParametersValue(parameters, "signatureValue", CertificateRevocationList.defaultValues("signatureValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "tbs":
return new ArrayBuffer(0);
case "version":
return 1;
case "signature":
return new AlgorithmIdentifier();
case "issuer":
return new RelativeDistinguishedNames();
case "thisUpdate":
return new Time();
case "nextUpdate":
return new Time();
case "revokedCertificates":
return [];
case "crlExtensions":
return new Extensions();
case "signatureAlgorithm":
return new AlgorithmIdentifier();
case "signatureValue":
return new asn1js.BitString();
default:
throw new Error(`Invalid member name for CertificateRevocationList class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* CertificateList ::= SEQUENCE {
* tbsCertList TBSCertList,
* signatureAlgorithm AlgorithmIdentifier,
* signatureValue BIT STRING }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [signatureAlgorithm]
* @property {string} [signatureValue]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || "CertificateList"),
value: [
tbsCertList(parameters),
AlgorithmIdentifier.schema(names.signatureAlgorithm || {
names: {
blockName: "signatureAlgorithm"
}
}),
new asn1js.BitString({ name: (names.signatureValue || "signatureValue") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"tbsCertList",
"tbsCertList.version",
"tbsCertList.signature",
"tbsCertList.issuer",
"tbsCertList.thisUpdate",
"tbsCertList.nextUpdate",
"tbsCertList.revokedCertificates",
"tbsCertList.extensions",
"signatureAlgorithm",
"signatureValue"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
CertificateRevocationList.schema()
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for CertificateRevocationList");
//endregion
//region Get internal properties from parsed schema
// noinspection JSUnresolvedVariable
this.tbs = asn1.result.tbsCertList.valueBeforeDecode;
if("tbsCertList.version" in asn1.result)
this.version = asn1.result["tbsCertList.version"].valueBlock.valueDec;
this.signature = new AlgorithmIdentifier({ schema: asn1.result["tbsCertList.signature"] });
this.issuer = new RelativeDistinguishedNames({ schema: asn1.result["tbsCertList.issuer"] });
this.thisUpdate = new Time({ schema: asn1.result["tbsCertList.thisUpdate"] });
if("tbsCertList.nextUpdate" in asn1.result)
this.nextUpdate = new Time({ schema: asn1.result["tbsCertList.nextUpdate"] });
if("tbsCertList.revokedCertificates" in asn1.result)
this.revokedCertificates = Array.from(asn1.result["tbsCertList.revokedCertificates"], element => new RevokedCertificate({ schema: element }));
if("tbsCertList.extensions" in asn1.result)
this.crlExtensions = new Extensions({ schema: asn1.result["tbsCertList.extensions"] });
this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.signatureAlgorithm });
this.signatureValue = asn1.result.signatureValue;
//endregion
}
//**********************************************************************************
encodeTBS()
{
//region Create array for output sequence
const outputArray = [];
if(this.version !== CertificateRevocationList.defaultValues("version"))
outputArray.push(new asn1js.Integer({ value: this.version }));
outputArray.push(this.signature.toSchema());
outputArray.push(this.issuer.toSchema());
outputArray.push(this.thisUpdate.toSchema());
if("nextUpdate" in this)
outputArray.push(this.nextUpdate.toSchema());
if("revokedCertificates" in this)
{
outputArray.push(new asn1js.Sequence({
value: Array.from(this.revokedCertificates, element => element.toSchema())
}));
}
if("crlExtensions" in this)
{
outputArray.push(new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
this.crlExtensions.toSchema()
]
}));
}
//endregion
return (new asn1js.Sequence({
value: outputArray
}));
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema(encodeFlag = false)
{
//region Decode stored TBS value
let tbsSchema;
if(encodeFlag === false)
{
if(this.tbs.length === 0) // No stored TBS part
return CertificateRevocationList.schema();
tbsSchema = asn1js.fromBER(this.tbs).result;
}
//endregion
//region Create TBS schema via assembling from TBS parts
else
tbsSchema = this.encodeTBS();
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
tbsSchema,
this.signatureAlgorithm.toSchema(),
this.signatureValue
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
tbs: bufferToHexCodes(this.tbs, 0, this.tbs.byteLength),
signature: this.signature.toJSON(),
issuer: this.issuer.toJSON(),
thisUpdate: this.thisUpdate.toJSON(),
signatureAlgorithm: this.signatureAlgorithm.toJSON(),
signatureValue: this.signatureValue.toJSON()
};
if(this.version !== CertificateRevocationList.defaultValues("version"))
object.version = this.version;
if("nextUpdate" in this)
object.nextUpdate = this.nextUpdate.toJSON();
if("revokedCertificates" in this)
object.revokedCertificates = Array.from(this.revokedCertificates, element => element.toJSON());
if("crlExtensions" in this)
object.crlExtensions = this.crlExtensions.toJSON();
return object;
}
//**********************************************************************************
isCertificateRevoked(certificate)
{
//region Check that issuer of the input certificate is the same with issuer of this CRL
if(this.issuer.isEqual(certificate.issuer) === false)
return false;
//endregion
//region Check that there are revoked certificates in this CRL
if(("revokedCertificates" in this) === false)
return false;
//endregion
//region Search for input certificate in revoked certificates array
for(const revokedCertificate of this.revokedCertificates)
{
if(revokedCertificate.userCertificate.isEqual(certificate.serialNumber))
return true;
}
//endregion
return false;
}
//**********************************************************************************
/**
* Make a signature for existing CRL data
* @param {Object} privateKey Private key for "subjectPublicKeyInfo" structure
* @param {string} [hashAlgorithm] Hashing algorithm. Default SHA-1
*/
sign(privateKey, hashAlgorithm = "SHA-1")
{
//region Initial checking
//region Get a private key from function parameter
if(typeof privateKey === "undefined")
return Promise.reject("Need to provide a private key for signing");
//endregion
//endregion
//region Initial variables
let sequence = Promise.resolve();
let parameters;
const engine = getEngine();
//endregion
//region Get a "default parameters" for current algorithm and set correct signature algorithm
sequence = sequence.then(() => engine.subtle.getSignatureParameters(privateKey, hashAlgorithm));
sequence = sequence.then(result =>
{
parameters = result.parameters;
this.signature = result.signatureAlgorithm;
this.signatureAlgorithm = result.signatureAlgorithm;
});
//endregion
//region Create TBS data for signing
sequence = sequence.then(() =>
{
this.tbs = this.encodeTBS().toBER(false);
});
//endregion
//region Signing TBS data on provided private key
sequence = sequence.then(() => engine.subtle.signWithPrivateKey(this.tbs, privateKey, parameters));
sequence = sequence.then(result =>
{
this.signatureValue = new asn1js.BitString({ valueHex: result });
});
//endregion
return sequence;
}
//**********************************************************************************
/**
* Verify existing signature
* @param {{[issuerCertificate]: Object, [publicKeyInfo]: Object}} parameters
* @returns {*}
*/
verify(parameters = {})
{
//region Global variables
let sequence = Promise.resolve();
let subjectPublicKeyInfo = -1;
const engine = getEngine();
//endregion
//region Get information about CRL issuer certificate
if("issuerCertificate" in parameters) // "issuerCertificate" must be of type "Certificate"
{
subjectPublicKeyInfo = parameters.issuerCertificate.subjectPublicKeyInfo;
// The CRL issuer name and "issuerCertificate" subject name are not equal
if(this.issuer.isEqual(parameters.issuerCertificate.subject) === false)
return Promise.resolve(false);
}
//region In case if there is only public key during verification
if("publicKeyInfo" in parameters)
subjectPublicKeyInfo = parameters.publicKeyInfo; // Must be of type "PublicKeyInfo"
//endregion
if(("subjectPublicKey" in subjectPublicKeyInfo) === false)
return Promise.reject("Issuer's certificate must be provided as an input parameter");
//endregion
//region Check the CRL for unknown critical extensions
if("crlExtensions" in this)
{
for(const extension of this.crlExtensions.extensions)
{
if(extension.critical)
{
// We can not be sure that unknown extension has no value for CRL signature
if(("parsedValue" in extension) === false)
return Promise.resolve(false);
}
}
}
//endregion
sequence = sequence.then(() => engine.subtle.verifyWithPublicKey(this.tbs, this.signatureValue, subjectPublicKeyInfo, this.signatureAlgorithm));
return sequence;
}
//**********************************************************************************
}
//**************************************************************************************

235
core/third-party/pkijs/CertificateSet.js vendored Normal file
View File

@@ -0,0 +1,235 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import Certificate from "./Certificate.js";
import AttributeCertificateV1 from "./AttributeCertificateV1.js";
import AttributeCertificateV2 from "./AttributeCertificateV2.js";
import OtherCertificateFormat from "./OtherCertificateFormat.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class CertificateSet
{
//**********************************************************************************
/**
* Constructor for CertificateSet class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Array}
* @desc certificates
*/
this.certificates = getParametersValue(parameters, "certificates", CertificateSet.defaultValues("certificates"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "certificates":
return [];
default:
throw new Error(`Invalid member name for Attribute class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* CertificateSet ::= SET OF CertificateChoices
*
* CertificateChoices ::= CHOICE {
* certificate Certificate,
* extendedCertificate [0] IMPLICIT ExtendedCertificate, -- Obsolete
* v1AttrCert [1] IMPLICIT AttributeCertificateV1, -- Obsolete
* v2AttrCert [2] IMPLICIT AttributeCertificateV2,
* other [3] IMPLICIT OtherCertificateFormat }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
*/
const names = getParametersValue(parameters, "names", {});
return (
new asn1js.Set({
name: (names.blockName || ""),
value: [
new asn1js.Repeated({
name: (names.certificates || "certificates"),
value: new asn1js.Choice({
value: [
Certificate.schema(),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Any()
]
}), // JUST A STUB
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: AttributeCertificateV1.schema().valueBlock.value
}),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
},
value: AttributeCertificateV2.schema().valueBlock.value
}),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 3 // [3]
},
value: OtherCertificateFormat.schema().valueBlock.value
})
]
})
})
]
})
);
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"certificates"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
CertificateSet.schema()
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for CertificateSet");
//endregion
//region Get internal properties from parsed schema
this.certificates = Array.from(asn1.result.certificates || [], element =>
{
const initialTagNumber = element.idBlock.tagNumber;
if(element.idBlock.tagClass === 1)
return new Certificate({ schema: element });
//region Making "Sequence" from "Constructed" value
const elementSequence = new asn1js.Sequence({
value: element.valueBlock.value
});
//endregion
switch(initialTagNumber)
{
case 1:
return new AttributeCertificateV1({ schema: elementSequence });
case 2:
return new AttributeCertificateV2({ schema: elementSequence });
case 3:
return new OtherCertificateFormat({ schema: elementSequence });
case 0:
default:
}
return element;
});
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Set({
value: Array.from(this.certificates, element =>
{
switch(true)
{
case (element instanceof Certificate):
return element.toSchema();
case (element instanceof AttributeCertificateV1):
return new asn1js.Constructed({
idBlock: {
tagClass: 3,
tagNumber: 1 // [1]
},
value: element.toSchema().valueBlock.value
});
case (element instanceof AttributeCertificateV2):
return new asn1js.Constructed({
idBlock: {
tagClass: 3,
tagNumber: 2 // [2]
},
value: element.toSchema().valueBlock.value
});
case (element instanceof OtherCertificateFormat):
return new asn1js.Constructed({
idBlock: {
tagClass: 3,
tagNumber: 3 // [3]
},
value: element.toSchema().valueBlock.value
});
default:
}
return element;
})
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
certificates: Array.from(this.certificates, element => element.toJSON())
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,191 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from "[MS-WCCE]: Windows Client Certificate Enrollment Protocol"
*/
export default class CertificateTemplate
{
//**********************************************************************************
/**
* Constructor for CertificateTemplate class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc templateID
*/
this.templateID = getParametersValue(parameters, "templateID", CertificateTemplate.defaultValues("templateID"));
if("templateMajorVersion" in parameters)
/**
* @type {number}
* @desc templateMajorVersion
*/
this.templateMajorVersion = getParametersValue(parameters, "templateMajorVersion", CertificateTemplate.defaultValues("templateMajorVersion"));
if("templateMinorVersion" in parameters)
/**
* @type {number}
* @desc templateMinorVersion
*/
this.templateMinorVersion = getParametersValue(parameters, "templateMinorVersion", CertificateTemplate.defaultValues("templateMinorVersion"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "templateID":
return "";
case "templateMajorVersion":
case "templateMinorVersion":
return 0;
default:
throw new Error(`Invalid member name for CertificateTemplate class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* CertificateTemplateOID ::= SEQUENCE {
* templateID OBJECT IDENTIFIER,
* templateMajorVersion INTEGER (0..4294967295) OPTIONAL,
* templateMinorVersion INTEGER (0..4294967295) OPTIONAL
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [templateID]
* @property {string} [templateMajorVersion]
* @property {string} [templateMinorVersion]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.templateID || "") }),
new asn1js.Integer({
name: (names.templateMajorVersion || ""),
optional: true
}),
new asn1js.Integer({
name: (names.templateMinorVersion || ""),
optional: true
}),
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"templateID",
"templateMajorVersion",
"templateMinorVersion"
]);
//endregion
//region Check the schema is valid
let asn1 = asn1js.compareSchema(schema,
schema,
CertificateTemplate.schema({
names: {
templateID: "templateID",
templateMajorVersion: "templateMajorVersion",
templateMinorVersion: "templateMinorVersion"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for CertificateTemplate");
//endregion
//region Get internal properties from parsed schema
this.templateID = asn1.result.templateID.valueBlock.toString();
if("templateMajorVersion" in asn1.result)
this.templateMajorVersion = asn1.result.templateMajorVersion.valueBlock.valueDec;
if("templateMinorVersion" in asn1.result)
this.templateMinorVersion = asn1.result.templateMinorVersion.valueBlock.valueDec;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(new asn1js.ObjectIdentifier({ value: this.templateID }));
if("templateMajorVersion" in this)
outputArray.push(new asn1js.Integer({ value: this.templateMajorVersion }));
if("templateMinorVersion" in this)
outputArray.push(new asn1js.Integer({ value: this.templateMinorVersion }));
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
extnID: this.templateID
};
if("templateMajorVersion" in this)
object.templateMajorVersion = this.templateMajorVersion;
if("templateMinorVersion" in this)
object.templateMinorVersion = this.templateMinorVersion;
return object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,383 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, bufferToHexCodes, clearProps } from "./pvutils.js";
import { getEngine } from "./common.js";
import PublicKeyInfo from "./PublicKeyInfo.js";
import RelativeDistinguishedNames from "./RelativeDistinguishedNames.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import Attribute from "./Attribute.js";
//**************************************************************************************
function CertificationRequestInfo(parameters = {})
{
//CertificationRequestInfo ::= SEQUENCE {
// version INTEGER { v1(0) } (v1,...),
// subject Name,
// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
// attributes [0] Attributes{{ CRIAttributes }}
//}
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [CertificationRequestInfo]
* @property {string} [CertificationRequestInfoVersion]
* @property {string} [subject]
* @property {string} [CertificationRequestInfoAttributes]
* @property {string} [attributes]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.CertificationRequestInfo || "CertificationRequestInfo"),
value: [
new asn1js.Integer({ name: (names.CertificationRequestInfoVersion || "CertificationRequestInfo.version") }),
RelativeDistinguishedNames.schema(names.subject || {
names: {
blockName: "CertificationRequestInfo.subject"
}
}),
PublicKeyInfo.schema({
names: {
blockName: "CertificationRequestInfo.subjectPublicKeyInfo"
}
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Repeated({
optional: true, // Because OpenSSL makes wrong "attributes" field
name: (names.CertificationRequestInfoAttributes || "CertificationRequestInfo.attributes"),
value: Attribute.schema(names.attributes || {})
})
]
})
]
}));
}
//**************************************************************************************
/**
* Class from RFC2986
*/
export default class CertificationRequest
{
//**********************************************************************************
/**
* Constructor for Attribute class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {ArrayBuffer}
* @desc tbs
*/
this.tbs = getParametersValue(parameters, "tbs", CertificationRequest.defaultValues("tbs"));
/**
* @type {number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", CertificationRequest.defaultValues("version"));
/**
* @type {RelativeDistinguishedNames}
* @desc subject
*/
this.subject = getParametersValue(parameters, "subject", CertificationRequest.defaultValues("subject"));
/**
* @type {PublicKeyInfo}
* @desc subjectPublicKeyInfo
*/
this.subjectPublicKeyInfo = getParametersValue(parameters, "subjectPublicKeyInfo", CertificationRequest.defaultValues("subjectPublicKeyInfo"));
if("attributes" in parameters)
/**
* @type {Array.<Attribute>}
* @desc attributes
*/
this.attributes = getParametersValue(parameters, "attributes", CertificationRequest.defaultValues("attributes"));
/**
* @type {AlgorithmIdentifier}
* @desc signatureAlgorithm
*/
this.signatureAlgorithm = getParametersValue(parameters, "signatureAlgorithm", CertificationRequest.defaultValues("signatureAlgorithm"));
/**
* @type {BitString}
* @desc signatureAlgorithm
*/
this.signatureValue = getParametersValue(parameters, "signatureValue", CertificationRequest.defaultValues("signatureValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "tbs":
return new ArrayBuffer(0);
case "version":
return 0;
case "subject":
return new RelativeDistinguishedNames();
case "subjectPublicKeyInfo":
return new PublicKeyInfo();
case "attributes":
return [];
case "signatureAlgorithm":
return new AlgorithmIdentifier();
case "signatureValue":
return new asn1js.BitString();
default:
throw new Error(`Invalid member name for CertificationRequest class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* CertificationRequest ::= SEQUENCE {
* certificationRequestInfo CertificationRequestInfo,
* signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
* signature BIT STRING
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [certificationRequestInfo]
* @property {string} [signatureAlgorithm]
* @property {string} [signatureValue]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
value: [
CertificationRequestInfo(names.certificationRequestInfo || {}),
new asn1js.Sequence({
name: (names.signatureAlgorithm || "signatureAlgorithm"),
value: [
new asn1js.ObjectIdentifier(),
new asn1js.Any({ optional: true })
]
}),
new asn1js.BitString({ name: (names.signatureValue || "signatureValue") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"CertificationRequestInfo",
"CertificationRequestInfo.version",
"CertificationRequestInfo.subject",
"CertificationRequestInfo.subjectPublicKeyInfo",
"CertificationRequestInfo.attributes",
"signatureAlgorithm",
"signatureValue"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
CertificationRequest.schema()
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for CertificationRequest");
//endregion
//region Get internal properties from parsed schema
this.tbs = asn1.result.CertificationRequestInfo.valueBeforeDecode;
this.version = asn1.result["CertificationRequestInfo.version"].valueBlock.valueDec;
this.subject = new RelativeDistinguishedNames({ schema: asn1.result["CertificationRequestInfo.subject"] });
this.subjectPublicKeyInfo = new PublicKeyInfo({ schema: asn1.result["CertificationRequestInfo.subjectPublicKeyInfo"] });
if("CertificationRequestInfo.attributes" in asn1.result)
this.attributes = Array.from(asn1.result["CertificationRequestInfo.attributes"], element => new Attribute({ schema: element }));
this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.signatureAlgorithm });
this.signatureValue = asn1.result.signatureValue;
//endregion
}
//**********************************************************************************
/**
* Aux function making ASN1js Sequence from current TBS
* @returns {Sequence}
*/
encodeTBS()
{
//region Create array for output sequence
const outputArray = [
new asn1js.Integer({ value: this.version }),
this.subject.toSchema(),
this.subjectPublicKeyInfo.toSchema()
];
if("attributes" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: Array.from(this.attributes, element => element.toSchema())
}));
}
//endregion
return (new asn1js.Sequence({
value: outputArray
}));
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema(encodeFlag = false)
{
//region Decode stored TBS value
let tbsSchema;
if(encodeFlag === false)
{
if(this.tbs.byteLength === 0) // No stored TBS part
return CertificationRequest.schema();
tbsSchema = asn1js.fromBER(this.tbs).result;
}
//endregion
//region Create TBS schema via assembling from TBS parts
else
tbsSchema = this.encodeTBS();
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
tbsSchema,
this.signatureAlgorithm.toSchema(),
this.signatureValue
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
tbs: bufferToHexCodes(this.tbs, 0, this.tbs.byteLength),
version: this.version,
subject: this.subject.toJSON(),
subjectPublicKeyInfo: this.subjectPublicKeyInfo.toJSON(),
signatureAlgorithm: this.signatureAlgorithm.toJSON(),
signatureValue: this.signatureValue.toJSON()
};
if("attributes" in this)
object.attributes = Array.from(this.attributes, element => element.toJSON());
return object;
}
//**********************************************************************************
/**
* Makes signature for currect certification request
* @param {Object} privateKey WebCrypto private key
* @param {string} [hashAlgorithm=SHA-1] String representing current hashing algorithm
*/
sign(privateKey, hashAlgorithm = "SHA-1")
{
//region Initial checking
//region Get a private key from function parameter
if(typeof privateKey === "undefined")
return Promise.reject("Need to provide a private key for signing");
//endregion
//endregion
//region Initial variables
let sequence = Promise.resolve();
let parameters;
const engine = getEngine();
//endregion
//region Get a "default parameters" for current algorithm and set correct signature algorithm
sequence = sequence.then(() => engine.subtle.getSignatureParameters(privateKey, hashAlgorithm));
sequence = sequence.then(result =>
{
parameters = result.parameters;
this.signatureAlgorithm = result.signatureAlgorithm;
});
//endregion
//region Create TBS data for signing
sequence = sequence.then(() =>
{
this.tbs = this.encodeTBS().toBER(false);
});
//endregion
//region Signing TBS data on provided private key
sequence = sequence.then(() => engine.subtle.signWithPrivateKey(this.tbs, privateKey, parameters));
sequence = sequence.then(result =>
{
this.signatureValue = new asn1js.BitString({ valueHex: result });
});
//endregion
return sequence;
}
//**********************************************************************************
/**
* Verify existing certification request signature
* @returns {*}
*/
verify()
{
return getEngine().subtle.verifyWithPublicKey(this.tbs, this.signatureValue, this.subjectPublicKeyInfo, this.signatureAlgorithm);
}
//**********************************************************************************
/**
* Importing public key for current certificate request
*/
getPublicKey(parameters = null)
{
return getEngine().getPublicKey(this.subjectPublicKeyInfo, this.signatureAlgorithm, parameters);
}
//**********************************************************************************
}
//**************************************************************************************

181
core/third-party/pkijs/ContentInfo.js vendored Normal file
View File

@@ -0,0 +1,181 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class ContentInfo
{
//**********************************************************************************
/**
* Constructor for ContentInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc contentType
*/
this.contentType = getParametersValue(parameters, "contentType", ContentInfo.defaultValues("contentType"));
/**
* @type {Any}
* @desc content
*/
this.content = getParametersValue(parameters, "content", ContentInfo.defaultValues("content"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "contentType":
return "";
case "content":
return new asn1js.Any();
default:
throw new Error(`Invalid member name for ContentInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "contentType":
return (memberValue === "");
case "content":
return (memberValue instanceof asn1js.Any);
default:
throw new Error(`Invalid member name for ContentInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* ContentInfo ::= SEQUENCE {
* contentType ContentType,
* content [0] EXPLICIT ANY DEFINED BY contentType }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [contentType]
* @property {string} [content]
*/
const names = getParametersValue(parameters, "names", {});
if(("optional" in names) === false)
names.optional = false;
return (new asn1js.Sequence({
name: (names.blockName || "ContentInfo"),
optional: names.optional,
value: [
new asn1js.ObjectIdentifier({ name: (names.contentType || "contentType") }),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [new asn1js.Any({ name: (names.content || "content") })] // EXPLICIT ANY value
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"contentType",
"content"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
ContentInfo.schema()
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for ContentInfo");
//endregion
//region Get internal properties from parsed schema
this.contentType = asn1.result.contentType.valueBlock.toString();
this.content = asn1.result.content;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.contentType }),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [this.content] // EXPLICIT ANY value
})
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
contentType: this.contentType
};
if(!(this.content instanceof asn1js.Any))
object.content = this.content.toJSON();
return object;
}
//**********************************************************************************
}
//**************************************************************************************

2565
core/third-party/pkijs/CryptoEngine.js vendored Normal file

File diff suppressed because it is too large Load Diff

179
core/third-party/pkijs/DigestInfo.js vendored Normal file
View File

@@ -0,0 +1,179 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
//**************************************************************************************
/**
* Class from RFC3447
*/
export default class DigestInfo
{
//**********************************************************************************
/**
* Constructor for DigestInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {AlgorithmIdentifier}
* @desc digestAlgorithm
*/
this.digestAlgorithm = getParametersValue(parameters, "digestAlgorithm", DigestInfo.defaultValues("digestAlgorithm"));
/**
* @type {OctetString}
* @desc digest
*/
this.digest = getParametersValue(parameters, "digest", DigestInfo.defaultValues("digest"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "digestAlgorithm":
return new AlgorithmIdentifier();
case "digest":
return new asn1js.OctetString();
default:
throw new Error(`Invalid member name for DigestInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "digestAlgorithm":
return ((AlgorithmIdentifier.compareWithDefault("algorithmId", memberValue.algorithmId)) &&
(("algorithmParams" in memberValue) === false));
case "digest":
return (memberValue.isEqual(DigestInfo.defaultValues(memberName)));
default:
throw new Error(`Invalid member name for DigestInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* DigestInfo ::= SEQUENCE {
* digestAlgorithm DigestAlgorithmIdentifier,
* digest Digest }
*
* Digest ::= OCTET STRING
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [type]
* @property {string} [setName]
* @property {string} [values]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
AlgorithmIdentifier.schema(names.digestAlgorithm || {
names: {
blockName: "digestAlgorithm"
}
}),
new asn1js.OctetString({ name: (names.digest || "digest") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"digestAlgorithm",
"digest"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
DigestInfo.schema({
names: {
digestAlgorithm: {
names: {
blockName: "digestAlgorithm"
}
},
digest: "digest"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for DigestInfo");
//endregion
//region Get internal properties from parsed schema
this.digestAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.digestAlgorithm });
this.digest = asn1.result.digest;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
this.digestAlgorithm.toSchema(),
this.digest
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
digestAlgorithm: this.digestAlgorithm.toJSON(),
digest: this.digest.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,334 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import GeneralName from "./GeneralName.js";
import RelativeDistinguishedNames from "./RelativeDistinguishedNames.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class DistributionPoint
{
//**********************************************************************************
/**
* Constructor for DistributionPoint class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
* @property {Object} [distributionPoint]
* @property {Object} [reasons]
* @property {Object} [cRLIssuer]
*/
constructor(parameters = {})
{
//region Internal properties of the object
if("distributionPoint" in parameters)
/**
* @type {Array.<GeneralName>}
* @desc distributionPoint
*/
this.distributionPoint = getParametersValue(parameters, "distributionPoint", DistributionPoint.defaultValues("distributionPoint"));
if("reasons" in parameters)
/**
* @type {BitString}
* @desc values
*/
this.reasons = getParametersValue(parameters, "reasons", DistributionPoint.defaultValues("reasons"));
if("cRLIssuer" in parameters)
/**
* @type {Array.<GeneralName>}
* @desc cRLIssuer
*/
this.cRLIssuer = getParametersValue(parameters, "cRLIssuer", DistributionPoint.defaultValues("cRLIssuer"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "distributionPoint":
return [];
case "reasons":
return new asn1js.BitString();
case "cRLIssuer":
return [];
default:
throw new Error(`Invalid member name for DistributionPoint class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* DistributionPoint ::= SEQUENCE {
* distributionPoint [0] DistributionPointName OPTIONAL,
* reasons [1] ReasonFlags OPTIONAL,
* cRLIssuer [2] GeneralNames OPTIONAL }
*
* DistributionPointName ::= CHOICE {
* fullName [0] GeneralNames,
* nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
*
* ReasonFlags ::= BIT STRING {
* unused (0),
* keyCompromise (1),
* cACompromise (2),
* affiliationChanged (3),
* superseded (4),
* cessationOfOperation (5),
* certificateHold (6),
* privilegeWithdrawn (7),
* aACompromise (8) }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [distributionPoint]
* @property {string} [distributionPointNames]
* @property {string} [reasons]
* @property {string} [cRLIssuer]
* @property {string} [cRLIssuerNames]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Choice({
value: [
new asn1js.Constructed({
name: (names.distributionPoint || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Repeated({
name: (names.distributionPointNames || ""),
value: GeneralName.schema()
})
]
}),
new asn1js.Constructed({
name: (names.distributionPoint || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: RelativeDistinguishedNames.schema().valueBlock.value
})
]
})
]
}),
new asn1js.Primitive({
name: (names.reasons || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
}
}), // IMPLICIT bitstring value
new asn1js.Constructed({
name: (names.cRLIssuer || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
},
value: [
new asn1js.Repeated({
name: (names.cRLIssuerNames || ""),
value: GeneralName.schema()
})
]
}) // IMPLICIT bitstring value
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"distributionPoint",
"distributionPointNames",
"reasons",
"cRLIssuer",
"cRLIssuerNames"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
DistributionPoint.schema({
names: {
distributionPoint: "distributionPoint",
distributionPointNames: "distributionPointNames",
reasons: "reasons",
cRLIssuer: "cRLIssuer",
cRLIssuerNames: "cRLIssuerNames"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for DistributionPoint");
//endregion
//region Get internal properties from parsed schema
if("distributionPoint" in asn1.result)
{
if(asn1.result.distributionPoint.idBlock.tagNumber === 0) // GENERAL_NAMES variant
this.distributionPoint = Array.from(asn1.result.distributionPointNames, element => new GeneralName({ schema: element }));
if(asn1.result.distributionPoint.idBlock.tagNumber === 1) // RDN variant
{
this.distributionPoint = new RelativeDistinguishedNames({
schema: new asn1js.Sequence({
value: asn1.result.distributionPoint.valueBlock.value
})
});
}
}
if("reasons" in asn1.result)
this.reasons = new asn1js.BitString({ valueHex: asn1.result.reasons.valueBlock.valueHex });
if("cRLIssuer" in asn1.result)
this.cRLIssuer = Array.from(asn1.result.cRLIssuerNames, element => new GeneralName({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
if("distributionPoint" in this)
{
let internalValue;
if(this.distributionPoint instanceof Array)
{
internalValue = new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: Array.from(this.distributionPoint, element => element.toSchema())
});
}
else
{
internalValue = new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [this.distributionPoint.toSchema()]
});
}
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [internalValue]
}));
}
if("reasons" in this)
{
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
valueHex: this.reasons.valueBlock.valueHex
}));
}
if("cRLIssuer" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
},
value: Array.from(this.cRLIssuer, element => element.toSchema())
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {};
if("distributionPoint" in this)
{
if(this.distributionPoint instanceof Array)
object.distributionPoint = Array.from(this.distributionPoint, element => element.toJSON());
else
object.distributionPoint = this.distributionPoint.toJSON();
}
if("reasons" in this)
object.reasons = this.reasons.toJSON();
if("cRLIssuer" in this)
object.cRLIssuer = Array.from(this.cRLIssuer, element => element.toJSON());
return object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,233 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
//**************************************************************************************
/**
* Class from RFC6318
*/
export default class ECCCMSSharedInfo
{
//**********************************************************************************
/**
* Constructor for ECCCMSSharedInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {AlgorithmIdentifier}
* @desc keyInfo
*/
this.keyInfo = getParametersValue(parameters, "keyInfo", ECCCMSSharedInfo.defaultValues("keyInfo"));
if("entityUInfo" in parameters)
/**
* @type {OctetString}
* @desc entityUInfo
*/
this.entityUInfo = getParametersValue(parameters, "entityUInfo", ECCCMSSharedInfo.defaultValues("entityUInfo"));
/**
* @type {OctetString}
* @desc suppPubInfo
*/
this.suppPubInfo = getParametersValue(parameters, "suppPubInfo", ECCCMSSharedInfo.defaultValues("suppPubInfo"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "keyInfo":
return new AlgorithmIdentifier();
case "entityUInfo":
return new asn1js.OctetString();
case "suppPubInfo":
return new asn1js.OctetString();
default:
throw new Error(`Invalid member name for ECCCMSSharedInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "keyInfo":
case "entityUInfo":
case "suppPubInfo":
return (memberValue.isEqual(ECCCMSSharedInfo.defaultValues(memberName)));
default:
throw new Error(`Invalid member name for ECCCMSSharedInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* ECC-CMS-SharedInfo ::= SEQUENCE {
* keyInfo AlgorithmIdentifier,
* entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL,
* suppPubInfo [2] EXPLICIT OCTET STRING }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [keyInfo]
* @property {string} [entityUInfo]
* @property {string} [suppPubInfo]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
AlgorithmIdentifier.schema(names.keyInfo || {}),
new asn1js.Constructed({
name: (names.entityUInfo || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
optional: true,
value: [new asn1js.OctetString()]
}),
new asn1js.Constructed({
name: (names.suppPubInfo || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
},
value: [new asn1js.OctetString()]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"keyInfo",
"entityUInfo",
"suppPubInfo"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
ECCCMSSharedInfo.schema({
names: {
keyInfo: {
names: {
blockName: "keyInfo"
}
},
entityUInfo: "entityUInfo",
suppPubInfo: "suppPubInfo"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for ECCCMSSharedInfo");
//endregion
//region Get internal properties from parsed schema
this.keyInfo = new AlgorithmIdentifier({ schema: asn1.result.keyInfo });
if("entityUInfo" in asn1.result)
this.entityUInfo = asn1.result.entityUInfo.valueBlock.value[0];
this.suppPubInfo = asn1.result.suppPubInfo.valueBlock.value[0];
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create output array for sequence
const outputArray = [];
outputArray.push(this.keyInfo.toSchema());
if("entityUInfo" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [this.entityUInfo]
}));
}
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
},
value: [this.suppPubInfo]
}));
//endregion
//region Construct and return new ASN.1 schema for this object
return new asn1js.Sequence({
value: outputArray
});
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
keyInfo: this.keyInfo.toJSON()
};
if("entityUInfo" in this)
_object.entityUInfo = this.entityUInfo.toJSON();
_object.suppPubInfo = this.suppPubInfo.toJSON();
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

344
core/third-party/pkijs/ECPrivateKey.js vendored Normal file
View File

@@ -0,0 +1,344 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, toBase64, arrayBufferToString, stringToArrayBuffer, fromBase64, clearProps } from "./pvutils.js";
import ECPublicKey from "./ECPublicKey.js";
//**************************************************************************************
/**
* Class from RFC5915
*/
export default class ECPrivateKey
{
//**********************************************************************************
/**
* Constructor for ECPrivateKey class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", ECPrivateKey.defaultValues("version"));
/**
* @type {OctetString}
* @desc privateKey
*/
this.privateKey = getParametersValue(parameters, "privateKey", ECPrivateKey.defaultValues("privateKey"));
if("namedCurve" in parameters)
/**
* @type {string}
* @desc namedCurve
*/
this.namedCurve = getParametersValue(parameters, "namedCurve", ECPrivateKey.defaultValues("namedCurve"));
if("publicKey" in parameters)
/**
* @type {ECPublicKey}
* @desc publicKey
*/
this.publicKey = getParametersValue(parameters, "publicKey", ECPrivateKey.defaultValues("publicKey"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
//region If input argument array contains "json" for this object
if("json" in parameters)
this.fromJSON(parameters.json);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "version":
return 1;
case "privateKey":
return new asn1js.OctetString();
case "namedCurve":
return "";
case "publicKey":
return new ECPublicKey();
default:
throw new Error(`Invalid member name for ECCPrivateKey class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "version":
return (memberValue === ECPrivateKey.defaultValues(memberName));
case "privateKey":
return (memberValue.isEqual(ECPrivateKey.defaultValues(memberName)));
case "namedCurve":
return (memberValue === "");
case "publicKey":
return ((ECPublicKey.compareWithDefault("namedCurve", memberValue.namedCurve)) &&
(ECPublicKey.compareWithDefault("x", memberValue.x)) &&
(ECPublicKey.compareWithDefault("y", memberValue.y)));
default:
throw new Error(`Invalid member name for ECCPrivateKey class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* ECPrivateKey ::= SEQUENCE {
* version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
* privateKey OCTET STRING,
* parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
* publicKey [1] BIT STRING OPTIONAL
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [version]
* @property {string} [privateKey]
* @property {string} [namedCurve]
* @property {string} [publicKey]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.version || "") }),
new asn1js.OctetString({ name: (names.privateKey || "") }),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.ObjectIdentifier({ name: (names.namedCurve || "") })
]
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [
new asn1js.BitString({ name: (names.publicKey || "") })
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"version",
"privateKey",
"namedCurve",
"publicKey"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
ECPrivateKey.schema({
names: {
version: "version",
privateKey: "privateKey",
namedCurve: "namedCurve",
publicKey: "publicKey"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for ECPrivateKey");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result.version.valueBlock.valueDec;
this.privateKey = asn1.result.privateKey;
if("namedCurve" in asn1.result)
this.namedCurve = asn1.result.namedCurve.valueBlock.toString();
if("publicKey" in asn1.result)
{
const publicKeyData = { schema: asn1.result.publicKey.valueBlock.valueHex };
if("namedCurve" in this)
publicKeyData.namedCurve = this.namedCurve;
this.publicKey = new ECPublicKey(publicKeyData);
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
const outputArray = [
new asn1js.Integer({ value: this.version }),
this.privateKey
];
if("namedCurve" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.ObjectIdentifier({ value: this.namedCurve })
]
}));
}
if("publicKey" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [
new asn1js.BitString({ valueHex: this.publicKey.toSchema().toBER(false) })
]
}));
}
return new asn1js.Sequence({
value: outputArray
});
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
if((("namedCurve" in this) === false) || (ECPrivateKey.compareWithDefault("namedCurve", this.namedCurve)))
throw new Error("Not enough information for making JSON: absent \"namedCurve\" value");
let crvName = "";
switch(this.namedCurve)
{
case "1.2.840.10045.3.1.7": // P-256
crvName = "P-256";
break;
case "1.3.132.0.34": // P-384
crvName = "P-384";
break;
case "1.3.132.0.35": // P-521
crvName = "P-521";
break;
default:
}
const privateKeyJSON = {
crv: crvName,
d: toBase64(arrayBufferToString(this.privateKey.valueBlock.valueHex), true, true, false)
};
if("publicKey" in this)
{
const publicKeyJSON = this.publicKey.toJSON();
privateKeyJSON.x = publicKeyJSON.x;
privateKeyJSON.y = publicKeyJSON.y;
}
return privateKeyJSON;
}
//**********************************************************************************
/**
* Convert JSON value into current object
* @param {Object} json
*/
fromJSON(json)
{
let coodinateLength = 0;
if("crv" in json)
{
switch(json.crv.toUpperCase())
{
case "P-256":
this.namedCurve = "1.2.840.10045.3.1.7";
coodinateLength = 32;
break;
case "P-384":
this.namedCurve = "1.3.132.0.34";
coodinateLength = 48;
break;
case "P-521":
this.namedCurve = "1.3.132.0.35";
coodinateLength = 66;
break;
default:
}
}
else
throw new Error("Absent mandatory parameter \"crv\"");
if("d" in json)
{
const convertBuffer = stringToArrayBuffer(fromBase64(json.d, true));
if(convertBuffer.byteLength < coodinateLength)
{
const buffer = new ArrayBuffer(coodinateLength);
const view = new Uint8Array(buffer);
const convertBufferView = new Uint8Array(convertBuffer);
view.set(convertBufferView, 1);
this.privateKey = new asn1js.OctetString({ valueHex: buffer });
}
else
this.privateKey = new asn1js.OctetString({ valueHex: convertBuffer.slice(0, coodinateLength) });
}
else
throw new Error("Absent mandatory parameter \"d\"");
if(("x" in json) && ("y" in json))
this.publicKey = new ECPublicKey({ json });
}
//**********************************************************************************
}
//**************************************************************************************

242
core/third-party/pkijs/ECPublicKey.js vendored Normal file
View File

@@ -0,0 +1,242 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, utilConcatBuf, isEqualBuffer, toBase64, fromBase64, arrayBufferToString, stringToArrayBuffer } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5480
*/
export default class ECPublicKey
{
//**********************************************************************************
/**
* Constructor for ECCPublicKey class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {ArrayBuffer}
* @desc type
*/
this.x = getParametersValue(parameters, "x", ECPublicKey.defaultValues("x"));
/**
* @type {ArrayBuffer}
* @desc values
*/
this.y = getParametersValue(parameters, "y", ECPublicKey.defaultValues("y"));
/**
* @type {string}
* @desc namedCurve
*/
this.namedCurve = getParametersValue(parameters, "namedCurve", ECPublicKey.defaultValues("namedCurve"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
//region If input argument array contains "json" for this object
if("json" in parameters)
this.fromJSON(parameters.json);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "x":
case "y":
return new ArrayBuffer(0);
case "namedCurve":
return "";
default:
throw new Error(`Invalid member name for ECCPublicKey class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "x":
case "y":
return (isEqualBuffer(memberValue, ECPublicKey.defaultValues(memberName)));
case "namedCurve":
return (memberValue === "");
default:
throw new Error(`Invalid member name for ECCPublicKey class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
return new asn1js.RawData();
}
//**********************************************************************************
/**
* Convert ArrayBuffer into current class
* @param {!ArrayBuffer} schema Special case: schema is an ArrayBuffer
*/
fromSchema(schema)
{
//region Check the schema is valid
if((schema instanceof ArrayBuffer) === false)
throw new Error("Object's schema was not verified against input data for ECPublicKey");
const view = new Uint8Array(schema);
if(view[0] !== 0x04)
throw new Error("Object's schema was not verified against input data for ECPublicKey");
//endregion
//region Get internal properties from parsed schema
let coordinateLength;
switch(this.namedCurve)
{
case "1.2.840.10045.3.1.7": // P-256
coordinateLength = 32;
break;
case "1.3.132.0.34": // P-384
coordinateLength = 48;
break;
case "1.3.132.0.35": // P-521
coordinateLength = 66;
break;
default:
throw new Error(`Incorrect curve OID: ${this.namedCurve}`);
}
if(schema.byteLength !== (coordinateLength * 2 + 1))
throw new Error("Object's schema was not verified against input data for ECPublicKey");
this.x = schema.slice(1, coordinateLength + 1);
this.y = schema.slice(1 + coordinateLength, coordinateLength * 2 + 1);
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
return new asn1js.RawData({ data: utilConcatBuf(
(new Uint8Array([0x04])).buffer,
this.x,
this.y
)
});
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
let crvName = "";
switch(this.namedCurve)
{
case "1.2.840.10045.3.1.7": // P-256
crvName = "P-256";
break;
case "1.3.132.0.34": // P-384
crvName = "P-384";
break;
case "1.3.132.0.35": // P-521
crvName = "P-521";
break;
default:
}
return {
crv: crvName,
x: toBase64(arrayBufferToString(this.x), true, true, false),
y: toBase64(arrayBufferToString(this.y), true, true, false)
};
}
//**********************************************************************************
/**
* Convert JSON value into current object
* @param {Object} json
*/
fromJSON(json)
{
let coodinateLength = 0;
if("crv" in json)
{
switch(json.crv.toUpperCase())
{
case "P-256":
this.namedCurve = "1.2.840.10045.3.1.7";
coodinateLength = 32;
break;
case "P-384":
this.namedCurve = "1.3.132.0.34";
coodinateLength = 48;
break;
case "P-521":
this.namedCurve = "1.3.132.0.35";
coodinateLength = 66;
break;
default:
}
}
else
throw new Error("Absent mandatory parameter \"crv\"");
if("x" in json)
{
const convertBuffer = stringToArrayBuffer(fromBase64(json.x, true));
if(convertBuffer.byteLength < coodinateLength)
{
this.x = new ArrayBuffer(coodinateLength);
const view = new Uint8Array(this.x);
const convertBufferView = new Uint8Array(convertBuffer);
view.set(convertBufferView, 1);
}
else
this.x = convertBuffer.slice(0, coodinateLength);
}
else
throw new Error("Absent mandatory parameter \"x\"");
if("y" in json)
{
const convertBuffer = stringToArrayBuffer(fromBase64(json.y, true));
if(convertBuffer.byteLength < coodinateLength)
{
this.y = new ArrayBuffer(coodinateLength);
const view = new Uint8Array(this.y);
const convertBufferView = new Uint8Array(convertBuffer);
view.set(convertBufferView, 1);
}
else
this.y = convertBuffer.slice(0, coodinateLength);
}
else
throw new Error("Absent mandatory parameter \"y\"");
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,243 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class EncapsulatedContentInfo
{
//**********************************************************************************
/**
* Constructor for EncapsulatedContentInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc eContentType
*/
this.eContentType = getParametersValue(parameters, "eContentType", EncapsulatedContentInfo.defaultValues("eContentType"));
if("eContent" in parameters)
{
/**
* @type {OctetString}
* @desc eContent
*/
this.eContent = getParametersValue(parameters, "eContent", EncapsulatedContentInfo.defaultValues("eContent"));
if((this.eContent.idBlock.tagClass === 1) &&
(this.eContent.idBlock.tagNumber === 4))
{
//region Divide OCTETSTRING value down to small pieces
if(this.eContent.idBlock.isConstructed === false)
{
const constrString = new asn1js.OctetString({
idBlock: { isConstructed: true },
isConstructed: true
});
let offset = 0;
let length = this.eContent.valueBlock.valueHex.byteLength;
while(length > 0)
{
const pieceView = new Uint8Array(this.eContent.valueBlock.valueHex, offset, ((offset + 65536) > this.eContent.valueBlock.valueHex.byteLength) ? (this.eContent.valueBlock.valueHex.byteLength - offset) : 65536);
const _array = new ArrayBuffer(pieceView.length);
const _view = new Uint8Array(_array);
for(let i = 0; i < _view.length; i++)
_view[i] = pieceView[i];
constrString.valueBlock.value.push(new asn1js.OctetString({ valueHex: _array }));
length -= pieceView.length;
offset += pieceView.length;
}
this.eContent = constrString;
}
//endregion
}
}
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "eContentType":
return "";
case "eContent":
return new asn1js.OctetString();
default:
throw new Error(`Invalid member name for EncapsulatedContentInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "eContentType":
return (memberValue === "");
case "eContent":
{
if((memberValue.idBlock.tagClass === 1) && (memberValue.idBlock.tagNumber === 4))
return (memberValue.isEqual(EncapsulatedContentInfo.defaultValues("eContent")));
return false;
}
default:
throw new Error(`Invalid member name for EncapsulatedContentInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* EncapsulatedContentInfo ::= SEQUENCE {
* eContentType ContentType,
* eContent [0] EXPLICIT OCTET STRING OPTIONAL } * Changed it to ANY, as in PKCS#7
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [type]
* @property {string} [setName]
* @property {string} [values]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.eContentType || "") }),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Any({ name: (names.eContent || "") }) // In order to aling this with PKCS#7 and CMS as well
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"eContentType",
"eContent"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
EncapsulatedContentInfo.schema({
names: {
eContentType: "eContentType",
eContent: "eContent"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for EncapsulatedContentInfo");
//endregion
//region Get internal properties from parsed schema
this.eContentType = asn1.result.eContentType.valueBlock.toString();
if("eContent" in asn1.result)
this.eContent = asn1.result.eContent;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(new asn1js.ObjectIdentifier({ value: this.eContentType }));
if("eContent" in this)
{
if(EncapsulatedContentInfo.compareWithDefault("eContent", this.eContent) === false)
{
outputArray.push(new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [this.eContent]
}));
}
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
eContentType: this.eContentType
};
if("eContent" in this)
{
if(EncapsulatedContentInfo.compareWithDefault("eContent", this.eContent) === false)
_object.eContent = this.eContent.toJSON();
}
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,287 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class EncryptedContentInfo
{
//**********************************************************************************
/**
* Constructor for EncryptedContentInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc contentType
*/
this.contentType = getParametersValue(parameters, "contentType", EncryptedContentInfo.defaultValues("contentType"));
/**
* @type {AlgorithmIdentifier}
* @desc contentEncryptionAlgorithm
*/
this.contentEncryptionAlgorithm = getParametersValue(parameters, "contentEncryptionAlgorithm", EncryptedContentInfo.defaultValues("contentEncryptionAlgorithm"));
if("encryptedContent" in parameters)
{
/**
* @type {OctetString}
* @desc encryptedContent (!!!) could be contructive or primitive value (!!!)
*/
this.encryptedContent = parameters.encryptedContent;
if((this.encryptedContent.idBlock.tagClass === 1) &&
(this.encryptedContent.idBlock.tagNumber === 4))
{
//region Divide OCTETSTRING value down to small pieces
if(this.encryptedContent.idBlock.isConstructed === false)
{
const constrString = new asn1js.OctetString({
idBlock: { isConstructed: true },
isConstructed: true
});
let offset = 0;
let length = this.encryptedContent.valueBlock.valueHex.byteLength;
while(length > 0)
{
const pieceView = new Uint8Array(this.encryptedContent.valueBlock.valueHex, offset, ((offset + 1024) > this.encryptedContent.valueBlock.valueHex.byteLength) ? (this.encryptedContent.valueBlock.valueHex.byteLength - offset) : 1024);
const _array = new ArrayBuffer(pieceView.length);
const _view = new Uint8Array(_array);
for(let i = 0; i < _view.length; i++)
_view[i] = pieceView[i];
constrString.valueBlock.value.push(new asn1js.OctetString({ valueHex: _array }));
length -= pieceView.length;
offset += pieceView.length;
}
this.encryptedContent = constrString;
}
//endregion
}
}
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "contentType":
return "";
case "contentEncryptionAlgorithm":
return new AlgorithmIdentifier();
case "encryptedContent":
return new asn1js.OctetString();
default:
throw new Error(`Invalid member name for EncryptedContentInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "contentType":
return (memberValue === "");
case "contentEncryptionAlgorithm":
return ((memberValue.algorithmId === "") && (("algorithmParams" in memberValue) === false));
case "encryptedContent":
return (memberValue.isEqual(EncryptedContentInfo.defaultValues(memberName)));
default:
throw new Error(`Invalid member name for EncryptedContentInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* EncryptedContentInfo ::= SEQUENCE {
* contentType ContentType,
* contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }
*
* Comment: Strange, but modern crypto engines create "encryptedContent" as "[0] EXPLICIT EncryptedContent"
*
* EncryptedContent ::= OCTET STRING
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [contentType]
* @property {string} [contentEncryptionAlgorithm]
* @property {string} [encryptedContent]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.contentType || "") }),
AlgorithmIdentifier.schema(names.contentEncryptionAlgorithm || {}),
// The CHOICE we need because "EncryptedContent" could have either "constructive"
// or "primitive" form of encoding and we need to handle both variants
new asn1js.Choice({
value: [
new asn1js.Constructed({
name: (names.encryptedContent || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Repeated({
value: new asn1js.OctetString()
})
]
}),
new asn1js.Primitive({
name: (names.encryptedContent || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
}
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"contentType",
"contentEncryptionAlgorithm",
"encryptedContent"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
EncryptedContentInfo.schema({
names: {
contentType: "contentType",
contentEncryptionAlgorithm: {
names: {
blockName: "contentEncryptionAlgorithm"
}
},
encryptedContent: "encryptedContent"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for EncryptedContentInfo");
//endregion
//region Get internal properties from parsed schema
this.contentType = asn1.result.contentType.valueBlock.toString();
this.contentEncryptionAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.contentEncryptionAlgorithm });
if("encryptedContent" in asn1.result)
{
this.encryptedContent = asn1.result.encryptedContent;
this.encryptedContent.idBlock.tagClass = 1; // UNIVERSAL
this.encryptedContent.idBlock.tagNumber = 4; // OCTETSTRING (!!!) The value still has instance of "in_window.org.pkijs.asn1.ASN1_CONSTRUCTED / ASN1_PRIMITIVE"
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const sequenceLengthBlock = {
isIndefiniteForm: false
};
const outputArray = [];
outputArray.push(new asn1js.ObjectIdentifier({ value: this.contentType }));
outputArray.push(this.contentEncryptionAlgorithm.toSchema());
if("encryptedContent" in this)
{
sequenceLengthBlock.isIndefiniteForm = this.encryptedContent.idBlock.isConstructed;
const encryptedValue = this.encryptedContent;
encryptedValue.idBlock.tagClass = 3; // CONTEXT-SPECIFIC
encryptedValue.idBlock.tagNumber = 0; // [0]
encryptedValue.lenBlock.isIndefiniteForm = this.encryptedContent.idBlock.isConstructed;
outputArray.push(encryptedValue);
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
lenBlock: sequenceLengthBlock,
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
contentType: this.contentType,
contentEncryptionAlgorithm: this.contentEncryptionAlgorithm.toJSON()
};
if("encryptedContent" in this)
_object.encryptedContent = this.encryptedContent.toJSON();
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

287
core/third-party/pkijs/EncryptedData.js vendored Normal file
View File

@@ -0,0 +1,287 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import { getEngine } from "./common.js";
import EncryptedContentInfo from "./EncryptedContentInfo.js";
import Attribute from "./Attribute.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class EncryptedData
{
//**********************************************************************************
/**
* Constructor for EncryptedData class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", EncryptedData.defaultValues("version"));
/**
* @type {EncryptedContentInfo}
* @desc encryptedContentInfo
*/
this.encryptedContentInfo = getParametersValue(parameters, "encryptedContentInfo", EncryptedData.defaultValues("encryptedContentInfo"));
if("unprotectedAttrs" in parameters)
/**
* @type {Array.<Attribute>}
* @desc unprotectedAttrs
*/
this.unprotectedAttrs = getParametersValue(parameters, "unprotectedAttrs", EncryptedData.defaultValues("unprotectedAttrs"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "version":
return 0;
case "encryptedContentInfo":
return new EncryptedContentInfo();
case "unprotectedAttrs":
return [];
default:
throw new Error(`Invalid member name for EncryptedData class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "version":
return (memberValue === 0);
case "encryptedContentInfo":
return ((EncryptedContentInfo.compareWithDefault("contentType", memberValue.contentType)) &&
(EncryptedContentInfo.compareWithDefault("contentEncryptionAlgorithm", memberValue.contentEncryptionAlgorithm)) &&
(EncryptedContentInfo.compareWithDefault("encryptedContent", memberValue.encryptedContent)));
case "unprotectedAttrs":
return (memberValue.length === 0);
default:
throw new Error(`Invalid member name for EncryptedData class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* EncryptedData ::= SEQUENCE {
* version CMSVersion,
* encryptedContentInfo EncryptedContentInfo,
* unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [version]
* @property {string} [encryptedContentInfo]
* @property {string} [unprotectedAttrs]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.version || "") }),
EncryptedContentInfo.schema(names.encryptedContentInfo || {}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [
new asn1js.Repeated({
name: (names.unprotectedAttrs || ""),
value: Attribute.schema()
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"version",
"encryptedContentInfo",
"unprotectedAttrs"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
EncryptedData.schema({
names: {
version: "version",
encryptedContentInfo: {
names: {
blockName: "encryptedContentInfo"
}
},
unprotectedAttrs: "unprotectedAttrs"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for EncryptedData");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result.version.valueBlock.valueDec;
this.encryptedContentInfo = new EncryptedContentInfo({ schema: asn1.result.encryptedContentInfo });
if("unprotectedAttrs" in asn1.result)
this.unprotectedAttrs = Array.from(asn1.result.unprotectedAttrs, element => new Attribute({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(new asn1js.Integer({ value: this.version }));
outputArray.push(this.encryptedContentInfo.toSchema());
if("unprotectedAttrs" in this)
{
outputArray.push(new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: Array.from(this.unprotectedAttrs, element => element.toSchema())
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
version: this.version,
encryptedContentInfo: this.encryptedContentInfo.toJSON()
};
if("unprotectedAttrs" in this)
_object.unprotectedAttrs = Array.from(this.unprotectedAttrs, element => element.toJSON());
return _object;
}
//**********************************************************************************
/**
* Create a new CMS Encrypted Data content
* @param {Object} parameters Parameters neccessary for encryption
* @returns {Promise}
*/
encrypt(parameters)
{
//region Check for input parameters
if((parameters instanceof Object) === false)
return Promise.reject("Parameters must have type \"Object\"");
//endregion
//region Get cryptographic engine
const engine = getEngine();
if(typeof engine === "undefined")
return Promise.reject("Unable to initialize cryptographic engine");
//endregion
//region Set "contentType" parameter
parameters.contentType = "1.2.840.113549.1.7.1"; // "data"
//endregion
if("encryptEncryptedContentInfo" in engine.subtle)
{
return engine.subtle.encryptEncryptedContentInfo(parameters).then(result =>
{
this.encryptedContentInfo = result;
});
}
return Promise.reject(`No support for "encryptEncryptedContentInfo" in current crypto engine ${engine.name}`);
}
//**********************************************************************************
/**
* Create a new CMS Encrypted Data content
* @param {Object} parameters Parameters neccessary for encryption
*/
decrypt(parameters)
{
//region Check for input parameters
if((parameters instanceof Object) === false)
return Promise.reject("Parameters must have type \"Object\"");
//endregion
//region Get cryptographic engine
const engine = getEngine();
if(typeof engine === "undefined")
return Promise.reject("Unable to initialize cryptographic engine");
//endregion
//region Set "encryptedContentInfo" value
parameters.encryptedContentInfo = this.encryptedContentInfo;
//endregion
if("decryptEncryptedContentInfo" in engine.subtle)
return engine.subtle.decryptEncryptedContentInfo(parameters);
return Promise.reject(`No support for "decryptEncryptedContentInfo" in current crypto engine ${engine.name}`);
}
//**********************************************************************************
}
//**************************************************************************************

1729
core/third-party/pkijs/EnvelopedData.js vendored Normal file

File diff suppressed because it is too large Load Diff

135
core/third-party/pkijs/ExtKeyUsage.js vendored Normal file
View File

@@ -0,0 +1,135 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class ExtKeyUsage
{
//**********************************************************************************
/**
* Constructor for ExtKeyUsage class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Array.<string>}
* @desc keyPurposes
*/
this.keyPurposes = getParametersValue(parameters, "keyPurposes", ExtKeyUsage.defaultValues("keyPurposes"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "keyPurposes":
return [];
default:
throw new Error(`Invalid member name for ExtKeyUsage class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* ExtKeyUsage ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
*
* KeyPurposeId ::= OBJECT IDENTIFIER
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [keyPurposes]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Repeated({
name: (names.keyPurposes || ""),
value: new asn1js.ObjectIdentifier()
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"keyPurposes"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
ExtKeyUsage.schema({
names: {
keyPurposes: "keyPurposes"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for ExtKeyUsage");
//endregion
//region Get internal properties from parsed schema
this.keyPurposes = Array.from(asn1.result.keyPurposes, element => element.valueBlock.toString());
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: Array.from(this.keyPurposes, element => new asn1js.ObjectIdentifier({ value: element }))
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
keyPurposes: Array.from(this.keyPurposes)
};
}
//**********************************************************************************
}
//**************************************************************************************

453
core/third-party/pkijs/Extension.js vendored Normal file
View File

@@ -0,0 +1,453 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import SubjectDirectoryAttributes from "./SubjectDirectoryAttributes.js";
import PrivateKeyUsagePeriod from "./PrivateKeyUsagePeriod.js";
import AltName from "./AltName.js";
import BasicConstraints from "./BasicConstraints.js";
import IssuingDistributionPoint from "./IssuingDistributionPoint.js";
import GeneralNames from "./GeneralNames.js";
import NameConstraints from "./NameConstraints.js";
import CRLDistributionPoints from "./CRLDistributionPoints.js";
import CertificatePolicies from "./CertificatePolicies.js";
import PolicyMappings from "./PolicyMappings.js";
import AuthorityKeyIdentifier from "./AuthorityKeyIdentifier.js";
import PolicyConstraints from "./PolicyConstraints.js";
import ExtKeyUsage from "./ExtKeyUsage.js";
import InfoAccess from "./InfoAccess.js";
import SignedCertificateTimestampList from "./SignedCertificateTimestampList.js";
import CertificateTemplate from "./CertificateTemplate.js";
import CAVersion from "./CAVersion.js";
import QCStatements from "./QCStatements.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class Extension
{
//**********************************************************************************
/**
* Constructor for Extension class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc extnID
*/
this.extnID = getParametersValue(parameters, "extnID", Extension.defaultValues("extnID"));
/**
* @type {boolean}
* @desc critical
*/
this.critical = getParametersValue(parameters, "critical", Extension.defaultValues("critical"));
/**
* @type {OctetString}
* @desc extnValue
*/
if("extnValue" in parameters)
this.extnValue = new asn1js.OctetString({ valueHex: parameters.extnValue });
else
this.extnValue = Extension.defaultValues("extnValue");
if("parsedValue" in parameters)
/**
* @type {Object}
* @desc parsedValue
*/
this.parsedValue = getParametersValue(parameters, "parsedValue", Extension.defaultValues("parsedValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "extnID":
return "";
case "critical":
return false;
case "extnValue":
return new asn1js.OctetString();
case "parsedValue":
return {};
default:
throw new Error(`Invalid member name for Extension class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* Extension ::= SEQUENCE {
* extnID OBJECT IDENTIFIER,
* critical BOOLEAN DEFAULT FALSE,
* extnValue OCTET STRING
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [extnID]
* @property {string} [critical]
* @property {string} [extnValue]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.extnID || "") }),
new asn1js.Boolean({
name: (names.critical || ""),
optional: true
}),
new asn1js.OctetString({ name: (names.extnValue || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"extnID",
"critical",
"extnValue"
]);
//endregion
//region Check the schema is valid
let asn1 = asn1js.compareSchema(schema,
schema,
Extension.schema({
names: {
extnID: "extnID",
critical: "critical",
extnValue: "extnValue"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for Extension");
//endregion
//region Get internal properties from parsed schema
this.extnID = asn1.result.extnID.valueBlock.toString();
if("critical" in asn1.result)
this.critical = asn1.result.critical.valueBlock.value;
this.extnValue = asn1.result.extnValue;
//region Get "parsedValue" for well-known extensions
asn1 = asn1js.fromBER(this.extnValue.valueBlock.valueHex);
if(asn1.offset === (-1))
return;
switch(this.extnID)
{
case "2.5.29.9": // SubjectDirectoryAttributes
try
{
this.parsedValue = new SubjectDirectoryAttributes({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new SubjectDirectoryAttributes();
this.parsedValue.parsingError = "Incorrectly formated SubjectDirectoryAttributes";
}
break;
case "2.5.29.14": // SubjectKeyIdentifier
this.parsedValue = asn1.result; // Should be just a simple OCTETSTRING
break;
case "2.5.29.15": // KeyUsage
this.parsedValue = asn1.result; // Should be just a simple BITSTRING
break;
case "2.5.29.16": // PrivateKeyUsagePeriod
try
{
this.parsedValue = new PrivateKeyUsagePeriod({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new PrivateKeyUsagePeriod();
this.parsedValue.parsingError = "Incorrectly formated PrivateKeyUsagePeriod";
}
break;
case "2.5.29.17": // SubjectAltName
case "2.5.29.18": // IssuerAltName
try
{
this.parsedValue = new AltName({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new AltName();
this.parsedValue.parsingError = "Incorrectly formated AltName";
}
break;
case "2.5.29.19": // BasicConstraints
try
{
this.parsedValue = new BasicConstraints({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new BasicConstraints();
this.parsedValue.parsingError = "Incorrectly formated BasicConstraints";
}
break;
case "2.5.29.20": // CRLNumber
case "2.5.29.27": // BaseCRLNumber (delta CRL indicator)
this.parsedValue = asn1.result; // Should be just a simple INTEGER
break;
case "2.5.29.21": // CRLReason
this.parsedValue = asn1.result; // Should be just a simple ENUMERATED
break;
case "2.5.29.24": // InvalidityDate
this.parsedValue = asn1.result; // Should be just a simple GeneralizedTime
break;
case "2.5.29.28": // IssuingDistributionPoint
try
{
this.parsedValue = new IssuingDistributionPoint({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new IssuingDistributionPoint();
this.parsedValue.parsingError = "Incorrectly formated IssuingDistributionPoint";
}
break;
case "2.5.29.29": // CertificateIssuer
try
{
this.parsedValue = new GeneralNames({ schema: asn1.result }); // Should be just a simple
}
catch(ex)
{
this.parsedValue = new GeneralNames();
this.parsedValue.parsingError = "Incorrectly formated GeneralNames";
}
break;
case "2.5.29.30": // NameConstraints
try
{
this.parsedValue = new NameConstraints({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new NameConstraints();
this.parsedValue.parsingError = "Incorrectly formated NameConstraints";
}
break;
case "2.5.29.31": // CRLDistributionPoints
case "2.5.29.46": // FreshestCRL
try
{
this.parsedValue = new CRLDistributionPoints({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new CRLDistributionPoints();
this.parsedValue.parsingError = "Incorrectly formated CRLDistributionPoints";
}
break;
case "2.5.29.32": // CertificatePolicies
case "1.3.6.1.4.1.311.21.10": // szOID_APPLICATION_CERT_POLICIES - Microsoft-specific OID
try
{
this.parsedValue = new CertificatePolicies({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new CertificatePolicies();
this.parsedValue.parsingError = "Incorrectly formated CertificatePolicies";
}
break;
case "2.5.29.33": // PolicyMappings
try
{
this.parsedValue = new PolicyMappings({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new PolicyMappings();
this.parsedValue.parsingError = "Incorrectly formated CertificatePolicies";
}
break;
case "2.5.29.35": // AuthorityKeyIdentifier
try
{
this.parsedValue = new AuthorityKeyIdentifier({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new AuthorityKeyIdentifier();
this.parsedValue.parsingError = "Incorrectly formated AuthorityKeyIdentifier";
}
break;
case "2.5.29.36": // PolicyConstraints
try
{
this.parsedValue = new PolicyConstraints({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new PolicyConstraints();
this.parsedValue.parsingError = "Incorrectly formated PolicyConstraints";
}
break;
case "2.5.29.37": // ExtKeyUsage
try
{
this.parsedValue = new ExtKeyUsage({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new ExtKeyUsage();
this.parsedValue.parsingError = "Incorrectly formated ExtKeyUsage";
}
break;
case "2.5.29.54": // InhibitAnyPolicy
this.parsedValue = asn1.result; // Should be just a simple INTEGER
break;
case "1.3.6.1.5.5.7.1.1": // AuthorityInfoAccess
case "1.3.6.1.5.5.7.1.11": // SubjectInfoAccess
try
{
this.parsedValue = new InfoAccess({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new InfoAccess();
this.parsedValue.parsingError = "Incorrectly formated InfoAccess";
}
break;
case "1.3.6.1.4.1.11129.2.4.2": // SignedCertificateTimestampList
try
{
this.parsedValue = new SignedCertificateTimestampList({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new SignedCertificateTimestampList();
this.parsedValue.parsingError = "Incorrectly formated SignedCertificateTimestampList";
}
break;
case "1.3.6.1.4.1.311.20.2": // szOID_ENROLL_CERTTYPE_EXTENSION - Microsoft-specific extension
this.parsedValue = asn1.result; // Used to be simple Unicode string
break;
case "1.3.6.1.4.1.311.21.2": // szOID_CERTSRV_PREVIOUS_CERT_HASH - Microsoft-specific extension
this.parsedValue = asn1.result; // Used to be simple OctetString
break;
case "1.3.6.1.4.1.311.21.7": // szOID_CERTIFICATE_TEMPLATE - Microsoft-specific extension
try
{
this.parsedValue = new CertificateTemplate({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new CertificateTemplate();
this.parsedValue.parsingError = "Incorrectly formated CertificateTemplate";
}
break;
case "1.3.6.1.4.1.311.21.1": // szOID_CERTSRV_CA_VERSION - Microsoft-specific extension
try
{
this.parsedValue = new CAVersion({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new CAVersion();
this.parsedValue.parsingError = "Incorrectly formated CAVersion";
}
break;
case "1.3.6.1.5.5.7.1.3": // QCStatements
try
{
this.parsedValue = new QCStatements({ schema: asn1.result });
}
catch(ex)
{
this.parsedValue = new QCStatements();
this.parsedValue.parsingError = "Incorrectly formated QCStatements";
}
break;
default:
}
//endregion
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(new asn1js.ObjectIdentifier({ value: this.extnID }));
if(this.critical !== Extension.defaultValues("critical"))
outputArray.push(new asn1js.Boolean({ value: this.critical }));
outputArray.push(this.extnValue);
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
extnID: this.extnID,
extnValue: this.extnValue.toJSON()
};
if(this.critical !== Extension.defaultValues("critical"))
object.critical = this.critical;
if("parsedValue" in this)
{
if("toJSON" in this.parsedValue)
object.parsedValue = this.parsedValue.toJSON();
}
return object;
}
//**********************************************************************************
}
//**************************************************************************************

137
core/third-party/pkijs/Extensions.js vendored Normal file
View File

@@ -0,0 +1,137 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import Extension from "./Extension.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class Extensions
{
//**********************************************************************************
/**
* Constructor for Extensions class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Array.<Extension>}
* @desc type
*/
this.extensions = getParametersValue(parameters, "extensions", Extensions.defaultValues("extensions"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "extensions":
return [];
default:
throw new Error(`Invalid member name for Extensions class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
* ```
*
* @param {Object} parameters Input parameters for the schema
* @param {boolean} optional Flag that current schema should be optional
* @returns {Object} asn1js schema object
*/
static schema(parameters = {}, optional = false)
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [extensions]
* @property {string} [extension]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
optional,
name: (names.blockName || ""),
value: [
new asn1js.Repeated({
name: (names.extensions || ""),
value: Extension.schema(names.extension || {})
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"extensions"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
Extensions.schema({
names: {
extensions: "extensions"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for Extensions");
//endregion
//region Get internal properties from parsed schema
this.extensions = Array.from(asn1.result.extensions, element => new Extension({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: Array.from(this.extensions, element => element.toSchema())
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
extensions: Array.from(this.extensions, element => element.toJSON())
};
}
//**********************************************************************************
}
//**************************************************************************************

644
core/third-party/pkijs/GeneralName.js vendored Normal file
View File

@@ -0,0 +1,644 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import RelativeDistinguishedNames from "./RelativeDistinguishedNames.js";
//**************************************************************************************
//region Additional asn1js schema elements existing inside GeneralName schema
//**************************************************************************************
/**
* Schema for "builtInStandardAttributes" of "ORAddress"
* @param {Object} parameters
* @property {Object} [names]
* @param {boolean} optional
* @returns {Sequence}
*/
function builtInStandardAttributes(parameters = {}, optional = false)
{
//builtInStandardAttributes ::= Sequence {
// country-name CountryName OPTIONAL,
// administration-domain-name AdministrationDomainName OPTIONAL,
// network-address [0] IMPLICIT NetworkAddress OPTIONAL,
// terminal-identifier [1] IMPLICIT TerminalIdentifier OPTIONAL,
// private-domain-name [2] PrivateDomainName OPTIONAL,
// organization-name [3] IMPLICIT OrganizationName OPTIONAL,
// numeric-user-identifier [4] IMPLICIT NumericUserIdentifier OPTIONAL,
// personal-name [5] IMPLICIT PersonalName OPTIONAL,
// organizational-unit-names [6] IMPLICIT OrganizationalUnitNames OPTIONAL }
/**
* @type {Object}
* @property {string} [country_name]
* @property {string} [administration_domain_name]
* @property {string} [network_address]
* @property {string} [terminal_identifier]
* @property {string} [private_domain_name]
* @property {string} [organization_name]
* @property {string} [numeric_user_identifier]
* @property {string} [personal_name]
* @property {string} [organizational_unit_names]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
optional,
value: [
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 2, // APPLICATION-SPECIFIC
tagNumber: 1 // [1]
},
name: (names.country_name || ""),
value: [
new asn1js.Choice({
value: [
new asn1js.NumericString(),
new asn1js.PrintableString()
]
})
]
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 2, // APPLICATION-SPECIFIC
tagNumber: 2 // [2]
},
name: (names.administration_domain_name || ""),
value: [
new asn1js.Choice({
value: [
new asn1js.NumericString(),
new asn1js.PrintableString()
]
})
]
}),
new asn1js.Primitive({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
name: (names.network_address || ""),
isHexOnly: true
}),
new asn1js.Primitive({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
name: (names.terminal_identifier || ""),
isHexOnly: true
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
},
name: (names.private_domain_name || ""),
value: [
new asn1js.Choice({
value: [
new asn1js.NumericString(),
new asn1js.PrintableString()
]
})
]
}),
new asn1js.Primitive({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 3 // [3]
},
name: (names.organization_name || ""),
isHexOnly: true
}),
new asn1js.Primitive({
optional: true,
name: (names.numeric_user_identifier || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 4 // [4]
},
isHexOnly: true
}),
new asn1js.Constructed({
optional: true,
name: (names.personal_name || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 5 // [5]
},
value: [
new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
isHexOnly: true
}),
new asn1js.Primitive({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
isHexOnly: true
}),
new asn1js.Primitive({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
},
isHexOnly: true
}),
new asn1js.Primitive({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 3 // [3]
},
isHexOnly: true
})
]
}),
new asn1js.Constructed({
optional: true,
name: (names.organizational_unit_names || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 6 // [6]
},
value: [
new asn1js.Repeated({
value: new asn1js.PrintableString()
})
]
})
]
}));
}
//**************************************************************************************
/**
* Schema for "builtInDomainDefinedAttributes" of "ORAddress"
* @param {boolean} optional
* @returns {Sequence}
*/
function builtInDomainDefinedAttributes(optional = false)
{
return (new asn1js.Sequence({
optional,
value: [
new asn1js.PrintableString(),
new asn1js.PrintableString()
]
}));
}
//**************************************************************************************
/**
* Schema for "builtInDomainDefinedAttributes" of "ORAddress"
* @param {boolean} optional
* @returns {Set}
*/
function extensionAttributes(optional = false)
{
return (new asn1js.Set({
optional,
value: [
new asn1js.Primitive({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
isHexOnly: true
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [new asn1js.Any()]
})
]
}));
}
//**************************************************************************************
//endregion
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class GeneralName
{
//**********************************************************************************
/**
* Constructor for GeneralName class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
* @property {number} [type] value type - from a tagged value (0 for "otherName", 1 for "rfc822Name" etc.)
* @property {Object} [value] asn1js object having GeneralName value (type depends on "type" value)
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc value type - from a tagged value (0 for "otherName", 1 for "rfc822Name" etc.)
*/
this.type = getParametersValue(parameters, "type", GeneralName.defaultValues("type"));
/**
* @type {Object}
* @desc asn1js object having GeneralName value (type depends on "type" value)
*/
this.value = getParametersValue(parameters, "value", GeneralName.defaultValues("value"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "type":
return 9;
case "value":
return {};
default:
throw new Error(`Invalid member name for GeneralName class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "type":
return (memberValue === GeneralName.defaultValues(memberName));
case "value":
return (Object.keys(memberValue).length === 0);
default:
throw new Error(`Invalid member name for GeneralName class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* GeneralName ::= Choice {
* otherName [0] OtherName,
* rfc822Name [1] IA5String,
* dNSName [2] IA5String,
* x400Address [3] ORAddress,
* directoryName [4] value,
* ediPartyName [5] EDIPartyName,
* uniformResourceIdentifier [6] IA5String,
* iPAddress [7] OCTET STRING,
* registeredID [8] OBJECT IDENTIFIER }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {Object} [directoryName]
* @property {Object} [builtInStandardAttributes]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Choice({
value: [
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier(),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [new asn1js.Any()]
})
]
}),
new asn1js.Primitive({
name: (names.blockName || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
}
}),
new asn1js.Primitive({
name: (names.blockName || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
}
}),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 3 // [3]
},
name: (names.blockName || ""),
value: [
builtInStandardAttributes((names.builtInStandardAttributes || {}), false),
builtInDomainDefinedAttributes(true),
extensionAttributes(true)
]
}),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 4 // [4]
},
name: (names.blockName || ""),
value: [RelativeDistinguishedNames.schema(names.directoryName || {})]
}),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 5 // [5]
},
name: (names.blockName || ""),
value: [
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Choice({
value: [
new asn1js.TeletexString(),
new asn1js.PrintableString(),
new asn1js.UniversalString(),
new asn1js.Utf8String(),
new asn1js.BmpString()
]
})
]
}),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [
new asn1js.Choice({
value: [
new asn1js.TeletexString(),
new asn1js.PrintableString(),
new asn1js.UniversalString(),
new asn1js.Utf8String(),
new asn1js.BmpString()
]
})
]
})
]
}),
new asn1js.Primitive({
name: (names.blockName || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 6 // [6]
}
}),
new asn1js.Primitive({
name: (names.blockName || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 7 // [7]
}
}),
new asn1js.Primitive({
name: (names.blockName || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 8 // [8]
}
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"blockName",
"otherName",
"rfc822Name",
"dNSName",
"x400Address",
"directoryName",
"ediPartyName",
"uniformResourceIdentifier",
"iPAddress",
"registeredID"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
GeneralName.schema({
names: {
blockName: "blockName",
otherName: "otherName",
rfc822Name: "rfc822Name",
dNSName: "dNSName",
x400Address: "x400Address",
directoryName: {
names: {
blockName: "directoryName"
}
},
ediPartyName: "ediPartyName",
uniformResourceIdentifier: "uniformResourceIdentifier",
iPAddress: "iPAddress",
registeredID: "registeredID"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for GeneralName");
//endregion
//region Get internal properties from parsed schema
this.type = asn1.result.blockName.idBlock.tagNumber;
switch(this.type)
{
case 0: // otherName
this.value = asn1.result.blockName;
break;
case 1: // rfc822Name + dNSName + uniformResourceIdentifier
case 2:
case 6:
{
const value = asn1.result.blockName;
value.idBlock.tagClass = 1; // UNIVERSAL
value.idBlock.tagNumber = 22; // IA5STRING
const valueBER = value.toBER(false);
this.value = asn1js.fromBER(valueBER).result.valueBlock.value;
}
break;
case 3: // x400Address
this.value = asn1.result.blockName;
break;
case 4: // directoryName
this.value = new RelativeDistinguishedNames({ schema: asn1.result.directoryName });
break;
case 5: // ediPartyName
this.value = asn1.result.ediPartyName;
break;
case 7: // iPAddress
this.value = new asn1js.OctetString({ valueHex: asn1.result.blockName.valueBlock.valueHex });
break;
case 8: // registeredID
{
const value = asn1.result.blockName;
value.idBlock.tagClass = 1; // UNIVERSAL
value.idBlock.tagNumber = 6; // ObjectIdentifier
const valueBER = value.toBER(false);
this.value = asn1js.fromBER(valueBER).result.valueBlock.toString(); // Getting a string representation of the ObjectIdentifier
}
break;
default:
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
switch(this.type)
{
case 0:
case 3:
case 5:
return new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: this.type
},
value: [
this.value
]
});
case 1:
case 2:
case 6:
{
const value = new asn1js.IA5String({ value: this.value });
value.idBlock.tagClass = 3;
value.idBlock.tagNumber = this.type;
return value;
}
case 4:
return new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 4
},
value: [this.value.toSchema()]
});
case 7:
{
const value = this.value;
value.idBlock.tagClass = 3;
value.idBlock.tagNumber = this.type;
return value;
}
case 8:
{
const value = new asn1js.ObjectIdentifier({ value: this.value });
value.idBlock.tagClass = 3;
value.idBlock.tagNumber = this.type;
return value;
}
default:
return GeneralName.schema();
}
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
type: this.type,
value: ""
};
if((typeof this.value) === "string")
_object.value = this.value;
else
{
try
{
_object.value = this.value.toJSON();
}
catch(ex){}
}
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

138
core/third-party/pkijs/GeneralNames.js vendored Normal file
View File

@@ -0,0 +1,138 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import GeneralName from "./GeneralName.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class GeneralNames
{
//**********************************************************************************
/**
* Constructor for GeneralNames class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Array.<GeneralName>}
* @desc Array of "general names"
*/
this.names = getParametersValue(parameters, "names", GeneralNames.defaultValues("names"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "names":
return [];
default:
throw new Error(`Invalid member name for GeneralNames class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
* ```
*
* @param {Object} parameters Input parameters for the schema
* @param {boolean} [optional=false] Flag would be element optional or not
* @returns {Object} asn1js schema object
*/
static schema(parameters = {}, optional = false)
{
/**
* @type {Object}
* @property {string} utcTimeName Name for "utcTimeName" choice
* @property {string} generalTimeName Name for "generalTimeName" choice
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
optional,
name: (names.blockName || ""),
value: [
new asn1js.Repeated({
name: (names.generalNames || ""),
value: GeneralName.schema()
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"names",
"generalNames"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
GeneralNames.schema({
names: {
blockName: "names",
generalNames: "generalNames"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for GeneralNames");
//endregion
//region Get internal properties from parsed schema
this.names = Array.from(asn1.result.generalNames, element => new GeneralName({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: Array.from(this.names, element => element.toSchema())
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
names: Array.from(this.names, element => element.toJSON())
};
}
//**********************************************************************************
}
//**************************************************************************************

257
core/third-party/pkijs/GeneralSubtree.js vendored Normal file
View File

@@ -0,0 +1,257 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import GeneralName from "./GeneralName.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class GeneralSubtree
{
//**********************************************************************************
/**
* Constructor for GeneralSubtree class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {GeneralName}
* @desc base
*/
this.base = getParametersValue(parameters, "base", GeneralSubtree.defaultValues("base"));
/**
* @type {number|Integer}
* @desc base
*/
this.minimum = getParametersValue(parameters, "minimum", GeneralSubtree.defaultValues("minimum"));
if("maximum" in parameters)
/**
* @type {number|Integer}
* @desc minimum
*/
this.maximum = getParametersValue(parameters, "maximum", GeneralSubtree.defaultValues("maximum"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "base":
return new GeneralName();
case "minimum":
return 0;
case "maximum":
return 0;
default:
throw new Error(`Invalid member name for GeneralSubtree class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* GeneralSubtree ::= SEQUENCE {
* base GeneralName,
* minimum [0] BaseDistance DEFAULT 0,
* maximum [1] BaseDistance OPTIONAL }
*
* BaseDistance ::= INTEGER (0..MAX)
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [base]
* @property {string} [minimum]
* @property {string} [maximum]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
GeneralName.schema(names.base || {}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [new asn1js.Integer({ name: (names.minimum || "") })]
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [new asn1js.Integer({ name: (names.maximum || "") })]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"base",
"minimum",
"maximum"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
GeneralSubtree.schema({
names: {
base: {
names: {
blockName: "base"
}
},
minimum: "minimum",
maximum: "maximum"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for GeneralSubtree");
//endregion
//region Get internal properties from parsed schema
this.base = new GeneralName({ schema: asn1.result.base });
if("minimum" in asn1.result)
{
if(asn1.result.minimum.valueBlock.isHexOnly)
this.minimum = asn1.result.minimum;
else
this.minimum = asn1.result.minimum.valueBlock.valueDec;
}
if("maximum" in asn1.result)
{
if(asn1.result.maximum.valueBlock.isHexOnly)
this.maximum = asn1.result.maximum;
else
this.maximum = asn1.result.maximum.valueBlock.valueDec;
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(this.base.toSchema());
if(this.minimum !== 0)
{
let valueMinimum = 0;
if(this.minimum instanceof asn1js.Integer)
valueMinimum = this.minimum;
else
valueMinimum = new asn1js.Integer({ value: this.minimum });
outputArray.push(new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [valueMinimum]
}));
}
if("maximum" in this)
{
let valueMaximum = 0;
if(this.maximum instanceof asn1js.Integer)
valueMaximum = this.maximum;
else
valueMaximum = new asn1js.Integer({ value: this.maximum });
outputArray.push(new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [valueMaximum]
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
base: this.base.toJSON()
};
if(this.minimum !== 0)
{
if((typeof this.minimum) === "number")
object.minimum = this.minimum;
else
object.minimum = this.minimum.toJSON();
}
if("maximum" in this)
{
if((typeof this.maximum) === "number")
object.maximum = this.maximum;
else
object.maximum = this.maximum.toJSON();
}
return object;
}
//**********************************************************************************
}
//**************************************************************************************

135
core/third-party/pkijs/InfoAccess.js vendored Normal file
View File

@@ -0,0 +1,135 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AccessDescription from "./AccessDescription.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class InfoAccess
{
//**********************************************************************************
/**
* Constructor for InfoAccess class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Array.<AccessDescription>}
* @desc accessDescriptions
*/
this.accessDescriptions = getParametersValue(parameters, "accessDescriptions", InfoAccess.defaultValues("accessDescriptions"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "accessDescriptions":
return [];
default:
throw new Error(`Invalid member name for InfoAccess class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* AuthorityInfoAccessSyntax ::=
* SEQUENCE SIZE (1..MAX) OF AccessDescription
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [accessDescriptions]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Repeated({
name: (names.accessDescriptions || ""),
value: AccessDescription.schema()
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"accessDescriptions"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
InfoAccess.schema({
names: {
accessDescriptions: "accessDescriptions"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for InfoAccess");
//endregion
//region Get internal properties from parsed schema
this.accessDescriptions = Array.from(asn1.result.accessDescriptions, element => new AccessDescription({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: Array.from(this.accessDescriptions, element => element.toSchema())
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
accessDescriptions: Array.from(this.accessDescriptions, element => element.toJSON())
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,155 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import RelativeDistinguishedNames from "./RelativeDistinguishedNames.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class IssuerAndSerialNumber
{
//**********************************************************************************
/**
* Constructor for IssuerAndSerialNumber class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {RelativeDistinguishedNames}
* @desc issuer
*/
this.issuer = getParametersValue(parameters, "issuer", IssuerAndSerialNumber.defaultValues("issuer"));
/**
* @type {Integer}
* @desc serialNumber
*/
this.serialNumber = getParametersValue(parameters, "serialNumber", IssuerAndSerialNumber.defaultValues("serialNumber"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "issuer":
return new RelativeDistinguishedNames();
case "serialNumber":
return new asn1js.Integer();
default:
throw new Error(`Invalid member name for IssuerAndSerialNumber class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* IssuerAndSerialNumber ::= SEQUENCE {
* issuer Name,
* serialNumber CertificateSerialNumber }
*
* CertificateSerialNumber ::= INTEGER
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [issuer]
* @property {string} [serialNumber]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
RelativeDistinguishedNames.schema(names.issuer || {}),
new asn1js.Integer({ name: (names.serialNumber || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"issuer",
"serialNumber"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
IssuerAndSerialNumber.schema({
names: {
issuer: {
names: {
blockName: "issuer"
}
},
serialNumber: "serialNumber"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for IssuerAndSerialNumber");
//endregion
//region Get internal properties from parsed schema
this.issuer = new RelativeDistinguishedNames({ schema: asn1.result.issuer });
this.serialNumber = asn1.result.serialNumber;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
this.issuer.toSchema(),
this.serialNumber
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
issuer: this.issuer.toJSON(),
serialNumber: this.serialNumber.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,449 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import GeneralName from "./GeneralName.js";
import RelativeDistinguishedNames from "./RelativeDistinguishedNames.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class IssuingDistributionPoint
{
//**********************************************************************************
/**
* Constructor for IssuingDistributionPoint class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
if("distributionPoint" in parameters)
/**
* @type {Array.<GeneralName>|RelativeDistinguishedNames}
* @desc distributionPoint
*/
this.distributionPoint = getParametersValue(parameters, "distributionPoint", IssuingDistributionPoint.defaultValues("distributionPoint"));
/**
* @type {boolean}
* @desc onlyContainsUserCerts
*/
this.onlyContainsUserCerts = getParametersValue(parameters, "onlyContainsUserCerts", IssuingDistributionPoint.defaultValues("onlyContainsUserCerts"));
/**
* @type {boolean}
* @desc onlyContainsCACerts
*/
this.onlyContainsCACerts = getParametersValue(parameters, "onlyContainsCACerts", IssuingDistributionPoint.defaultValues("onlyContainsCACerts"));
if("onlySomeReasons" in parameters)
/**
* @type {number}
* @desc onlySomeReasons
*/
this.onlySomeReasons = getParametersValue(parameters, "onlySomeReasons", IssuingDistributionPoint.defaultValues("onlySomeReasons"));
/**
* @type {boolean}
* @desc indirectCRL
*/
this.indirectCRL = getParametersValue(parameters, "indirectCRL", IssuingDistributionPoint.defaultValues("indirectCRL"));
/**
* @type {boolean}
* @desc onlyContainsAttributeCerts
*/
this.onlyContainsAttributeCerts = getParametersValue(parameters, "onlyContainsAttributeCerts", IssuingDistributionPoint.defaultValues("onlyContainsAttributeCerts"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "distributionPoint":
return [];
case "onlyContainsUserCerts":
return false;
case "onlyContainsCACerts":
return false;
case "onlySomeReasons":
return 0;
case "indirectCRL":
return false;
case "onlyContainsAttributeCerts":
return false;
default:
throw new Error(`Invalid member name for IssuingDistributionPoint class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* IssuingDistributionPoint ::= SEQUENCE {
* distributionPoint [0] DistributionPointName OPTIONAL,
* onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE,
* onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE,
* onlySomeReasons [3] ReasonFlags OPTIONAL,
* indirectCRL [4] BOOLEAN DEFAULT FALSE,
* onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
*
* ReasonFlags ::= BIT STRING {
* unused (0),
* keyCompromise (1),
* cACompromise (2),
* affiliationChanged (3),
* superseded (4),
* cessationOfOperation (5),
* certificateHold (6),
* privilegeWithdrawn (7),
* aACompromise (8) }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [distributionPoint]
* @property {string} [distributionPointNames]
* @property {string} [onlyContainsUserCerts]
* @property {string} [onlyContainsCACerts]
* @property {string} [onlySomeReasons]
* @property {string} [indirectCRL]
* @property {string} [onlyContainsAttributeCerts]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Choice({
value: [
new asn1js.Constructed({
name: (names.distributionPoint || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Repeated({
name: (names.distributionPointNames || ""),
value: GeneralName.schema()
})
]
}),
new asn1js.Constructed({
name: (names.distributionPoint || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: RelativeDistinguishedNames.schema().valueBlock.value
})
]
})
]
}),
new asn1js.Primitive({
name: (names.onlyContainsUserCerts || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
}
}), // IMPLICIT boolean value
new asn1js.Primitive({
name: (names.onlyContainsCACerts || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
}
}), // IMPLICIT boolean value
new asn1js.Primitive({
name: (names.onlySomeReasons || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 3 // [3]
}
}), // IMPLICIT bitstring value
new asn1js.Primitive({
name: (names.indirectCRL || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 4 // [4]
}
}), // IMPLICIT boolean value
new asn1js.Primitive({
name: (names.onlyContainsAttributeCerts || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 5 // [5]
}
}) // IMPLICIT boolean value
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"distributionPoint",
"distributionPointNames",
"onlyContainsUserCerts",
"onlyContainsCACerts",
"onlySomeReasons",
"indirectCRL",
"onlyContainsAttributeCerts"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
IssuingDistributionPoint.schema({
names: {
distributionPoint: "distributionPoint",
distributionPointNames: "distributionPointNames",
onlyContainsUserCerts: "onlyContainsUserCerts",
onlyContainsCACerts: "onlyContainsCACerts",
onlySomeReasons: "onlySomeReasons",
indirectCRL: "indirectCRL",
onlyContainsAttributeCerts: "onlyContainsAttributeCerts"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for IssuingDistributionPoint");
//endregion
//region Get internal properties from parsed schema
if("distributionPoint" in asn1.result)
{
switch(true)
{
case (asn1.result.distributionPoint.idBlock.tagNumber === 0): // GENERAL_NAMES variant
this.distributionPoint = Array.from(asn1.result.distributionPointNames, element => new GeneralName({ schema: element }));
break;
case (asn1.result.distributionPoint.idBlock.tagNumber === 1): // RDN variant
{
this.distributionPoint = new RelativeDistinguishedNames({
schema: new asn1js.Sequence({
value: asn1.result.distributionPoint.valueBlock.value
})
});
}
break;
default:
throw new Error("Unknown tagNumber for distributionPoint: {$asn1.result.distributionPoint.idBlock.tagNumber}");
}
}
if("onlyContainsUserCerts" in asn1.result)
{
const view = new Uint8Array(asn1.result.onlyContainsUserCerts.valueBlock.valueHex);
this.onlyContainsUserCerts = (view[0] !== 0x00);
}
if("onlyContainsCACerts" in asn1.result)
{
const view = new Uint8Array(asn1.result.onlyContainsCACerts.valueBlock.valueHex);
this.onlyContainsCACerts = (view[0] !== 0x00);
}
if("onlySomeReasons" in asn1.result)
{
const view = new Uint8Array(asn1.result.onlySomeReasons.valueBlock.valueHex);
this.onlySomeReasons = view[0];
}
if("indirectCRL" in asn1.result)
{
const view = new Uint8Array(asn1.result.indirectCRL.valueBlock.valueHex);
this.indirectCRL = (view[0] !== 0x00);
}
if("onlyContainsAttributeCerts" in asn1.result)
{
const view = new Uint8Array(asn1.result.onlyContainsAttributeCerts.valueBlock.valueHex);
this.onlyContainsAttributeCerts = (view[0] !== 0x00);
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
if("distributionPoint" in this)
{
let value;
if(this.distributionPoint instanceof Array)
{
value = new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: Array.from(this.distributionPoint, element => element.toSchema())
});
}
else
{
value = this.distributionPoint.toSchema();
value.idBlock.tagClass = 3; // CONTEXT - SPECIFIC
value.idBlock.tagNumber = 1; // [1]
}
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [value]
}));
}
if(this.onlyContainsUserCerts !== IssuingDistributionPoint.defaultValues("onlyContainsUserCerts"))
{
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
valueHex: (new Uint8Array([0xFF])).buffer
}));
}
if(this.onlyContainsCACerts !== IssuingDistributionPoint.defaultValues("onlyContainsCACerts"))
{
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 2 // [2]
},
valueHex: (new Uint8Array([0xFF])).buffer
}));
}
if("onlySomeReasons" in this)
{
const buffer = new ArrayBuffer(1);
const view = new Uint8Array(buffer);
view[0] = this.onlySomeReasons;
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 3 // [3]
},
valueHex: buffer
}));
}
if(this.indirectCRL !== IssuingDistributionPoint.defaultValues("indirectCRL"))
{
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 4 // [4]
},
valueHex: (new Uint8Array([0xFF])).buffer
}));
}
if(this.onlyContainsAttributeCerts !== IssuingDistributionPoint.defaultValues("onlyContainsAttributeCerts"))
{
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 5 // [5]
},
valueHex: (new Uint8Array([0xFF])).buffer
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {};
if("distributionPoint" in this)
{
if(this.distributionPoint instanceof Array)
object.distributionPoint = Array.from(this.distributionPoint, element => element.toJSON());
else
object.distributionPoint = this.distributionPoint.toJSON();
}
if(this.onlyContainsUserCerts !== IssuingDistributionPoint.defaultValues("onlyContainsUserCerts"))
object.onlyContainsUserCerts = this.onlyContainsUserCerts;
if(this.onlyContainsCACerts !== IssuingDistributionPoint.defaultValues("onlyContainsCACerts"))
object.onlyContainsCACerts = this.onlyContainsCACerts;
if("onlySomeReasons" in this)
object.onlySomeReasons = this.onlySomeReasons;
if(this.indirectCRL !== IssuingDistributionPoint.defaultValues("indirectCRL"))
object.indirectCRL = this.indirectCRL;
if(this.onlyContainsAttributeCerts !== IssuingDistributionPoint.defaultValues("onlyContainsAttributeCerts"))
object.onlyContainsAttributeCerts = this.onlyContainsAttributeCerts;
return object;
}
//**********************************************************************************
}
//**************************************************************************************

220
core/third-party/pkijs/KEKIdentifier.js vendored Normal file
View File

@@ -0,0 +1,220 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import OtherKeyAttribute from "./OtherKeyAttribute.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class KEKIdentifier
{
//**********************************************************************************
/**
* Constructor for KEKIdentifier class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {OctetString}
* @desc keyIdentifier
*/
this.keyIdentifier = getParametersValue(parameters, "keyIdentifier", KEKIdentifier.defaultValues("keyIdentifier"));
if("date" in parameters)
/**
* @type {GeneralizedTime}
* @desc date
*/
this.date = getParametersValue(parameters, "date", KEKIdentifier.defaultValues("date"));
if("other" in parameters)
/**
* @type {OtherKeyAttribute}
* @desc other
*/
this.other = getParametersValue(parameters, "other", KEKIdentifier.defaultValues("other"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "keyIdentifier":
return new asn1js.OctetString();
case "date":
return new asn1js.GeneralizedTime();
case "other":
return new OtherKeyAttribute();
default:
throw new Error(`Invalid member name for KEKIdentifier class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "keyIdentifier":
return (memberValue.isEqual(KEKIdentifier.defaultValues("keyIdentifier")));
case "date":
// noinspection OverlyComplexBooleanExpressionJS
return ((memberValue.year === 0) &&
(memberValue.month === 0) &&
(memberValue.day === 0) &&
(memberValue.hour === 0) &&
(memberValue.minute === 0) &&
(memberValue.second === 0) &&
(memberValue.millisecond === 0));
case "other":
return ((memberValue.compareWithDefault("keyAttrId", memberValue.keyAttrId)) &&
(("keyAttr" in memberValue) === false));
default:
throw new Error(`Invalid member name for KEKIdentifier class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* KEKIdentifier ::= SEQUENCE {
* keyIdentifier OCTET STRING,
* date GeneralizedTime OPTIONAL,
* other OtherKeyAttribute OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [keyIdentifier]
* @property {string} [date]
* @property {string} [other]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.OctetString({ name: (names.keyIdentifier || "") }),
new asn1js.GeneralizedTime({
optional: true,
name: (names.date || "")
}),
OtherKeyAttribute.schema(names.other || {})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"keyIdentifier",
"date",
"other"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
KEKIdentifier.schema({
names: {
keyIdentifier: "keyIdentifier",
date: "date",
other: {
names: {
blockName: "other"
}
}
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for KEKIdentifier");
//endregion
//region Get internal properties from parsed schema
this.keyIdentifier = asn1.result.keyIdentifier;
if("date" in asn1.result)
this.date = asn1.result.date;
if("other" in asn1.result)
this.other = new OtherKeyAttribute({ schema: asn1.result.other });
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(this.keyIdentifier);
if("date" in this)
outputArray.push(this.date);
if("other" in this)
outputArray.push(this.other.toSchema());
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
keyIdentifier: this.keyIdentifier.toJSON()
};
if("date" in this)
_object.date = this.date;
if("other" in this)
_object.other = this.other.toJSON();
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,221 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import KEKIdentifier from "./KEKIdentifier.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class KEKRecipientInfo
{
//**********************************************************************************
/**
* Constructor for KEKRecipientInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", KEKRecipientInfo.defaultValues("version"));
/**
* @type {KEKIdentifier}
* @desc kekid
*/
this.kekid = getParametersValue(parameters, "kekid", KEKRecipientInfo.defaultValues("kekid"));
/**
* @type {AlgorithmIdentifier}
* @desc keyEncryptionAlgorithm
*/
this.keyEncryptionAlgorithm = getParametersValue(parameters, "keyEncryptionAlgorithm", KEKRecipientInfo.defaultValues("keyEncryptionAlgorithm"));
/**
* @type {OctetString}
* @desc encryptedKey
*/
this.encryptedKey = getParametersValue(parameters, "encryptedKey", KEKRecipientInfo.defaultValues("encryptedKey"));
/**
* @type {ArrayBuffer}
* @desc preDefinedKEK KEK using to encrypt CEK
*/
this.preDefinedKEK = getParametersValue(parameters, "preDefinedKEK", KEKRecipientInfo.defaultValues("preDefinedKEK"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "version":
return 0;
case "kekid":
return new KEKIdentifier();
case "keyEncryptionAlgorithm":
return new AlgorithmIdentifier();
case "encryptedKey":
return new asn1js.OctetString();
case "preDefinedKEK":
return new ArrayBuffer(0);
default:
throw new Error(`Invalid member name for KEKRecipientInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "KEKRecipientInfo":
return (memberValue === KEKRecipientInfo.defaultValues("version"));
case "kekid":
return ((memberValue.compareWithDefault("keyIdentifier", memberValue.keyIdentifier)) &&
(("date" in memberValue) === false) &&
(("other" in memberValue) === false));
case "keyEncryptionAlgorithm":
return ((memberValue.algorithmId === "") && (("algorithmParams" in memberValue) === false));
case "encryptedKey":
return (memberValue.isEqual(KEKRecipientInfo.defaultValues("encryptedKey")));
case "preDefinedKEK":
return (memberValue.byteLength === 0);
default:
throw new Error(`Invalid member name for KEKRecipientInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* KEKRecipientInfo ::= SEQUENCE {
* version CMSVersion, -- always set to 4
* kekid KEKIdentifier,
* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
* encryptedKey EncryptedKey }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [version]
* @property {string} [kekid]
* @property {string} [keyEncryptionAlgorithm]
* @property {string} [encryptedKey]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.version || "") }),
KEKIdentifier.schema(names.kekid || {}),
AlgorithmIdentifier.schema(names.keyEncryptionAlgorithm || {}),
new asn1js.OctetString({ name: (names.encryptedKey || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"version",
"kekid",
"keyEncryptionAlgorithm",
"encryptedKey"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
KEKRecipientInfo.schema({
names: {
version: "version",
kekid: {
names: {
blockName: "kekid"
}
},
keyEncryptionAlgorithm: {
names: {
blockName: "keyEncryptionAlgorithm"
}
},
encryptedKey: "encryptedKey"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for KEKRecipientInfo");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result.version.valueBlock.valueDec;
this.kekid = new KEKIdentifier({ schema: asn1.result.kekid });
this.keyEncryptionAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.keyEncryptionAlgorithm });
this.encryptedKey = asn1.result.encryptedKey;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.Integer({ value: this.version }),
this.kekid.toSchema(),
this.keyEncryptionAlgorithm.toSchema(),
this.encryptedKey
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
version: this.version,
kekid: this.kekid.toJSON(),
keyEncryptionAlgorithm: this.keyEncryptionAlgorithm.toJSON(),
encryptedKey: this.encryptedKey.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,206 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import IssuerAndSerialNumber from "./IssuerAndSerialNumber.js";
import RecipientKeyIdentifier from "./RecipientKeyIdentifier.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class KeyAgreeRecipientIdentifier
{
//**********************************************************************************
/**
* Constructor for KeyAgreeRecipientIdentifier class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc variant
*/
this.variant = getParametersValue(parameters, "variant", KeyAgreeRecipientIdentifier.defaultValues("variant"));
/**
* @type {*}
* @desc values
*/
this.value = getParametersValue(parameters, "value", KeyAgreeRecipientIdentifier.defaultValues("value"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "variant":
return (-1);
case "value":
return {};
default:
throw new Error(`Invalid member name for KeyAgreeRecipientIdentifier class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "variant":
return (memberValue === (-1));
case "value":
return (Object.keys(memberValue).length === 0);
default:
throw new Error(`Invalid member name for KeyAgreeRecipientIdentifier class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* KeyAgreeRecipientIdentifier ::= CHOICE {
* issuerAndSerialNumber IssuerAndSerialNumber,
* rKeyId [0] IMPLICIT RecipientKeyIdentifier }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [issuerAndSerialNumber]
* @property {string} [rKeyId]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Choice({
value: [
IssuerAndSerialNumber.schema(names.issuerAndSerialNumber || {
names: {
blockName: (names.blockName || "")
}
}),
new asn1js.Constructed({
name: (names.blockName || ""),
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: RecipientKeyIdentifier.schema(names.rKeyId || {
names: {
blockName: (names.blockName || "")
}
}).valueBlock.value
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"blockName"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
KeyAgreeRecipientIdentifier.schema({
names: {
blockName: "blockName"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for KeyAgreeRecipientIdentifier");
//endregion
//region Get internal properties from parsed schema
if(asn1.result.blockName.idBlock.tagClass === 1)
{
this.variant = 1;
this.value = new IssuerAndSerialNumber({ schema: asn1.result.blockName });
}
else
{
this.variant = 2;
this.value = new RecipientKeyIdentifier({
schema: new asn1js.Sequence({
value: asn1.result.blockName.valueBlock.value
})
});
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
switch(this.variant)
{
case 1:
return this.value.toSchema();
case 2:
return new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: this.value.toSchema().valueBlock.value
});
default:
return new asn1js.Any();
}
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
variant: this.variant
};
if((this.variant === 1) || (this.variant === 2))
_object.value = this.value.toJSON();
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,290 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import OriginatorIdentifierOrKey from "./OriginatorIdentifierOrKey.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import RecipientEncryptedKeys from "./RecipientEncryptedKeys.js";
import Certificate from "./Certificate.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class KeyAgreeRecipientInfo
{
//**********************************************************************************
/**
* Constructor for KeyAgreeRecipientInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", KeyAgreeRecipientInfo.defaultValues("version"));
/**
* @type {OriginatorIdentifierOrKey}
* @desc originator
*/
this.originator = getParametersValue(parameters, "originator", KeyAgreeRecipientInfo.defaultValues("originator"));
if("ukm" in parameters)
/**
* @type {OctetString}
* @desc ukm
*/
this.ukm = getParametersValue(parameters, "ukm", KeyAgreeRecipientInfo.defaultValues("ukm"));
/**
* @type {AlgorithmIdentifier}
* @desc keyEncryptionAlgorithm
*/
this.keyEncryptionAlgorithm = getParametersValue(parameters, "keyEncryptionAlgorithm", KeyAgreeRecipientInfo.defaultValues("keyEncryptionAlgorithm"));
/**
* @type {RecipientEncryptedKeys}
* @desc recipientEncryptedKeys
*/
this.recipientEncryptedKeys = getParametersValue(parameters, "recipientEncryptedKeys", KeyAgreeRecipientInfo.defaultValues("recipientEncryptedKeys"));
/**
* @type {Certificate}
* @desc recipientCertificate For some reasons we need to store recipient's certificate here
*/
this.recipientCertificate = getParametersValue(parameters, "recipientCertificate", KeyAgreeRecipientInfo.defaultValues("recipientCertificate"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "version":
return 0;
case "originator":
return new OriginatorIdentifierOrKey();
case "ukm":
return new asn1js.OctetString();
case "keyEncryptionAlgorithm":
return new AlgorithmIdentifier();
case "recipientEncryptedKeys":
return new RecipientEncryptedKeys();
case "recipientCertificate":
return new Certificate();
default:
throw new Error(`Invalid member name for KeyAgreeRecipientInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "version":
return (memberValue === 0);
case "originator":
return ((memberValue.variant === (-1)) && (("value" in memberValue) === false));
case "ukm":
return (memberValue.isEqual(KeyAgreeRecipientInfo.defaultValues("ukm")));
case "keyEncryptionAlgorithm":
return ((memberValue.algorithmId === "") && (("algorithmParams" in memberValue) === false));
case "recipientEncryptedKeys":
return (memberValue.encryptedKeys.length === 0);
case "recipientCertificate":
return false; // For now leave it as is
default:
throw new Error(`Invalid member name for KeyAgreeRecipientInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* KeyAgreeRecipientInfo ::= SEQUENCE {
* version CMSVersion, -- always set to 3
* originator [0] EXPLICIT OriginatorIdentifierOrKey,
* ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL,
* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
* recipientEncryptedKeys RecipientEncryptedKeys }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [version]
* @property {string} [originator]
* @property {string} [ukm]
* @property {string} [keyEncryptionAlgorithm]
* @property {string} [recipientEncryptedKeys]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: names.blockName || "",
value: [
new asn1js.Integer({ name: names.version || "" }),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
OriginatorIdentifierOrKey.schema(names.originator || {})
]
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [new asn1js.OctetString({ name: names.ukm || "" })]
}),
AlgorithmIdentifier.schema(names.keyEncryptionAlgorithm || {}),
RecipientEncryptedKeys.schema(names.recipientEncryptedKeys || {})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"version",
"originator",
"ukm",
"keyEncryptionAlgorithm",
"recipientEncryptedKeys"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
KeyAgreeRecipientInfo.schema({
names: {
version: "version",
originator: {
names: {
blockName: "originator"
}
},
ukm: "ukm",
keyEncryptionAlgorithm: {
names: {
blockName: "keyEncryptionAlgorithm"
}
},
recipientEncryptedKeys: {
names: {
blockName: "recipientEncryptedKeys"
}
}
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for KeyAgreeRecipientInfo");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result.version.valueBlock.valueDec;
this.originator = new OriginatorIdentifierOrKey({ schema: asn1.result.originator });
if("ukm" in asn1.result)
this.ukm = asn1.result.ukm;
this.keyEncryptionAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.keyEncryptionAlgorithm });
this.recipientEncryptedKeys = new RecipientEncryptedKeys({ schema: asn1.result.recipientEncryptedKeys });
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for final sequence
const outputArray = [];
outputArray.push(new asn1js.Integer({ value: this.version }));
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [this.originator.toSchema()]
}));
if("ukm" in this)
{
outputArray.push(new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [this.ukm]
}));
}
outputArray.push(this.keyEncryptionAlgorithm.toSchema());
outputArray.push(this.recipientEncryptedKeys.toSchema());
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
version: this.version,
originator: this.originator.toJSON()
};
if("ukm" in this)
_object.ukm = this.ukm.toJSON();
_object.keyEncryptionAlgorithm = this.keyEncryptionAlgorithm.toJSON();
_object.recipientEncryptedKeys = this.recipientEncryptedKeys.toJSON();
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

20
core/third-party/pkijs/KeyBag.js vendored Normal file
View File

@@ -0,0 +1,20 @@
import PrivateKeyInfo from "./PrivateKeyInfo.js";
//**************************************************************************************
/**
* Class from RFC5208
*/
export default class KeyBag extends PrivateKeyInfo
{
//**********************************************************************************
/**
* Constructor for Attribute class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
super(parameters);
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,248 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import Certificate from "./Certificate.js";
import RecipientIdentifier from "./RecipientIdentifier.js";
import IssuerAndSerialNumber from "./IssuerAndSerialNumber.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class KeyTransRecipientInfo
{
//**********************************************************************************
/**
* Constructor for KeyTransRecipientInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", KeyTransRecipientInfo.defaultValues("version"));
/**
* @type {RecipientIdentifier}
* @desc rid
*/
this.rid = getParametersValue(parameters, "rid", KeyTransRecipientInfo.defaultValues("rid"));
/**
* @type {AlgorithmIdentifier}
* @desc keyEncryptionAlgorithm
*/
this.keyEncryptionAlgorithm = getParametersValue(parameters, "keyEncryptionAlgorithm", KeyTransRecipientInfo.defaultValues("keyEncryptionAlgorithm"));
/**
* @type {OctetString}
* @desc encryptedKey
*/
this.encryptedKey = getParametersValue(parameters, "encryptedKey", KeyTransRecipientInfo.defaultValues("encryptedKey"));
/**
* @type {Certificate}
* @desc recipientCertificate For some reasons we need to store recipient's certificate here
*/
this.recipientCertificate = getParametersValue(parameters, "recipientCertificate", KeyTransRecipientInfo.defaultValues("recipientCertificate"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "version":
return (-1);
case "rid":
return {};
case "keyEncryptionAlgorithm":
return new AlgorithmIdentifier();
case "encryptedKey":
return new asn1js.OctetString();
case "recipientCertificate":
return new Certificate();
default:
throw new Error(`Invalid member name for KeyTransRecipientInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "version":
return (memberValue === KeyTransRecipientInfo.defaultValues("version"));
case "rid":
return (Object.keys(memberValue).length === 0);
case "keyEncryptionAlgorithm":
case "encryptedKey":
return memberValue.isEqual(KeyTransRecipientInfo.defaultValues(memberName));
case "recipientCertificate":
return false; // For now we do not need to compare any values with the "recipientCertificate"
default:
throw new Error(`Invalid member name for KeyTransRecipientInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* KeyTransRecipientInfo ::= SEQUENCE {
* version CMSVersion, -- always set to 0 or 2
* rid RecipientIdentifier,
* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
* encryptedKey EncryptedKey }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [version]
* @property {string} [rid]
* @property {string} [keyEncryptionAlgorithm]
* @property {string} [encryptedKey]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.version || "") }),
RecipientIdentifier.schema(names.rid || {}),
AlgorithmIdentifier.schema(names.keyEncryptionAlgorithm || {}),
new asn1js.OctetString({ name: (names.encryptedKey || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"version",
"rid",
"keyEncryptionAlgorithm",
"encryptedKey"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
KeyTransRecipientInfo.schema({
names: {
version: "version",
rid: {
names: {
blockName: "rid"
}
},
keyEncryptionAlgorithm: {
names: {
blockName: "keyEncryptionAlgorithm"
}
},
encryptedKey: "encryptedKey"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for KeyTransRecipientInfo");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result.version.valueBlock.valueDec;
if(asn1.result.rid.idBlock.tagClass === 3)
this.rid = new asn1js.OctetString({ valueHex: asn1.result.rid.valueBlock.valueHex }); // SubjectKeyIdentifier
else
this.rid = new IssuerAndSerialNumber({ schema: asn1.result.rid });
this.keyEncryptionAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.keyEncryptionAlgorithm });
this.encryptedKey = asn1.result.encryptedKey;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
if(this.rid instanceof IssuerAndSerialNumber)
{
this.version = 0;
outputArray.push(new asn1js.Integer({ value: this.version }));
outputArray.push(this.rid.toSchema());
}
else
{
this.version = 2;
outputArray.push(new asn1js.Integer({ value: this.version }));
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
valueHex: this.rid.valueBlock.valueHex
}));
}
outputArray.push(this.keyEncryptionAlgorithm.toSchema());
outputArray.push(this.encryptedKey);
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
version: this.version,
rid: this.rid.toJSON(),
keyEncryptionAlgorithm: this.keyEncryptionAlgorithm.toJSON(),
encryptedKey: this.encryptedKey.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

214
core/third-party/pkijs/MacData.js vendored Normal file
View File

@@ -0,0 +1,214 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import DigestInfo from "./DigestInfo.js";
//**************************************************************************************
/**
* Class from RFC7292
*/
export default class MacData
{
//**********************************************************************************
/**
* Constructor for MacData class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {DigestInfo}
* @desc mac
*/
this.mac = getParametersValue(parameters, "mac", MacData.defaultValues("mac"));
/**
* @type {OctetString}
* @desc macSalt
*/
this.macSalt = getParametersValue(parameters, "macSalt", MacData.defaultValues("macSalt"));
if("iterations" in parameters)
/**
* @type {number}
* @desc iterations
*/
this.iterations = getParametersValue(parameters, "iterations", MacData.defaultValues("iterations"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "mac":
return new DigestInfo();
case "macSalt":
return new asn1js.OctetString();
case "iterations":
return 1;
default:
throw new Error(`Invalid member name for MacData class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "mac":
return ((DigestInfo.compareWithDefault("digestAlgorithm", memberValue.digestAlgorithm)) &&
(DigestInfo.compareWithDefault("digest", memberValue.digest)));
case "macSalt":
return (memberValue.isEqual(MacData.defaultValues(memberName)));
case "iterations":
return (memberValue === MacData.defaultValues(memberName));
default:
throw new Error(`Invalid member name for MacData class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* MacData ::= SEQUENCE {
* mac DigestInfo,
* macSalt OCTET STRING,
* iterations INTEGER DEFAULT 1
* -- Note: The default is for historical reasons and its use is
* -- deprecated. A higher value, like 1024 is recommended.
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [optional]
* @property {string} [mac]
* @property {string} [macSalt]
* @property {string} [iterations]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
optional: (names.optional || true),
value: [
DigestInfo.schema(names.mac || {
names: {
blockName: "mac"
}
}),
new asn1js.OctetString({ name: (names.macSalt || "macSalt") }),
new asn1js.Integer({
optional: true,
name: (names.iterations || "iterations")
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"mac",
"macSalt",
"iterations"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
MacData.schema({
names: {
mac: {
names: {
blockName: "mac"
}
},
macSalt: "macSalt",
iterations: "iterations"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for MacData");
//endregion
//region Get internal properties from parsed schema
this.mac = new DigestInfo({ schema: asn1.result.mac });
this.macSalt = asn1.result.macSalt;
if("iterations" in asn1.result)
this.iterations = asn1.result.iterations.valueBlock.valueDec;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
const outputArray = [
this.mac.toSchema(),
this.macSalt
];
if("iterations" in this)
outputArray.push(new asn1js.Integer({ value: this.iterations }));
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const output = {
mac: this.mac.toJSON(),
macSalt: this.macSalt.toJSON()
};
if("iterations" in this)
output.iterations = this.iterations.toJSON();
return output;
}
//**********************************************************************************
}
//**************************************************************************************

171
core/third-party/pkijs/MessageImprint.js vendored Normal file
View File

@@ -0,0 +1,171 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
//**************************************************************************************
/**
* Class from RFC3161
*/
export default class MessageImprint
{
//**********************************************************************************
/**
* Constructor for MessageImprint class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {AlgorithmIdentifier}
* @desc hashAlgorithm
*/
this.hashAlgorithm = getParametersValue(parameters, "hashAlgorithm", MessageImprint.defaultValues("hashAlgorithm"));
/**
* @type {OctetString}
* @desc hashedMessage
*/
this.hashedMessage = getParametersValue(parameters, "hashedMessage", MessageImprint.defaultValues("hashedMessage"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "hashAlgorithm":
return new AlgorithmIdentifier();
case "hashedMessage":
return new asn1js.OctetString();
default:
throw new Error(`Invalid member name for MessageImprint class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "hashAlgorithm":
return ((memberValue.algorithmId === "") && (("algorithmParams" in memberValue) === false));
case "hashedMessage":
return (memberValue.isEqual(MessageImprint.defaultValues(memberName)) === 0);
default:
throw new Error(`Invalid member name for MessageImprint class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* MessageImprint ::= SEQUENCE {
* hashAlgorithm AlgorithmIdentifier,
* hashedMessage OCTET STRING }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [hashAlgorithm]
* @property {string} [hashedMessage]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
AlgorithmIdentifier.schema(names.hashAlgorithm || {}),
new asn1js.OctetString({ name: (names.hashedMessage || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"hashAlgorithm",
"hashedMessage"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
MessageImprint.schema({
names: {
hashAlgorithm: {
names: {
blockName: "hashAlgorithm"
}
},
hashedMessage: "hashedMessage"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for MessageImprint");
//endregion
//region Get internal properties from parsed schema
this.hashAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.hashAlgorithm });
this.hashedMessage = asn1.result.hashedMessage;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
this.hashAlgorithm.toSchema(),
this.hashedMessage
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
hashAlgorithm: this.hashAlgorithm.toJSON(),
hashedMessage: this.hashedMessage.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,207 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import GeneralSubtree from "./GeneralSubtree.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class NameConstraints
{
//**********************************************************************************
/**
* Constructor for NameConstraints class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
if("permittedSubtrees" in parameters)
/**
* @type {Array.<GeneralSubtree>}
* @desc permittedSubtrees
*/
this.permittedSubtrees = getParametersValue(parameters, "permittedSubtrees", NameConstraints.defaultValues("permittedSubtrees"));
if("excludedSubtrees" in parameters)
/**
* @type {Array.<GeneralSubtree>}
* @desc excludedSubtrees
*/
this.excludedSubtrees = getParametersValue(parameters, "excludedSubtrees", NameConstraints.defaultValues("excludedSubtrees"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "permittedSubtrees":
return [];
case "excludedSubtrees":
return [];
default:
throw new Error(`Invalid member name for NameConstraints class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* NameConstraints ::= SEQUENCE {
* permittedSubtrees [0] GeneralSubtrees OPTIONAL,
* excludedSubtrees [1] GeneralSubtrees OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [permittedSubtrees]
* @property {string} [excludedSubtrees]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Repeated({
name: (names.permittedSubtrees || ""),
value: GeneralSubtree.schema()
})
]
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: [
new asn1js.Repeated({
name: (names.excludedSubtrees || ""),
value: GeneralSubtree.schema()
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"permittedSubtrees",
"excludedSubtrees"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
NameConstraints.schema({
names: {
permittedSubtrees: "permittedSubtrees",
excludedSubtrees: "excludedSubtrees"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for NameConstraints");
//endregion
//region Get internal properties from parsed schema
if("permittedSubtrees" in asn1.result)
this.permittedSubtrees = Array.from(asn1.result.permittedSubtrees, element => new GeneralSubtree({ schema: element }));
if("excludedSubtrees" in asn1.result)
this.excludedSubtrees = Array.from(asn1.result.excludedSubtrees, element => new GeneralSubtree({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
if("permittedSubtrees" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: Array.from(this.permittedSubtrees, element => element.toSchema())
}));
}
if("excludedSubtrees" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: Array.from(this.excludedSubtrees, element => element.toSchema())
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {};
if("permittedSubtrees" in this)
object.permittedSubtrees = Array.from(this.permittedSubtrees, element => element.toJSON());
if("excludedSubtrees" in this)
object.excludedSubtrees = Array.from(this.excludedSubtrees, element => element.toJSON());
return object;
}
//**********************************************************************************
}
//**************************************************************************************

304
core/third-party/pkijs/OCSPRequest.js vendored Normal file
View File

@@ -0,0 +1,304 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import { getEngine } from "./common.js";
import TBSRequest from "./TBSRequest.js";
import Signature from "./Signature.js";
import Request from "./Request.js";
import CertID from "./CertID.js";
//**************************************************************************************
/**
* Class from RFC6960
*/
export default class OCSPRequest
{
//**********************************************************************************
/**
* Constructor for OCSPRequest class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {TBSRequest}
* @desc tbsRequest
*/
this.tbsRequest = getParametersValue(parameters, "tbsRequest", OCSPRequest.defaultValues("tbsRequest"));
if("optionalSignature" in parameters)
/**
* @type {Signature}
* @desc optionalSignature
*/
this.optionalSignature = getParametersValue(parameters, "optionalSignature", OCSPRequest.defaultValues("optionalSignature"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "tbsRequest":
return new TBSRequest();
case "optionalSignature":
return new Signature();
default:
throw new Error(`Invalid member name for OCSPRequest class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "tbsRequest":
// noinspection OverlyComplexBooleanExpressionJS
return ((TBSRequest.compareWithDefault("tbs", memberValue.tbs)) &&
(TBSRequest.compareWithDefault("version", memberValue.version)) &&
(TBSRequest.compareWithDefault("requestorName", memberValue.requestorName)) &&
(TBSRequest.compareWithDefault("requestList", memberValue.requestList)) &&
(TBSRequest.compareWithDefault("requestExtensions", memberValue.requestExtensions)));
case "optionalSignature":
return ((Signature.compareWithDefault("signatureAlgorithm", memberValue.signatureAlgorithm)) &&
(Signature.compareWithDefault("signature", memberValue.signature)) &&
(Signature.compareWithDefault("certs", memberValue.certs)));
default:
throw new Error(`Invalid member name for OCSPRequest class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* OCSPRequest ::= SEQUENCE {
* tbsRequest TBSRequest,
* optionalSignature [0] EXPLICIT Signature OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [tbsRequest]
* @property {string} [optionalSignature]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: names.blockName || "OCSPRequest",
value: [
TBSRequest.schema(names.tbsRequest || {
names: {
blockName: "tbsRequest"
}
}),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
Signature.schema(names.optionalSignature || {
names: {
blockName: "optionalSignature"
}
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"tbsRequest",
"optionalSignature"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
OCSPRequest.schema()
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for OCSPRequest");
//endregion
//region Get internal properties from parsed schema
this.tbsRequest = new TBSRequest({ schema: asn1.result.tbsRequest });
if("optionalSignature" in asn1.result)
this.optionalSignature = new Signature({ schema: asn1.result.optionalSignature });
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @param {boolean} encodeFlag If param equal to false then create TBS schema via decoding stored value. In othe case create TBS schema via assembling from TBS parts.
* @returns {Object} asn1js object
*/
toSchema(encodeFlag = false)
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(this.tbsRequest.toSchema(encodeFlag));
if("optionalSignature" in this)
outputArray.push(this.optionalSignature.toSchema());
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
tbsRequest: this.tbsRequest.toJSON()
};
if("optionalSignature" in this)
_object.optionalSignature = this.optionalSignature.toJSON();
return _object;
}
//**********************************************************************************
/**
* Making OCSP Request for specific certificate
* @param {Certificate} certificate Certificate making OCSP Request for
* @param {Object} parameters Additional parameters
* @returns {Promise}
*/
createForCertificate(certificate, parameters)
{
//region Initial variables
let sequence = Promise.resolve();
const certID = new CertID();
//endregion
//region Create OCSP certificate identifier for the certificate
sequence = sequence.then(() =>
certID.createForCertificate(certificate, parameters)
);
//endregion
//region Make final request data
sequence = sequence.then(() =>
{
this.tbsRequest = new TBSRequest({
requestList: [
new Request({
reqCert: certID
})
]
});
}, error =>
Promise.reject(error)
);
//endregion
return sequence;
}
//**********************************************************************************
/**
* Make signature for current OCSP Request
* @param {Object} privateKey Private key for "subjectPublicKeyInfo" structure
* @param {string} [hashAlgorithm] Hashing algorithm. Default SHA-1
* @returns {Promise}
*/
sign(privateKey, hashAlgorithm = "SHA-1")
{
//region Initial checking
//region Check private key
if(typeof privateKey === "undefined")
return Promise.reject("Need to provide a private key for signing");
//endregion
//region Check that "optionalSignature" exists in the current request
if(("optionalSignature" in this) === false)
return Promise.reject("Need to create \"optionalSignature\" field before signing");
//endregion
//endregion
//region Initial variables
let sequence = Promise.resolve();
let parameters;
let tbs;
const engine = getEngine();
//endregion
//region Get a "default parameters" for current algorithm and set correct signature algorithm
sequence = sequence.then(() => engine.subtle.getSignatureParameters(privateKey, hashAlgorithm));
sequence = sequence.then(result =>
{
parameters = result.parameters;
this.optionalSignature.signatureAlgorithm = result.signatureAlgorithm;
});
//endregion
//region Create TBS data for signing
sequence = sequence.then(() =>
{
tbs = this.tbsRequest.toSchema(true).toBER(false);
});
//endregion
//region Signing TBS data on provided private key
sequence = sequence.then(() => engine.subtle.signWithPrivateKey(tbs, privateKey, parameters));
sequence = sequence.then(result =>
{
this.optionalSignature.signature = new asn1js.BitString({ valueHex: result });
});
//endregion
return sequence;
}
//**********************************************************************************
verify()
{
// TODO: Create the function
}
//**********************************************************************************
}
//**************************************************************************************

299
core/third-party/pkijs/OCSPResponse.js vendored Normal file
View File

@@ -0,0 +1,299 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import ResponseBytes from "./ResponseBytes.js";
import BasicOCSPResponse from "./BasicOCSPResponse.js";
//**************************************************************************************
/**
* Class from RFC6960
*/
export default class OCSPResponse
{
//**********************************************************************************
/**
* Constructor for OCSPResponse class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Enumerated}
* @desc responseStatus
*/
this.responseStatus = getParametersValue(parameters, "responseStatus", OCSPResponse.defaultValues("responseStatus"));
if("responseBytes" in parameters)
/**
* @type {ResponseBytes}
* @desc responseBytes
*/
this.responseBytes = getParametersValue(parameters, "responseBytes", OCSPResponse.defaultValues("responseBytes"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "responseStatus":
return new asn1js.Enumerated();
case "responseBytes":
return new ResponseBytes();
default:
throw new Error(`Invalid member name for OCSPResponse class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "responseStatus":
return (memberValue.isEqual(OCSPResponse.defaultValues(memberName)));
case "responseBytes":
return ((ResponseBytes.compareWithDefault("responseType", memberValue.responseType)) &&
(ResponseBytes.compareWithDefault("response", memberValue.response)));
default:
throw new Error(`Invalid member name for OCSPResponse class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* OCSPResponse ::= SEQUENCE {
* responseStatus OCSPResponseStatus,
* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
*
* OCSPResponseStatus ::= ENUMERATED {
* successful (0), -- Response has valid confirmations
* malformedRequest (1), -- Illegal confirmation request
* internalError (2), -- Internal error in issuer
* tryLater (3), -- Try again later
* -- (4) is not used
* sigRequired (5), -- Must sign the request
* unauthorized (6) -- Request unauthorized
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [responseStatus]
* @property {string} [responseBytes]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || "OCSPResponse"),
value: [
new asn1js.Enumerated({ name: (names.responseStatus || "responseStatus") }),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
ResponseBytes.schema(names.responseBytes || {
names: {
blockName: "responseBytes"
}
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"responseStatus",
"responseBytes"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
OCSPResponse.schema()
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for OCSPResponse");
//endregion
//region Get internal properties from parsed schema
this.responseStatus = asn1.result.responseStatus;
if("responseBytes" in asn1.result)
this.responseBytes = new ResponseBytes({ schema: asn1.result.responseBytes });
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(this.responseStatus);
if("responseBytes" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [this.responseBytes.toSchema()]
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
responseStatus: this.responseStatus.toJSON()
};
if("responseBytes" in this)
_object.responseBytes = this.responseBytes.toJSON();
return _object;
}
//**********************************************************************************
/**
* Get OCSP response status for specific certificate
* @param {Certificate} certificate
* @param {Certificate} issuerCertificate
* @returns {*}
*/
getCertificateStatus(certificate, issuerCertificate)
{
//region Initial variables
let basicResponse;
const result = {
isForCertificate: false,
status: 2 // 0 = good, 1 = revoked, 2 = unknown
};
//endregion
//region Check that "ResponseBytes" contain "OCSP_BASIC_RESPONSE"
if(("responseBytes" in this) === false)
return result;
if(this.responseBytes.responseType !== "1.3.6.1.5.5.7.48.1.1") // id-pkix-ocsp-basic
return result;
try
{
const asn1Basic = asn1js.fromBER(this.responseBytes.response.valueBlock.valueHex);
basicResponse = new BasicOCSPResponse({ schema: asn1Basic.result });
}
catch(ex)
{
return result;
}
//endregion
return basicResponse.getCertificateStatus(certificate, issuerCertificate);
}
//**********************************************************************************
/**
* Make a signature for current OCSP Response
* @param {Object} privateKey Private key for "subjectPublicKeyInfo" structure
* @param {string} [hashAlgorithm] Hashing algorithm. Default SHA-1
* @returns {Promise}
*/
sign(privateKey, hashAlgorithm)
{
//region Check that ResponseData has type BasicOCSPResponse and sign it
if(this.responseBytes.responseType === "1.3.6.1.5.5.7.48.1.1")
{
const asn1 = asn1js.fromBER(this.responseBytes.response.valueBlock.valueHex);
const basicResponse = new BasicOCSPResponse({ schema: asn1.result });
return basicResponse.sign(privateKey, hashAlgorithm);
}
return Promise.reject(`Unknown ResponseBytes type: ${this.responseBytes.responseType}`);
//endregion
}
//**********************************************************************************
/**
* Verify current OCSP Response
* @param {Certificate|null} issuerCertificate In order to decrease size of resp issuer cert could be ommited. In such case you need manually provide it.
* @returns {Promise}
*/
verify(issuerCertificate = null)
{
//region Check that ResponseBytes exists in the object
if(("responseBytes" in this) === false)
return Promise.reject("Empty ResponseBytes field");
//endregion
//region Check that ResponceData has type BasicOCSPResponse and verify it
if(this.responseBytes.responseType === "1.3.6.1.5.5.7.48.1.1")
{
const asn1 = asn1js.fromBER(this.responseBytes.response.valueBlock.valueHex);
const basicResponse = new BasicOCSPResponse({ schema: asn1.result });
if(issuerCertificate !== null)
{
if(("certs" in basicResponse) === false)
basicResponse.certs = [];
basicResponse.certs.push(issuerCertificate);
}
return basicResponse.verify();
}
return Promise.reject(`Unknown ResponseBytes type: ${this.responseBytes.responseType}`);
//endregion
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,228 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import IssuerAndSerialNumber from "./IssuerAndSerialNumber.js";
import OriginatorPublicKey from "./OriginatorPublicKey.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class OriginatorIdentifierOrKey
{
//**********************************************************************************
/**
* Constructor for OriginatorIdentifierOrKey class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc variant
*/
this.variant = getParametersValue(parameters, "variant", OriginatorIdentifierOrKey.defaultValues("variant"));
if("value" in parameters)
/**
* @type {IssuerAndSerialNumber|OctetString|OriginatorPublicKey}
* @desc value
*/
this.value = getParametersValue(parameters, "value", OriginatorIdentifierOrKey.defaultValues("value"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "variant":
return (-1);
case "value":
return {};
default:
throw new Error(`Invalid member name for OriginatorIdentifierOrKey class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "variant":
return (memberValue === (-1));
case "value":
return (Object.keys(memberValue).length === 0);
default:
throw new Error(`Invalid member name for OriginatorIdentifierOrKey class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* OriginatorIdentifierOrKey ::= CHOICE {
* issuerAndSerialNumber IssuerAndSerialNumber,
* subjectKeyIdentifier [0] SubjectKeyIdentifier,
* originatorKey [1] OriginatorPublicKey }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Choice({
value: [
IssuerAndSerialNumber.schema({
names: {
blockName: (names.blockName || "")
}
}),
new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
name: (names.blockName || "")
}),
new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
name: (names.blockName || ""),
value: OriginatorPublicKey.schema().valueBlock.value
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"blockName"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
OriginatorIdentifierOrKey.schema({
names: {
blockName: "blockName"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for OriginatorIdentifierOrKey");
//endregion
//region Get internal properties from parsed schema
if(asn1.result.blockName.idBlock.tagClass === 1)
{
this.variant = 1;
this.value = new IssuerAndSerialNumber({ schema: asn1.result.blockName });
}
else
{
if(asn1.result.blockName.idBlock.tagNumber === 0)
{
//region Create "OCTETSTRING" from "ASN1_PRIMITIVE"
asn1.result.blockName.idBlock.tagClass = 1; // UNIVERSAL
asn1.result.blockName.idBlock.tagNumber = 4; // OCTETSTRING
//endregion
this.variant = 2;
this.value = asn1.result.blockName;
}
else
{
this.variant = 3;
this.value = new OriginatorPublicKey({
schema: new asn1js.Sequence({
value: asn1.result.blockName.valueBlock.value
})
});
}
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
switch(this.variant)
{
case 1:
return this.value.toSchema();
case 2:
this.value.idBlock.tagClass = 3; // CONTEXT-SPECIFIC
this.value.idBlock.tagNumber = 0; // [0]
return this.value;
case 3:
{
const _schema = this.value.toSchema();
_schema.idBlock.tagClass = 3; // CONTEXT-SPECIFIC
_schema.idBlock.tagNumber = 1; // [1]
return _schema;
}
default:
return new asn1js.Any();
}
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
variant: this.variant
};
if((this.variant === 1) || (this.variant === 2) || (this.variant === 3))
_object.value = this.value.toJSON();
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

228
core/third-party/pkijs/OriginatorInfo.js vendored Normal file
View File

@@ -0,0 +1,228 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import CertificateSet from "./CertificateSet.js";
import RevocationInfoChoices from "./RevocationInfoChoices.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class OriginatorInfo
{
//**********************************************************************************
/**
* Constructor for OriginatorInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
if("certs" in parameters)
/**
* @type {CertificateSet}
* @desc certs
*/
this.certs = getParametersValue(parameters, "certs", OriginatorInfo.defaultValues("certs"));
if("crls" in parameters)
/**
* @type {RevocationInfoChoices}
* @desc crls
*/
this.crls = getParametersValue(parameters, "crls", OriginatorInfo.defaultValues("crls"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "certs":
return new CertificateSet();
case "crls":
return new RevocationInfoChoices();
default:
throw new Error(`Invalid member name for OriginatorInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "certs":
return (memberValue.certificates.length === 0);
case "crls":
return ((memberValue.crls.length === 0) && (memberValue.otherRevocationInfos.length === 0));
default:
throw new Error(`Invalid member name for OriginatorInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* OriginatorInfo ::= SEQUENCE {
* certs [0] IMPLICIT CertificateSet OPTIONAL,
* crls [1] IMPLICIT RevocationInfoChoices OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [certs]
* @property {string} [crls]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Constructed({
name: (names.certs || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: CertificateSet.schema().valueBlock.value
}),
new asn1js.Constructed({
name: (names.crls || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: RevocationInfoChoices.schema().valueBlock.value
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"certs",
"crls"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
OriginatorInfo.schema({
names: {
certs: "certs",
crls: "crls"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for OriginatorInfo");
//endregion
//region Get internal properties from parsed schema
if("certs" in asn1.result)
{
this.certs = new CertificateSet({
schema: new asn1js.Set({
value: asn1.result.certs.valueBlock.value
})
});
}
if("crls" in asn1.result)
{
this.crls = new RevocationInfoChoices({
schema: new asn1js.Set({
value: asn1.result.crls.valueBlock.value
})
});
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
const sequenceValue = [];
if("certs" in this)
{
sequenceValue.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: this.certs.toSchema().valueBlock.value
}));
}
if("crls" in this)
{
sequenceValue.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: this.crls.toSchema().valueBlock.value
}));
}
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: sequenceValue
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {};
if("certs" in this)
object.certs = this.certs.toJSON();
if("crls" in this)
object.crls = this.crls.toJSON();
return object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,170 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class OriginatorPublicKey
{
//**********************************************************************************
/**
* Constructor for OriginatorPublicKey class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {AlgorithmIdentifier}
* @desc algorithm
*/
this.algorithm = getParametersValue(parameters, "algorithm", OriginatorPublicKey.defaultValues("algorithm"));
/**
* @type {BitString}
* @desc publicKey
*/
this.publicKey = getParametersValue(parameters, "publicKey", OriginatorPublicKey.defaultValues("publicKey"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "algorithm":
return new AlgorithmIdentifier();
case "publicKey":
return new asn1js.BitString();
default:
throw new Error(`Invalid member name for OriginatorPublicKey class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "algorithm":
case "publicKey":
return (memberValue.isEqual(OriginatorPublicKey.defaultValues(memberName)));
default:
throw new Error(`Invalid member name for OriginatorPublicKey class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* OriginatorPublicKey ::= SEQUENCE {
* algorithm AlgorithmIdentifier,
* publicKey BIT STRING }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [algorithm]
* @property {string} [publicKey]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
AlgorithmIdentifier.schema(names.algorithm || {}),
new asn1js.BitString({ name: (names.publicKey || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"algorithm",
"publicKey"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
OriginatorPublicKey.schema({
names: {
algorithm: {
names: {
blockName: "algorithm"
}
},
publicKey: "publicKey"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for OriginatorPublicKey");
//endregion
//region Get internal properties from parsed schema
this.algorithm = new AlgorithmIdentifier({ schema: asn1.result.algorithm });
this.publicKey = asn1.result.publicKey;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
this.algorithm.toSchema(),
this.publicKey
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
algorithm: this.algorithm.toJSON(),
publicKey: this.publicKey.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,147 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class OtherCertificateFormat
{
//**********************************************************************************
/**
* Constructor for OtherCertificateFormat class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc otherCertFormat
*/
this.otherCertFormat = getParametersValue(parameters, "otherCertFormat", OtherCertificateFormat.defaultValues("otherCertFormat"));
/**
* @type {Any}
* @desc otherCert
*/
this.otherCert = getParametersValue(parameters, "otherCert", OtherCertificateFormat.defaultValues("otherCert"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "otherCertFormat":
return "";
case "otherCert":
return new asn1js.Any();
default:
throw new Error(`Invalid member name for OtherCertificateFormat class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* OtherCertificateFormat ::= SEQUENCE {
* otherCertFormat OBJECT IDENTIFIER,
* otherCert ANY DEFINED BY otherCertFormat }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [otherCertFormat]
* @property {string} [otherCert]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.otherCertFormat || "otherCertFormat") }),
new asn1js.Any({ name: (names.otherCert || "otherCert") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"otherCertFormat",
"otherCert"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
OtherCertificateFormat.schema()
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for OtherCertificateFormat");
//endregion
//region Get internal properties from parsed schema
this.otherCertFormat = asn1.result.otherCertFormat.valueBlock.toString();
this.otherCert = asn1.result.otherCert;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.otherCertFormat }),
this.otherCert
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
otherCertFormat: this.otherCertFormat
};
if(!(this.otherCert instanceof asn1js.Any))
object.otherCert = this.otherCert.toJSON();
return object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,185 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class OtherKeyAttribute
{
//**********************************************************************************
/**
* Constructor for OtherKeyAttribute class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc keyAttrId
*/
this.keyAttrId = getParametersValue(parameters, "keyAttrId", OtherKeyAttribute.defaultValues("keyAttrId"));
if("keyAttr" in parameters)
/**
* @type {*}
* @desc keyAttr
*/
this.keyAttr = getParametersValue(parameters, "keyAttr", OtherKeyAttribute.defaultValues("keyAttr"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "keyAttrId":
return "";
case "keyAttr":
return {};
default:
throw new Error(`Invalid member name for OtherKeyAttribute class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "keyAttrId":
return (memberValue === "");
case "keyAttr":
return (Object.keys(memberValue).length === 0);
default:
throw new Error(`Invalid member name for OtherKeyAttribute class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* OtherKeyAttribute ::= SEQUENCE {
* keyAttrId OBJECT IDENTIFIER,
* keyAttr ANY DEFINED BY keyAttrId OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [optional]
* @property {string} [keyAttrId]
* @property {string} [keyAttr]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
optional: (names.optional || true),
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.keyAttrId || "") }),
new asn1js.Any({
optional: true,
name: (names.keyAttr || "")
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"keyAttrId",
"keyAttr"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
OtherKeyAttribute.schema({
names: {
keyAttrId: "keyAttrId",
keyAttr: "keyAttr"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for OtherKeyAttribute");
//endregion
//region Get internal properties from parsed schema
this.keyAttrId = asn1.result.keyAttrId.valueBlock.toString();
if("keyAttr" in asn1.result)
this.keyAttr = asn1.result.keyAttr;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(new asn1js.ObjectIdentifier({ value: this.keyAttrId }));
if("keyAttr" in this)
outputArray.push(this.keyAttr);
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
keyAttrId: this.keyAttrId
};
if("keyAttr" in this)
_object.keyAttr = this.keyAttr.toJSON();
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

190
core/third-party/pkijs/OtherPrimeInfo.js vendored Normal file
View File

@@ -0,0 +1,190 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, toBase64, arrayBufferToString, stringToArrayBuffer, fromBase64, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC3447
*/
export default class OtherPrimeInfo
{
//**********************************************************************************
/**
* Constructor for OtherPrimeInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Integer}
* @desc prime
*/
this.prime = getParametersValue(parameters, "prime", OtherPrimeInfo.defaultValues("prime"));
/**
* @type {Integer}
* @desc exponent
*/
this.exponent = getParametersValue(parameters, "exponent", OtherPrimeInfo.defaultValues("exponent"));
/**
* @type {Integer}
* @desc coefficient
*/
this.coefficient = getParametersValue(parameters, "coefficient", OtherPrimeInfo.defaultValues("coefficient"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
//region If input argument array contains "json" for this object
if("json" in parameters)
this.fromJSON(parameters.json);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "prime":
return new asn1js.Integer();
case "exponent":
return new asn1js.Integer();
case "coefficient":
return new asn1js.Integer();
default:
throw new Error(`Invalid member name for OtherPrimeInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* OtherPrimeInfo ::= Sequence {
* prime Integer, -- ri
* exponent Integer, -- di
* coefficient Integer -- ti
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} prime
* @property {string} exponent
* @property {string} coefficient
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.prime || "") }),
new asn1js.Integer({ name: (names.exponent || "") }),
new asn1js.Integer({ name: (names.coefficient || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"prime",
"exponent",
"coefficient"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
OtherPrimeInfo.schema({
names: {
prime: "prime",
exponent: "exponent",
coefficient: "coefficient"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for OtherPrimeInfo");
//endregion
//region Get internal properties from parsed schema
this.prime = asn1.result.prime.convertFromDER();
this.exponent = asn1.result.exponent.convertFromDER();
this.coefficient = asn1.result.coefficient.convertFromDER();
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
this.prime.convertToDER(),
this.exponent.convertToDER(),
this.coefficient.convertToDER()
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
r: toBase64(arrayBufferToString(this.prime.valueBlock.valueHex), true, true),
d: toBase64(arrayBufferToString(this.exponent.valueBlock.valueHex), true, true),
t: toBase64(arrayBufferToString(this.coefficient.valueBlock.valueHex), true, true)
};
}
//**********************************************************************************
/**
* Convert JSON value into current object
* @param {Object} json
*/
fromJSON(json)
{
if("r" in json)
this.prime = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.r, true)) });
else
throw new Error("Absent mandatory parameter \"r\"");
if("d" in json)
this.exponent = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.d, true)) });
else
throw new Error("Absent mandatory parameter \"d\"");
if("t" in json)
this.coefficient = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.t, true)) });
else
throw new Error("Absent mandatory parameter \"t\"");
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,170 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class OtherRecipientInfo
{
//**********************************************************************************
/**
* Constructor for OtherRecipientInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc oriType
*/
this.oriType = getParametersValue(parameters, "oriType", OtherRecipientInfo.defaultValues("oriType"));
/**
* @type {*}
* @desc oriValue
*/
this.oriValue = getParametersValue(parameters, "oriValue", OtherRecipientInfo.defaultValues("oriValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "oriType":
return "";
case "oriValue":
return {};
default:
throw new Error(`Invalid member name for OtherRecipientInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "oriType":
return (memberValue === "");
case "oriValue":
return (Object.keys(memberValue).length === 0);
default:
throw new Error(`Invalid member name for OtherRecipientInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* OtherRecipientInfo ::= SEQUENCE {
* oriType OBJECT IDENTIFIER,
* oriValue ANY DEFINED BY oriType }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [oriType]
* @property {string} [oriValue]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.oriType || "") }),
new asn1js.Any({ name: (names.oriValue || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"oriType",
"oriValue"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
OtherRecipientInfo.schema({
names: {
oriType: "oriType",
oriValue: "oriValue"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for OtherRecipientInfo");
//endregion
//region Get internal properties from parsed schema
this.oriType = asn1.result.oriType.valueBlock.toString();
this.oriValue = asn1.result.oriValue;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.oriType }),
this.oriValue
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
oriType: this.oriType
};
if(OtherRecipientInfo.compareWithDefault("oriValue", this.oriValue) === false)
_object.oriValue = this.oriValue.toJSON();
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,147 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class OtherRevocationInfoFormat
{
//**********************************************************************************
/**
* Constructor for OtherRevocationInfoFormat class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc otherRevInfoFormat
*/
this.otherRevInfoFormat = getParametersValue(parameters, "otherRevInfoFormat", OtherRevocationInfoFormat.defaultValues("otherRevInfoFormat"));
/**
* @type {Any}
* @desc otherRevInfo
*/
this.otherRevInfo = getParametersValue(parameters, "otherRevInfo", OtherRevocationInfoFormat.defaultValues("otherRevInfo"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "otherRevInfoFormat":
return "";
case "otherRevInfo":
return new asn1js.Any();
default:
throw new Error(`Invalid member name for OtherRevocationInfoFormat class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* OtherCertificateFormat ::= SEQUENCE {
* otherRevInfoFormat OBJECT IDENTIFIER,
* otherRevInfo ANY DEFINED BY otherCertFormat }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [otherRevInfoFormat]
* @property {string} [otherRevInfo]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.otherRevInfoFormat || "otherRevInfoFormat") }),
new asn1js.Any({ name: (names.otherRevInfo || "otherRevInfo") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"otherRevInfoFormat",
"otherRevInfo"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
OtherRevocationInfoFormat.schema()
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for OtherRevocationInfoFormat");
//endregion
//region Get internal properties from parsed schema
this.otherRevInfoFormat = asn1.result.otherRevInfoFormat.valueBlock.toString();
this.otherRevInfo = asn1.result.otherRevInfo;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.otherRevInfoFormat }),
this.otherRevInfo
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
otherRevInfoFormat: this.otherRevInfoFormat
};
if(!(this.otherRevInfo instanceof asn1js.Any))
object.otherRevInfo = this.otherRevInfo.toJSON();
return object;
}
//**********************************************************************************
}
//**************************************************************************************

157
core/third-party/pkijs/PBES2Params.js vendored Normal file
View File

@@ -0,0 +1,157 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
//**************************************************************************************
/**
* Class from RFC2898
*/
export default class PBES2Params
{
//**********************************************************************************
/**
* Constructor for PBES2Params class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {AlgorithmIdentifier}
* @desc keyDerivationFunc
*/
this.keyDerivationFunc = getParametersValue(parameters, "keyDerivationFunc", PBES2Params.defaultValues("keyDerivationFunc"));
/**
* @type {AlgorithmIdentifier}
* @desc encryptionScheme
*/
this.encryptionScheme = getParametersValue(parameters, "encryptionScheme", PBES2Params.defaultValues("encryptionScheme"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "keyDerivationFunc":
return new AlgorithmIdentifier();
case "encryptionScheme":
return new AlgorithmIdentifier();
default:
throw new Error(`Invalid member name for PBES2Params class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PBES2-params ::= SEQUENCE {
* keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [keyDerivationFunc]
* @property {string} [encryptionScheme]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
AlgorithmIdentifier.schema(names.keyDerivationFunc || {}),
AlgorithmIdentifier.schema(names.encryptionScheme || {})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"keyDerivationFunc",
"encryptionScheme"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PBES2Params.schema({
names: {
keyDerivationFunc: {
names: {
blockName: "keyDerivationFunc"
}
},
encryptionScheme: {
names: {
blockName: "encryptionScheme"
}
}
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PBES2Params");
//endregion
//region Get internal properties from parsed schema
this.keyDerivationFunc = new AlgorithmIdentifier({ schema: asn1.result.keyDerivationFunc });
this.encryptionScheme = new AlgorithmIdentifier({ schema: asn1.result.encryptionScheme });
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
this.keyDerivationFunc.toSchema(),
this.encryptionScheme.toSchema()
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
keyDerivationFunc: this.keyDerivationFunc.toJSON(),
encryptionScheme: this.encryptionScheme.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

242
core/third-party/pkijs/PBKDF2Params.js vendored Normal file
View File

@@ -0,0 +1,242 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
//**************************************************************************************
/**
* Class from RFC2898
*/
export default class PBKDF2Params
{
//**********************************************************************************
/**
* Constructor for PBKDF2Params class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Object}
* @desc salt
*/
this.salt = getParametersValue(parameters, "salt", PBKDF2Params.defaultValues("salt"));
/**
* @type {number}
* @desc iterationCount
*/
this.iterationCount = getParametersValue(parameters, "iterationCount", PBKDF2Params.defaultValues("iterationCount"));
if("keyLength" in parameters)
/**
* @type {number}
* @desc keyLength
*/
this.keyLength = getParametersValue(parameters, "keyLength", PBKDF2Params.defaultValues("keyLength"));
if("prf" in parameters)
/**
* @type {AlgorithmIdentifier}
* @desc prf
*/
this.prf = getParametersValue(parameters, "prf", PBKDF2Params.defaultValues("prf"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "salt":
return {};
case "iterationCount":
return (-1);
case "keyLength":
return 0;
case "prf":
return new AlgorithmIdentifier({
algorithmId: "1.3.14.3.2.26", // SHA-1
algorithmParams: new asn1js.Null()
});
default:
throw new Error(`Invalid member name for PBKDF2Params class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PBKDF2-params ::= SEQUENCE {
* salt CHOICE {
* specified OCTET STRING,
* otherSource AlgorithmIdentifier },
* iterationCount INTEGER (1..MAX),
* keyLength INTEGER (1..MAX) OPTIONAL,
* prf AlgorithmIdentifier
* DEFAULT { algorithm hMAC-SHA1, parameters NULL } }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [saltPrimitive]
* @property {string} [saltConstructed]
* @property {string} [iterationCount]
* @property {string} [keyLength]
* @property {string} [prf]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Choice({
value: [
new asn1js.OctetString({ name: (names.saltPrimitive || "") }),
AlgorithmIdentifier.schema(names.saltConstructed || {})
]
}),
new asn1js.Integer({ name: (names.iterationCount || "") }),
new asn1js.Integer({
name: (names.keyLength || ""),
optional: true
}),
AlgorithmIdentifier.schema(names.prf || {
names: {
optional: true
}
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"salt",
"iterationCount",
"keyLength",
"prf"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PBKDF2Params.schema({
names: {
saltPrimitive: "salt",
saltConstructed: {
names: {
blockName: "salt"
}
},
iterationCount: "iterationCount",
keyLength: "keyLength",
prf: {
names: {
blockName: "prf",
optional: true
}
}
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PBKDF2Params");
//endregion
//region Get internal properties from parsed schema
this.salt = asn1.result.salt;
this.iterationCount = asn1.result.iterationCount.valueBlock.valueDec;
if("keyLength" in asn1.result)
this.keyLength = asn1.result.keyLength.valueBlock.valueDec;
if("prf" in asn1.result)
this.prf = new AlgorithmIdentifier({ schema: asn1.result.prf });
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(this.salt);
outputArray.push(new asn1js.Integer({ value: this.iterationCount }));
if("keyLength" in this)
{
if(PBKDF2Params.defaultValues("keyLength") !== this.keyLength)
outputArray.push(new asn1js.Integer({ value: this.keyLength }));
}
if("prf" in this)
{
if(PBKDF2Params.defaultValues("prf").isEqual(this.prf) === false)
outputArray.push(this.prf.toSchema());
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
salt: this.salt.toJSON(),
iterationCount: this.iterationCount
};
if("keyLength" in this)
{
if(PBKDF2Params.defaultValues("keyLength") !== this.keyLength)
_object.keyLength = this.keyLength;
}
if("prf" in this)
{
if(PBKDF2Params.defaultValues("prf").isEqual(this.prf) === false)
_object.prf = this.prf.toJSON();
}
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

650
core/third-party/pkijs/PFX.js vendored Normal file
View File

@@ -0,0 +1,650 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, utilConcatBuf, clearProps } from "./pvutils.js";
import { getCrypto, getEngine, getRandomValues, getOIDByAlgorithm, getAlgorithmByOID } from "./common.js";
import ContentInfo from "./ContentInfo.js";
import MacData from "./MacData.js";
import DigestInfo from "./DigestInfo.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import SignedData from "./SignedData.js";
import EncapsulatedContentInfo from "./EncapsulatedContentInfo.js";
import Attribute from "./Attribute.js";
import SignerInfo from "./SignerInfo.js";
import IssuerAndSerialNumber from "./IssuerAndSerialNumber.js";
import SignedAndUnsignedAttributes from "./SignedAndUnsignedAttributes.js";
import AuthenticatedSafe from "./AuthenticatedSafe.js";
//**************************************************************************************
/**
* Class from RFC7292
*/
export default class PFX
{
//**********************************************************************************
/**
* Constructor for PFX class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", PFX.defaultValues("version"));
/**
* @type {ContentInfo}
* @desc authSafe
*/
this.authSafe = getParametersValue(parameters, "authSafe", PFX.defaultValues("authSafe"));
if("macData" in parameters)
/**
* @type {MacData}
* @desc macData
*/
this.macData = getParametersValue(parameters, "macData", PFX.defaultValues("macData"));
if("parsedValue" in parameters)
/**
* @type {*}
* @desc parsedValue
*/
this.parsedValue = getParametersValue(parameters, "parsedValue", PFX.defaultValues("parsedValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "version":
return 3;
case "authSafe":
return (new ContentInfo());
case "macData":
return (new MacData());
case "parsedValue":
return {};
default:
throw new Error(`Invalid member name for PFX class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "version":
return (memberValue === PFX.defaultValues(memberName));
case "authSafe":
return ((ContentInfo.compareWithDefault("contentType", memberValue.contentType)) &&
(ContentInfo.compareWithDefault("content", memberValue.content)));
case "macData":
return ((MacData.compareWithDefault("mac", memberValue.mac)) &&
(MacData.compareWithDefault("macSalt", memberValue.macSalt)) &&
(MacData.compareWithDefault("iterations", memberValue.iterations)));
case "parsedValue":
return ((memberValue instanceof Object) && (Object.keys(memberValue).length === 0));
default:
throw new Error(`Invalid member name for PFX class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PFX ::= SEQUENCE {
* version INTEGER {v3(3)}(v3,...),
* authSafe ContentInfo,
* macData MacData OPTIONAL
* }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [version]
* @property {string} [authSafe]
* @property {string} [macData]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.version || "version") }),
ContentInfo.schema(names.authSafe || {
names: {
blockName: "authSafe"
}
}),
MacData.schema(names.macData || {
names: {
blockName: "macData",
optional: true
}
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"version",
"authSafe",
"macData"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PFX.schema({
names: {
version: "version",
authSafe: {
names: {
blockName: "authSafe"
}
},
macData: {
names: {
blockName: "macData"
}
}
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PFX");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result.version.valueBlock.valueDec;
this.authSafe = new ContentInfo({ schema: asn1.result.authSafe });
if("macData" in asn1.result)
this.macData = new MacData({ schema: asn1.result.macData });
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
const outputArray = [
new asn1js.Integer({ value: this.version }),
this.authSafe.toSchema()
];
if("macData" in this)
outputArray.push(this.macData.toSchema());
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const output = {
version: this.version,
authSafe: this.authSafe.toJSON()
};
if("macData" in this)
output.macData = this.macData.toJSON();
return output;
}
//**********************************************************************************
/**
* Making ContentInfo from "parsedValue" object
* @param {Object} parameters Parameters, specific to each "integrity mode"
*/
makeInternalValues(parameters = {})
{
//region Check mandatory parameter
if((parameters instanceof Object) === false)
return Promise.reject("The \"parameters\" must has \"Object\" type");
if(("parsedValue" in this) === false)
return Promise.reject("Please call \"parseValues\" function first in order to make \"parsedValue\" data");
if(("integrityMode" in this.parsedValue) === false)
return Promise.reject("Absent mandatory parameter \"integrityMode\" inside \"parsedValue\"");
//endregion
//region Initial variables
let sequence = Promise.resolve();
//endregion
//region Get a "crypto" extension
const crypto = getCrypto();
if(typeof crypto === "undefined")
return Promise.reject("Unable to create WebCrypto object");
//endregion
//region Makes values for each particular integrity mode
//region Check that we do have neccessary fields in "parsedValue" object
if(("authenticatedSafe" in this.parsedValue) === false)
return Promise.reject("Absent mandatory parameter \"authenticatedSafe\" in \"parsedValue\"");
//endregion
switch(this.parsedValue.integrityMode)
{
//region HMAC-based integrity
case 0:
{
//region Check additional mandatory parameters
if(("iterations" in parameters) === false)
return Promise.reject("Absent mandatory parameter \"iterations\"");
if(("pbkdf2HashAlgorithm" in parameters) === false)
return Promise.reject("Absent mandatory parameter \"pbkdf2HashAlgorithm\"");
if(("hmacHashAlgorithm" in parameters) === false)
return Promise.reject("Absent mandatory parameter \"hmacHashAlgorithm\"");
if(("password" in parameters) === false)
return Promise.reject("Absent mandatory parameter \"password\"");
//endregion
//region Initial variables
const saltBuffer = new ArrayBuffer(64);
const saltView = new Uint8Array(saltBuffer);
getRandomValues(saltView);
const data = this.parsedValue.authenticatedSafe.toSchema().toBER(false);
this.authSafe = new ContentInfo({
contentType: "1.2.840.113549.1.7.1",
content: new asn1js.OctetString({ valueHex: data })
});
//endregion
//region Call current crypto engine for making HMAC-based data stamp
const engine = getEngine();
if(("stampDataWithPassword" in engine.subtle) === false)
return Promise.reject(`No support for "stampDataWithPassword" in current engine "${engine.name}"`);
sequence = sequence.then(() =>
engine.subtle.stampDataWithPassword({
password: parameters.password,
hashAlgorithm: parameters.hmacHashAlgorithm,
salt: saltBuffer,
iterationCount: parameters.iterations,
contentToStamp: data
})
);
//endregion
//region Make "MacData" values
sequence = sequence.then(
result =>
{
this.macData = new MacData({
mac: new DigestInfo({
digestAlgorithm: new AlgorithmIdentifier({
algorithmId: getOIDByAlgorithm({ name: parameters.hmacHashAlgorithm })
}),
digest: new asn1js.OctetString({ valueHex: result })
}),
macSalt: new asn1js.OctetString({ valueHex: saltBuffer }),
iterations: parameters.iterations
});
},
error => Promise.reject(error)
);
//endregion
//endregion
}
break;
//endregion
//region publicKey-based integrity
case 1:
{
//region Check additional mandatory parameters
if(("signingCertificate" in parameters) === false)
return Promise.reject("Absent mandatory parameter \"signingCertificate\"");
if(("privateKey" in parameters) === false)
return Promise.reject("Absent mandatory parameter \"privateKey\"");
if(("hashAlgorithm" in parameters) === false)
return Promise.reject("Absent mandatory parameter \"hashAlgorithm\"");
//endregion
//region Making data to be signed
// NOTE: all internal data for "authenticatedSafe" must be already prepared.
// Thus user must call "makeValues" for all internal "SafeContent" value with appropriate parameters.
// Or user can choose to use values from initial parsing of existing PKCS#12 data.
const toBeSigned = this.parsedValue.authenticatedSafe.toSchema().toBER(false);
//endregion
//region Initial variables
const cmsSigned = new SignedData({
version: 1,
encapContentInfo: new EncapsulatedContentInfo({
eContentType: "1.2.840.113549.1.7.1", // "data" content type
eContent: new asn1js.OctetString({ valueHex: toBeSigned })
}),
certificates: [parameters.signingCertificate]
});
//endregion
//region Making additional attributes for CMS Signed Data
//region Create a message digest
sequence = sequence.then(
() => crypto.digest({ name: parameters.hashAlgorithm }, new Uint8Array(toBeSigned))
);
//endregion
//region Combine all signed extensions
sequence = sequence.then(
result =>
{
//region Initial variables
const signedAttr = [];
//endregion
//region contentType
signedAttr.push(new Attribute({
type: "1.2.840.113549.1.9.3",
values: [
new asn1js.ObjectIdentifier({ value: "1.2.840.113549.1.7.1" })
]
}));
//endregion
//region signingTime
signedAttr.push(new Attribute({
type: "1.2.840.113549.1.9.5",
values: [
new asn1js.UTCTime({ valueDate: new Date() })
]
}));
//endregion
//region messageDigest
signedAttr.push(new Attribute({
type: "1.2.840.113549.1.9.4",
values: [
new asn1js.OctetString({ valueHex: result })
]
}));
//endregion
//region Making final value for "SignerInfo" type
cmsSigned.signerInfos.push(new SignerInfo({
version: 1,
sid: new IssuerAndSerialNumber({
issuer: parameters.signingCertificate.issuer,
serialNumber: parameters.signingCertificate.serialNumber
}),
signedAttrs: new SignedAndUnsignedAttributes({
type: 0,
attributes: signedAttr
})
}));
//endregion
},
error => Promise.reject(`Error during making digest for message: ${error}`)
);
//endregion
//endregion
//region Signing CMS Signed Data
sequence = sequence.then(
() => cmsSigned.sign(parameters.privateKey, 0, parameters.hashAlgorithm)
);
//endregion
//region Making final CMS_CONTENT_INFO type
sequence = sequence.then(
() =>
{
this.authSafe = new ContentInfo({
contentType: "1.2.840.113549.1.7.2",
content: cmsSigned.toSchema(true)
});
},
error => Promise.reject(`Error during making signature: ${error}`)
);
//endregion
}
break;
//endregion
//region default
default:
return Promise.reject(`Parameter "integrityMode" has unknown value: ${parameters.integrityMode}`);
//endregion
}
//endregion
return sequence;
}
//**********************************************************************************
parseInternalValues(parameters)
{
//region Check input data from "parameters"
if((parameters instanceof Object) === false)
return Promise.reject("The \"parameters\" must has \"Object\" type");
if(("checkIntegrity" in parameters) === false)
parameters.checkIntegrity = true;
//endregion
//region Initial variables
let sequence = Promise.resolve();
//endregion
//region Get a "crypto" extension
const crypto = getCrypto();
if(typeof crypto === "undefined")
return Promise.reject("Unable to create WebCrypto object");
//endregion
//region Create value for "this.parsedValue.authenticatedSafe" and check integrity
this.parsedValue = {};
switch(this.authSafe.contentType)
{
//region data
case "1.2.840.113549.1.7.1":
{
//region Check additional mandatory parameters
if(("password" in parameters) === false)
return Promise.reject("Absent mandatory parameter \"password\"");
//endregion
//region Integrity based on HMAC
this.parsedValue.integrityMode = 0;
//endregion
//region Check that we do have OCTETSTRING as "content"
if((this.authSafe.content instanceof asn1js.OctetString) === false)
return Promise.reject("Wrong type of \"this.authSafe.content\"");
//endregion
//region Check we have "constructive encoding" for AuthSafe content
let authSafeContent = new ArrayBuffer(0);
if(this.authSafe.content.valueBlock.isConstructed)
{
for(const contentValue of this.authSafe.content.valueBlock.value)
authSafeContent = utilConcatBuf(authSafeContent, contentValue.valueBlock.valueHex);
}
else
authSafeContent = this.authSafe.content.valueBlock.valueHex;
//endregion
//region Parse internal ASN.1 data
const asn1 = asn1js.fromBER(authSafeContent);
if(asn1.offset === (-1))
return Promise.reject("Error during parsing of ASN.1 data inside \"this.authSafe.content\"");
//endregion
//region Set "authenticatedSafe" value
this.parsedValue.authenticatedSafe = new AuthenticatedSafe({ schema: asn1.result });
//endregion
//region Check integrity
if(parameters.checkIntegrity)
{
//region Check that "MacData" exists
if(("macData" in this) === false)
return Promise.reject("Absent \"macData\" value, can not check PKCS#12 data integrity");
//endregion
//region Initial variables
const hashAlgorithm = getAlgorithmByOID(this.macData.mac.digestAlgorithm.algorithmId);
if(("name" in hashAlgorithm) === false)
return Promise.reject(`Unsupported digest algorithm: ${this.macData.mac.digestAlgorithm.algorithmId}`);
//endregion
//region Call current crypto engine for verifying HMAC-based data stamp
const engine = getEngine();
sequence = sequence.then(() =>
engine.subtle.verifyDataStampedWithPassword({
password: parameters.password,
hashAlgorithm: hashAlgorithm.name,
salt: this.macData.macSalt.valueBlock.valueHex,
iterationCount: this.macData.iterations,
contentToVerify: authSafeContent,
signatureToVerify: this.macData.mac.digest.valueBlock.valueHex
})
);
//endregion
//region Verify HMAC signature
sequence = sequence.then(
result =>
{
if(result === false)
return Promise.reject("Integrity for the PKCS#12 data is broken!");
return Promise.resolve();
},
error => Promise.reject(error)
);
//endregion
}
//endregion
}
break;
//endregion
//region signedData
case "1.2.840.113549.1.7.2":
{
//region Integrity based on signature using public key
this.parsedValue.integrityMode = 1;
//endregion
//region Parse CMS Signed Data
const cmsSigned = new SignedData({ schema: this.authSafe.content });
//endregion
//region Check that we do have OCTETSTRING as "content"
if(("eContent" in cmsSigned.encapContentInfo) === false)
return Promise.reject("Absent of attached data in \"cmsSigned.encapContentInfo\"");
if((cmsSigned.encapContentInfo.eContent instanceof asn1js.OctetString) === false)
return Promise.reject("Wrong type of \"cmsSigned.encapContentInfo.eContent\"");
//endregion
//region Create correct data block for verification
let data = new ArrayBuffer(0);
if(cmsSigned.encapContentInfo.eContent.idBlock.isConstructed === false)
data = cmsSigned.encapContentInfo.eContent.valueBlock.valueHex;
else
{
for(let i = 0; i < cmsSigned.encapContentInfo.eContent.valueBlock.value.length; i++)
data = utilConcatBuf(data, cmsSigned.encapContentInfo.eContent.valueBlock.value[i].valueBlock.valueHex);
}
//endregion
//region Parse internal ASN.1 data
const asn1 = asn1js.fromBER(data);
if(asn1.offset === (-1))
return Promise.reject("Error during parsing of ASN.1 data inside \"this.authSafe.content\"");
//endregion
//region Set "authenticatedSafe" value
this.parsedValue.authenticatedSafe = new AuthenticatedSafe({ schema: asn1.result });
//endregion
//region Check integrity
sequence = sequence.then(
() => cmsSigned.verify({ signer: 0, checkChain: false })
).then(
result =>
{
if(result === false)
return Promise.reject("Integrity for the PKCS#12 data is broken!");
return Promise.resolve();
},
error => Promise.reject(`Error during integrity verification: ${error}`)
);
//endregion
}
break;
//endregion
//region default
default:
return Promise.reject(`Incorrect value for "this.authSafe.contentType": ${this.authSafe.contentType}`);
//endregion
}
//endregion
//region Return result of the function
return sequence.then(
() => this,
error => Promise.reject(`Error during parsing: ${error}`)
);
//endregion
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,285 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import EncryptedData from "./EncryptedData.js";
import EncryptedContentInfo from "./EncryptedContentInfo.js";
import PrivateKeyInfo from "./PrivateKeyInfo.js";
//**************************************************************************************
/**
* Class from RFC7292
*/
export default class PKCS8ShroudedKeyBag
{
//**********************************************************************************
/**
* Constructor for PKCS8ShroudedKeyBag class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {AlgorithmIdentifier}
* @desc encryptionAlgorithm
*/
this.encryptionAlgorithm = getParametersValue(parameters, "encryptionAlgorithm", PKCS8ShroudedKeyBag.defaultValues("encryptionAlgorithm"));
/**
* @type {OctetString}
* @desc encryptedData
*/
this.encryptedData = getParametersValue(parameters, "encryptedData", PKCS8ShroudedKeyBag.defaultValues("encryptedData"));
if("parsedValue" in parameters)
/**
* @type {*}
* @desc parsedValue
*/
this.parsedValue = getParametersValue(parameters, "parsedValue", PKCS8ShroudedKeyBag.defaultValues("parsedValue"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "encryptionAlgorithm":
return (new AlgorithmIdentifier());
case "encryptedData":
return (new asn1js.OctetString());
case "parsedValue":
return {};
default:
throw new Error(`Invalid member name for PKCS8ShroudedKeyBag class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "encryptionAlgorithm":
return ((AlgorithmIdentifier.compareWithDefault("algorithmId", memberValue.algorithmId)) &&
(("algorithmParams" in memberValue) === false));
case "encryptedData":
return (memberValue.isEqual(PKCS8ShroudedKeyBag.defaultValues(memberName)));
case "parsedValue":
return ((memberValue instanceof Object) && (Object.keys(memberValue).length === 0));
default:
throw new Error(`Invalid member name for PKCS8ShroudedKeyBag class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo
*
* EncryptedPrivateKeyInfo ::= SEQUENCE {
* encryptionAlgorithm AlgorithmIdentifier {{KeyEncryptionAlgorithms}},
* encryptedData EncryptedData
* }
*
* EncryptedData ::= OCTET STRING
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [encryptionAlgorithm]
* @property {string} [encryptedData]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
AlgorithmIdentifier.schema(names.encryptionAlgorithm || {
names: {
blockName: "encryptionAlgorithm"
}
}),
new asn1js.Choice({
value: [
new asn1js.OctetString({ name: (names.encryptedData || "encryptedData") }),
new asn1js.OctetString({
idBlock: {
isConstructed: true
},
name: (names.encryptedData || "encryptedData")
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"encryptionAlgorithm",
"encryptedData"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PKCS8ShroudedKeyBag.schema({
names: {
encryptionAlgorithm: {
names: {
blockName: "encryptionAlgorithm"
}
},
encryptedData: "encryptedData"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PKCS8ShroudedKeyBag");
//endregion
//region Get internal properties from parsed schema
this.encryptionAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.encryptionAlgorithm });
this.encryptedData = asn1.result.encryptedData;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
this.encryptionAlgorithm.toSchema(),
this.encryptedData
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
encryptionAlgorithm: this.encryptionAlgorithm.toJSON(),
encryptedData: this.encryptedData.toJSON()
};
}
//**********************************************************************************
parseInternalValues(parameters)
{
//region Initial variables
let sequence = Promise.resolve();
const cmsEncrypted = new EncryptedData({
encryptedContentInfo: new EncryptedContentInfo({
contentEncryptionAlgorithm: this.encryptionAlgorithm,
encryptedContent: this.encryptedData
})
});
//endregion
//region Decrypt internal data
sequence = sequence.then(
() => cmsEncrypted.decrypt(parameters),
error => Promise.reject(error)
);
//endregion
//region Initialize "parsedValue" with decrypted PKCS#8 private key
sequence = sequence.then(
/**
* @param {ArrayBuffer} result
*/
result =>
{
const asn1 = asn1js.fromBER(result);
if(asn1.offset === (-1))
return Promise.reject("Error during parsing ASN.1 data");
this.parsedValue = new PrivateKeyInfo({ schema: asn1.result });
return Promise.resolve();
},
error => Promise.reject(error)
);
//endregion
return sequence;
}
//**********************************************************************************
makeInternalValues(parameters)
{
//region Check that we do have "parsedValue"
if(("parsedValue" in this) === false)
return Promise.reject("Please initialize \"parsedValue\" first");
//endregion
//region Initial variables
let sequence = Promise.resolve();
const cmsEncrypted = new EncryptedData();
//endregion
//region Encrypt internal data
sequence = sequence.then(
() =>
{
parameters.contentToEncrypt = this.parsedValue.toSchema().toBER(false);
return cmsEncrypted.encrypt(parameters);
},
error => Promise.reject(error)
);
//endregion
//region Initialize internal values
sequence = sequence.then(
() =>
{
this.encryptionAlgorithm = cmsEncrypted.encryptedContentInfo.contentEncryptionAlgorithm;
this.encryptedData = cmsEncrypted.encryptedContentInfo.encryptedContent;
}
);
//endregion
return sequence;
}
//**********************************************************************************
}
//**************************************************************************************

227
core/third-party/pkijs/PKIStatusInfo.js vendored Normal file
View File

@@ -0,0 +1,227 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC3161
*/
export default class PKIStatusInfo
{
//**********************************************************************************
/**
* Constructor for PKIStatusInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc status
*/
this.status = getParametersValue(parameters, "status", PKIStatusInfo.defaultValues("status"));
if("statusStrings" in parameters)
/**
* @type {Array.<Utf8String>}
* @desc statusStrings
*/
this.statusStrings = getParametersValue(parameters, "statusStrings", PKIStatusInfo.defaultValues("statusStrings"));
if("failInfo" in parameters)
/**
* @type {BitString}
* @desc failInfo
*/
this.failInfo = getParametersValue(parameters, "failInfo", PKIStatusInfo.defaultValues("failInfo"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "status":
return 2;
case "statusStrings":
return [];
case "failInfo":
return new asn1js.BitString();
default:
throw new Error(`Invalid member name for PKIStatusInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "status":
return (memberValue === PKIStatusInfo.defaultValues(memberName));
case "statusStrings":
return (memberValue.length === 0);
case "failInfo":
return (memberValue.isEqual(PKIStatusInfo.defaultValues(memberName)));
default:
throw new Error(`Invalid member name for PKIStatusInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PKIStatusInfo ::= SEQUENCE {
* status PKIStatus,
* statusString PKIFreeText OPTIONAL,
* failInfo PKIFailureInfo OPTIONAL }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [status]
* @property {string} [statusStrings]
* @property {string} [failInfo]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.status || "") }),
new asn1js.Sequence({
optional: true,
value: [
new asn1js.Repeated({
name: (names.statusStrings || ""),
value: new asn1js.Utf8String()
})
]
}),
new asn1js.BitString({
name: (names.failInfo || ""),
optional: true
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"status",
"statusStrings",
"failInfo"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PKIStatusInfo.schema({
names: {
status: "status",
statusStrings: "statusStrings",
failInfo: "failInfo"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PKIStatusInfo");
//endregion
//region Get internal properties from parsed schema
const _status = asn1.result.status;
if((_status.valueBlock.isHexOnly === true) ||
(_status.valueBlock.valueDec < 0) ||
(_status.valueBlock.valueDec > 5))
throw new Error("PKIStatusInfo \"status\" has invalid value");
this.status = _status.valueBlock.valueDec;
if("statusStrings" in asn1.result)
this.statusStrings = asn1.result.statusStrings;
if("failInfo" in asn1.result)
this.failInfo = asn1.result.failInfo;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array of output sequence
const outputArray = [];
outputArray.push(new asn1js.Integer({ value: this.status }));
if("statusStrings" in this)
{
outputArray.push(new asn1js.Sequence({
optional: true,
value: this.statusStrings
}));
}
if("failInfo" in this)
outputArray.push(this.failInfo);
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const _object = {
status: this.status
};
if("statusStrings" in this)
_object.statusStrings = Array.from(this.statusStrings, element => element.toJSON());
if("failInfo" in this)
_object.failInfo = this.failInfo.toJSON();
return _object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,247 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
//**************************************************************************************
/**
* Class from RFC5652
*/
export default class PasswordRecipientinfo
{
//**********************************************************************************
/**
* Constructor for PasswordRecipientinfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", PasswordRecipientinfo.defaultValues("version"));
if("keyDerivationAlgorithm" in parameters)
/**
* @type {AlgorithmIdentifier}
* @desc keyDerivationAlgorithm
*/
this.keyDerivationAlgorithm = getParametersValue(parameters, "keyDerivationAlgorithm", PasswordRecipientinfo.defaultValues("keyDerivationAlgorithm"));
/**
* @type {AlgorithmIdentifier}
* @desc keyEncryptionAlgorithm
*/
this.keyEncryptionAlgorithm = getParametersValue(parameters, "keyEncryptionAlgorithm", PasswordRecipientinfo.defaultValues("keyEncryptionAlgorithm"));
/**
* @type {OctetString}
* @desc encryptedKey
*/
this.encryptedKey = getParametersValue(parameters, "encryptedKey", PasswordRecipientinfo.defaultValues("encryptedKey"));
/**
* @type {ArrayBuffer}
* @desc password Password to derive key from
*/
this.password = getParametersValue(parameters, "password", PasswordRecipientinfo.defaultValues("password"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "version":
return (-1);
case "keyDerivationAlgorithm":
return new AlgorithmIdentifier();
case "keyEncryptionAlgorithm":
return new AlgorithmIdentifier();
case "encryptedKey":
return new asn1js.OctetString();
case "password":
return new ArrayBuffer(0);
default:
throw new Error(`Invalid member name for PasswordRecipientinfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
static compareWithDefault(memberName, memberValue)
{
switch(memberName)
{
case "version":
return (memberValue === (-1));
case "keyDerivationAlgorithm":
case "keyEncryptionAlgorithm":
return ((memberValue.algorithmId === "") && (("algorithmParams" in memberValue) === false));
case "encryptedKey":
return (memberValue.isEqual(PasswordRecipientinfo.defaultValues("encryptedKey")));
case "password":
return (memberValue.byteLength === 0);
default:
throw new Error(`Invalid member name for PasswordRecipientinfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PasswordRecipientInfo ::= SEQUENCE {
* version CMSVersion, -- Always set to 0
* keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
* encryptedKey EncryptedKey }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [keyDerivationAlgorithm]
* @property {string} [keyEncryptionAlgorithm]
* @property {string} [encryptedKey]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.version || "") }),
new asn1js.Constructed({
name: (names.keyDerivationAlgorithm || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: AlgorithmIdentifier.schema().valueBlock.value
}),
AlgorithmIdentifier.schema(names.keyEncryptionAlgorithm || {}),
new asn1js.OctetString({ name: (names.encryptedKey || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"version",
"keyDerivationAlgorithm",
"keyEncryptionAlgorithm",
"encryptedKey"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PasswordRecipientinfo.schema({
names: {
version: "version",
keyDerivationAlgorithm: "keyDerivationAlgorithm",
keyEncryptionAlgorithm: {
names: {
blockName: "keyEncryptionAlgorithm"
}
},
encryptedKey: "encryptedKey"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PasswordRecipientinfo");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result.version.valueBlock.valueDec;
if("keyDerivationAlgorithm" in asn1.result)
{
this.keyDerivationAlgorithm = new AlgorithmIdentifier({
schema: new asn1js.Sequence({
value: asn1.result.keyDerivationAlgorithm.valueBlock.value
})
});
}
this.keyEncryptionAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.keyEncryptionAlgorithm });
this.encryptedKey = asn1.result.encryptedKey;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create output array for sequence
const outputArray = [];
outputArray.push(new asn1js.Integer({ value: this.version }));
if("keyDerivationAlgorithm" in this)
{
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: this.keyDerivationAlgorithm.toSchema().valueBlock.value
}));
}
outputArray.push(this.keyEncryptionAlgorithm.toSchema());
outputArray.push(this.encryptedKey);
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
version: this.version,
keyDerivationAlgorithm: this.keyDerivationAlgorithm.toJSON(),
keyEncryptionAlgorithm: this.keyEncryptionAlgorithm.toJSON(),
encryptedKey: this.encryptedKey.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,216 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class PolicyConstraints
{
//**********************************************************************************
/**
* Constructor for PolicyConstraints class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
if("requireExplicitPolicy" in parameters)
/**
* @type {number}
* @desc requireExplicitPolicy
*/
this.requireExplicitPolicy = getParametersValue(parameters, "requireExplicitPolicy", PolicyConstraints.defaultValues("requireExplicitPolicy"));
if("inhibitPolicyMapping" in parameters)
/**
* @type {number}
* @desc Value of the TIME class
*/
this.inhibitPolicyMapping = getParametersValue(parameters, "inhibitPolicyMapping", PolicyConstraints.defaultValues("inhibitPolicyMapping"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "requireExplicitPolicy":
return 0;
case "inhibitPolicyMapping":
return 0;
default:
throw new Error(`Invalid member name for PolicyConstraints class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PolicyConstraints ::= SEQUENCE {
* requireExplicitPolicy [0] SkipCerts OPTIONAL,
* inhibitPolicyMapping [1] SkipCerts OPTIONAL }
*
* SkipCerts ::= INTEGER (0..MAX)
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [requireExplicitPolicy]
* @property {string} [inhibitPolicyMapping]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Primitive({
name: (names.requireExplicitPolicy || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
}
}), // IMPLICIT integer value
new asn1js.Primitive({
name: (names.inhibitPolicyMapping || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
}
}) // IMPLICIT integer value
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"requireExplicitPolicy",
"inhibitPolicyMapping"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PolicyConstraints.schema({
names: {
requireExplicitPolicy: "requireExplicitPolicy",
inhibitPolicyMapping: "inhibitPolicyMapping"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PolicyConstraints");
//endregion
//region Get internal properties from parsed schema
if("requireExplicitPolicy" in asn1.result)
{
const field1 = asn1.result.requireExplicitPolicy;
field1.idBlock.tagClass = 1; // UNIVERSAL
field1.idBlock.tagNumber = 2; // INTEGER
const ber1 = field1.toBER(false);
const int1 = asn1js.fromBER(ber1);
this.requireExplicitPolicy = int1.result.valueBlock.valueDec;
}
if("inhibitPolicyMapping" in asn1.result)
{
const field2 = asn1.result.inhibitPolicyMapping;
field2.idBlock.tagClass = 1; // UNIVERSAL
field2.idBlock.tagNumber = 2; // INTEGER
const ber2 = field2.toBER(false);
const int2 = asn1js.fromBER(ber2);
this.inhibitPolicyMapping = int2.result.valueBlock.valueDec;
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create correct values for output sequence
const outputArray = [];
if("requireExplicitPolicy" in this)
{
const int1 = new asn1js.Integer({ value: this.requireExplicitPolicy });
int1.idBlock.tagClass = 3; // CONTEXT-SPECIFIC
int1.idBlock.tagNumber = 0; // [0]
outputArray.push(int1);
}
if("inhibitPolicyMapping" in this)
{
const int2 = new asn1js.Integer({ value: this.inhibitPolicyMapping });
int2.idBlock.tagClass = 3; // CONTEXT-SPECIFIC
int2.idBlock.tagNumber = 1; // [1]
outputArray.push(int2);
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {};
if("requireExplicitPolicy" in this)
object.requireExplicitPolicy = this.requireExplicitPolicy;
if("inhibitPolicyMapping" in this)
object.inhibitPolicyMapping = this.inhibitPolicyMapping;
return object;
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,178 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import PolicyQualifierInfo from "./PolicyQualifierInfo.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class PolicyInformation
{
//**********************************************************************************
/**
* Constructor for PolicyInformation class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc policyIdentifier
*/
this.policyIdentifier = getParametersValue(parameters, "policyIdentifier", PolicyInformation.defaultValues("policyIdentifier"));
if("policyQualifiers" in parameters)
/**
* @type {Array.<PolicyQualifierInfo>}
* @desc Value of the TIME class
*/
this.policyQualifiers = getParametersValue(parameters, "policyQualifiers", PolicyInformation.defaultValues("policyQualifiers"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "policyIdentifier":
return "";
case "policyQualifiers":
return [];
default:
throw new Error(`Invalid member name for PolicyInformation class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PolicyInformation ::= SEQUENCE {
* policyIdentifier CertPolicyId,
* policyQualifiers SEQUENCE SIZE (1..MAX) OF
* PolicyQualifierInfo OPTIONAL }
*
* CertPolicyId ::= OBJECT IDENTIFIER
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [policyIdentifier]
* @property {string} [policyQualifiers]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.policyIdentifier || "") }),
new asn1js.Sequence({
optional: true,
value: [
new asn1js.Repeated({
name: (names.policyQualifiers || ""),
value: PolicyQualifierInfo.schema()
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"policyIdentifier",
"policyQualifiers"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PolicyInformation.schema({
names: {
policyIdentifier: "policyIdentifier",
policyQualifiers: "policyQualifiers"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PolicyInformation");
//endregion
//region Get internal properties from parsed schema
this.policyIdentifier = asn1.result.policyIdentifier.valueBlock.toString();
if("policyQualifiers" in asn1.result)
this.policyQualifiers = Array.from(asn1.result.policyQualifiers, element => new PolicyQualifierInfo({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(new asn1js.ObjectIdentifier({ value: this.policyIdentifier }));
if("policyQualifiers" in this)
{
outputArray.push(new asn1js.Sequence({
value: Array.from(this.policyQualifiers, element => element.toSchema())
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {
policyIdentifier: this.policyIdentifier
};
if("policyQualifiers" in this)
object.policyQualifiers = Array.from(this.policyQualifiers, element => element.toJSON());
return object;
}
//**********************************************************************************
}
//**************************************************************************************

148
core/third-party/pkijs/PolicyMapping.js vendored Normal file
View File

@@ -0,0 +1,148 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class PolicyMapping
{
//**********************************************************************************
/**
* Constructor for PolicyMapping class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc issuerDomainPolicy
*/
this.issuerDomainPolicy = getParametersValue(parameters, "issuerDomainPolicy", PolicyMapping.defaultValues("issuerDomainPolicy"));
/**
* @type {string}
* @desc subjectDomainPolicy
*/
this.subjectDomainPolicy = getParametersValue(parameters, "subjectDomainPolicy", PolicyMapping.defaultValues("subjectDomainPolicy"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "issuerDomainPolicy":
return "";
case "subjectDomainPolicy":
return "";
default:
throw new Error(`Invalid member name for PolicyMapping class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PolicyMapping ::= SEQUENCE {
* issuerDomainPolicy CertPolicyId,
* subjectDomainPolicy CertPolicyId }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [issuerDomainPolicy]
* @property {string} [subjectDomainPolicy]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.issuerDomainPolicy || "") }),
new asn1js.ObjectIdentifier({ name: (names.subjectDomainPolicy || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"issuerDomainPolicy",
"subjectDomainPolicy"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PolicyMapping.schema({
names: {
issuerDomainPolicy: "issuerDomainPolicy",
subjectDomainPolicy: "subjectDomainPolicy"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PolicyMapping");
//endregion
//region Get internal properties from parsed schema
this.issuerDomainPolicy = asn1.result.issuerDomainPolicy.valueBlock.toString();
this.subjectDomainPolicy = asn1.result.subjectDomainPolicy.valueBlock.toString();
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.issuerDomainPolicy }),
new asn1js.ObjectIdentifier({ value: this.subjectDomainPolicy })
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
issuerDomainPolicy: this.issuerDomainPolicy,
subjectDomainPolicy: this.subjectDomainPolicy
};
}
//**********************************************************************************
}
//**************************************************************************************

135
core/third-party/pkijs/PolicyMappings.js vendored Normal file
View File

@@ -0,0 +1,135 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import PolicyMapping from "./PolicyMapping.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class PolicyMappings
{
//**********************************************************************************
/**
* Constructor for PolicyMappings class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {Array.<PolicyMapping>}
* @desc mappings
*/
this.mappings = getParametersValue(parameters, "mappings", PolicyMappings.defaultValues("mappings"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "mappings":
return [];
default:
throw new Error(`Invalid member name for PolicyMappings class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF PolicyMapping
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [utcTimeName] Name for "utcTimeName" choice
* @property {string} [generalTimeName] Name for "generalTimeName" choice
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Repeated({
name: (names.mappings || ""),
value: PolicyMapping.schema()
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"mappings"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PolicyMappings.schema({
names: {
mappings: "mappings"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PolicyMappings");
//endregion
//region Get internal properties from parsed schema
this.mappings = Array.from(asn1.result.mappings, element => new PolicyMapping({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: Array.from(this.mappings, element => element.toSchema())
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
mappings: Array.from(this.mappings, element => element.toJSON())
};
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,154 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class PolicyQualifierInfo
{
//**********************************************************************************
/**
* Constructor for PolicyQualifierInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {string}
* @desc policyQualifierId
*/
this.policyQualifierId = getParametersValue(parameters, "policyQualifierId", PolicyQualifierInfo.defaultValues("policyQualifierId"));
/**
* @type {Object}
* @desc qualifier
*/
this.qualifier = getParametersValue(parameters, "qualifier", PolicyQualifierInfo.defaultValues("qualifier"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "policyQualifierId":
return "";
case "qualifier":
return new asn1js.Any();
default:
throw new Error(`Invalid member name for PolicyQualifierInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PolicyQualifierInfo ::= SEQUENCE {
* policyQualifierId PolicyQualifierId,
* qualifier ANY DEFINED BY policyQualifierId }
*
* id-qt OBJECT IDENTIFIER ::= { id-pkix 2 }
* id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 }
* id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 }
*
* PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [policyQualifierId]
* @property {string} [qualifier]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.ObjectIdentifier({ name: (names.policyQualifierId || "") }),
new asn1js.Any({ name: (names.qualifier || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"policyQualifierId",
"qualifier"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PolicyQualifierInfo.schema({
names: {
policyQualifierId: "policyQualifierId",
qualifier: "qualifier"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PolicyQualifierInfo");
//endregion
//region Get internal properties from parsed schema
this.policyQualifierId = asn1.result.policyQualifierId.valueBlock.toString();
this.qualifier = asn1.result.qualifier;
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
new asn1js.ObjectIdentifier({ value: this.policyQualifierId }),
this.qualifier
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
return {
policyQualifierId: this.policyQualifierId,
qualifier: this.qualifier.toJSON()
};
}
//**********************************************************************************
}
//**************************************************************************************

329
core/third-party/pkijs/PrivateKeyInfo.js vendored Normal file
View File

@@ -0,0 +1,329 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import Attribute from "./Attribute.js";
import ECPrivateKey from "./ECPrivateKey.js";
import RSAPrivateKey from "./RSAPrivateKey.js";
//**************************************************************************************
/**
* Class from RFC5208
*/
export default class PrivateKeyInfo
{
//**********************************************************************************
/**
* Constructor for PrivateKeyInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @desc version
*/
this.version = getParametersValue(parameters, "version", PrivateKeyInfo.defaultValues("version"));
/**
* @type {AlgorithmIdentifier}
* @desc privateKeyAlgorithm
*/
this.privateKeyAlgorithm = getParametersValue(parameters, "privateKeyAlgorithm", PrivateKeyInfo.defaultValues("privateKeyAlgorithm"));
/**
* @type {OctetString}
* @desc privateKey
*/
this.privateKey = getParametersValue(parameters, "privateKey", PrivateKeyInfo.defaultValues("privateKey"));
if("attributes" in parameters)
/**
* @type {Array.<Attribute>}
* @desc attributes
*/
this.attributes = getParametersValue(parameters, "attributes", PrivateKeyInfo.defaultValues("attributes"));
if("parsedKey" in parameters)
/**
* @type {ECPrivateKey|RSAPrivateKey}
* @desc Parsed public key value
*/
this.parsedKey = getParametersValue(parameters, "parsedKey", PrivateKeyInfo.defaultValues("parsedKey"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
//region If input argument array contains "json" for this object
if("json" in parameters)
this.fromJSON(parameters.json);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "version":
return 0;
case "privateKeyAlgorithm":
return new AlgorithmIdentifier();
case "privateKey":
return new asn1js.OctetString();
case "attributes":
return [];
case "parsedKey":
return {};
default:
throw new Error(`Invalid member name for PrivateKeyInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PrivateKeyInfo ::= SEQUENCE {
* version Version,
* privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}},
* privateKey PrivateKey,
* attributes [0] Attributes OPTIONAL }
*
* Version ::= INTEGER {v1(0)} (v1,...)
*
* PrivateKey ::= OCTET STRING
*
* Attributes ::= SET OF Attribute
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [version]
* @property {string} [privateKeyAlgorithm]
* @property {string} [privateKey]
* @property {string} [attributes]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.version || "") }),
AlgorithmIdentifier.schema(names.privateKeyAlgorithm || {}),
new asn1js.OctetString({ name: (names.privateKey || "") }),
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: [
new asn1js.Repeated({
name: (names.attributes || ""),
value: Attribute.schema()
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"version",
"privateKeyAlgorithm",
"privateKey",
"attributes"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PrivateKeyInfo.schema({
names: {
version: "version",
privateKeyAlgorithm: {
names: {
blockName: "privateKeyAlgorithm"
}
},
privateKey: "privateKey",
attributes: "attributes"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PrivateKeyInfo");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result.version.valueBlock.valueDec;
this.privateKeyAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.privateKeyAlgorithm });
this.privateKey = asn1.result.privateKey;
if("attributes" in asn1.result)
this.attributes = Array.from(asn1.result.attributes, element => new Attribute({ schema: element }));
switch(this.privateKeyAlgorithm.algorithmId)
{
case "1.2.840.113549.1.1.1": // RSA
{
const privateKeyASN1 = asn1js.fromBER(this.privateKey.valueBlock.valueHex);
if(privateKeyASN1.offset !== (-1))
this.parsedKey = new RSAPrivateKey({ schema: privateKeyASN1.result });
}
break;
case "1.2.840.10045.2.1": // ECDSA
if("algorithmParams" in this.privateKeyAlgorithm)
{
if(this.privateKeyAlgorithm.algorithmParams instanceof asn1js.ObjectIdentifier)
{
const privateKeyASN1 = asn1js.fromBER(this.privateKey.valueBlock.valueHex);
if(privateKeyASN1.offset !== (-1))
{
this.parsedKey = new ECPrivateKey({
namedCurve: this.privateKeyAlgorithm.algorithmParams.valueBlock.toString(),
schema: privateKeyASN1.result
});
}
}
}
break;
default:
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [
new asn1js.Integer({ value: this.version }),
this.privateKeyAlgorithm.toSchema(),
this.privateKey
];
if("attributes" in this)
{
outputArray.push(new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: Array.from(this.attributes, element => element.toSchema())
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
//region Return common value in case we do not have enough info fo making JWK
if(("parsedKey" in this) === false)
{
const object = {
version: this.version,
privateKeyAlgorithm: this.privateKeyAlgorithm.toJSON(),
privateKey: this.privateKey.toJSON()
};
if("attributes" in this)
object.attributes = Array.from(this.attributes, element => element.toJSON());
return object;
}
//endregion
//region Making JWK
const jwk = {};
switch(this.privateKeyAlgorithm.algorithmId)
{
case "1.2.840.10045.2.1": // ECDSA
jwk.kty = "EC";
break;
case "1.2.840.113549.1.1.1": // RSA
jwk.kty = "RSA";
break;
default:
}
const publicKeyJWK = this.parsedKey.toJSON();
for(const key of Object.keys(publicKeyJWK))
jwk[key] = publicKeyJWK[key];
return jwk;
//endregion
}
//**********************************************************************************
/**
* Convert JSON value into current object
* @param {Object} json
*/
fromJSON(json)
{
if("kty" in json)
{
switch(json.kty.toUpperCase())
{
case "EC":
this.parsedKey = new ECPrivateKey({ json });
this.privateKeyAlgorithm = new AlgorithmIdentifier({
algorithmId: "1.2.840.10045.2.1",
algorithmParams: new asn1js.ObjectIdentifier({ value: this.parsedKey.namedCurve })
});
break;
case "RSA":
this.parsedKey = new RSAPrivateKey({ json });
this.privateKeyAlgorithm = new AlgorithmIdentifier({
algorithmId: "1.2.840.113549.1.1.1",
algorithmParams: new asn1js.Null()
});
break;
default:
throw new Error(`Invalid value for "kty" parameter: ${json.kty}`);
}
this.privateKey = new asn1js.OctetString({ valueHex: this.parsedKey.toSchema().toBER(false) });
}
}
//**********************************************************************************
}
//**************************************************************************************

View File

@@ -0,0 +1,207 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class PrivateKeyUsagePeriod
{
//**********************************************************************************
/**
* Constructor for PrivateKeyUsagePeriod class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
if("notBefore" in parameters)
/**
* @type {Date}
* @desc notBefore
*/
this.notBefore = getParametersValue(parameters, "notBefore", PrivateKeyUsagePeriod.defaultValues("notBefore"));
if("notAfter" in parameters)
/**
* @type {Date}
* @desc notAfter
*/
this.notAfter = getParametersValue(parameters, "notAfter", PrivateKeyUsagePeriod.defaultValues("notAfter"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "notBefore":
return new Date();
case "notAfter":
return new Date();
default:
throw new Error(`Invalid member name for PrivateKeyUsagePeriod class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* PrivateKeyUsagePeriod OID ::= 2.5.29.16
*
* PrivateKeyUsagePeriod ::= SEQUENCE {
* notBefore [0] GeneralizedTime OPTIONAL,
* notAfter [1] GeneralizedTime OPTIONAL }
* -- either notBefore or notAfter MUST be present
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [notBefore]
* @property {string} [notAfter]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Primitive({
name: (names.notBefore || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
}
}),
new asn1js.Primitive({
name: (names.notAfter || ""),
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
}
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"notBefore",
"notAfter"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PrivateKeyUsagePeriod.schema({
names: {
notBefore: "notBefore",
notAfter: "notAfter"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PrivateKeyUsagePeriod");
//endregion
//region Get internal properties from parsed schema
if("notBefore" in asn1.result)
{
const localNotBefore = new asn1js.GeneralizedTime();
localNotBefore.fromBuffer(asn1.result.notBefore.valueBlock.valueHex);
this.notBefore = localNotBefore.toDate();
}
if("notAfter" in asn1.result)
{
const localNotAfter = new asn1js.GeneralizedTime({ valueHex: asn1.result.notAfter.valueBlock.valueHex });
localNotAfter.fromBuffer(asn1.result.notAfter.valueBlock.valueHex);
this.notAfter = localNotAfter.toDate();
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
if("notBefore" in this)
{
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
valueHex: (new asn1js.GeneralizedTime({ valueDate: this.notBefore })).valueBlock.valueHex
}));
}
if("notAfter" in this)
{
outputArray.push(new asn1js.Primitive({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
valueHex: (new asn1js.GeneralizedTime({ valueDate: this.notAfter })).valueBlock.valueHex
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const object = {};
if("notBefore" in this)
object.notBefore = this.notBefore;
if("notAfter" in this)
object.notAfter = this.notAfter;
return object;
}
//**********************************************************************************
}
//**************************************************************************************

311
core/third-party/pkijs/PublicKeyInfo.js vendored Normal file
View File

@@ -0,0 +1,311 @@
import * as asn1js from "./asn1.js";
import { getParametersValue, clearProps } from "./pvutils.js";
import { getCrypto } from "./common.js";
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
import ECPublicKey from "./ECPublicKey.js";
import RSAPublicKey from "./RSAPublicKey.js";
//**************************************************************************************
/**
* Class from RFC5280
*/
export default class PublicKeyInfo
{
//**********************************************************************************
/**
* Constructor for PublicKeyInfo class
* @param {Object} [parameters={}]
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {AlgorithmIdentifier}
* @desc Algorithm identifier
*/
this.algorithm = getParametersValue(parameters, "algorithm", PublicKeyInfo.defaultValues("algorithm"));
/**
* @type {BitString}
* @desc Subject public key value
*/
this.subjectPublicKey = getParametersValue(parameters, "subjectPublicKey", PublicKeyInfo.defaultValues("subjectPublicKey"));
if("parsedKey" in parameters)
/**
* @type {ECPublicKey|RSAPublicKey}
* @desc Parsed public key value
*/
this.parsedKey = getParametersValue(parameters, "parsedKey", PublicKeyInfo.defaultValues("parsedKey"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
//region If input argument array contains "json" for this object
if("json" in parameters)
this.fromJSON(parameters.json);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "algorithm":
return new AlgorithmIdentifier();
case "subjectPublicKey":
return new asn1js.BitString();
default:
throw new Error(`Invalid member name for PublicKeyInfo class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of pre-defined ASN.1 schema for current class
*
* ASN.1 schema:
* ```asn1
* SubjectPublicKeyInfo ::= Sequence {
* algorithm AlgorithmIdentifier,
* subjectPublicKey BIT STRING }
* ```
*
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [algorithm]
* @property {string} [subjectPublicKey]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
AlgorithmIdentifier.schema(names.algorithm || {}),
new asn1js.BitString({ name: (names.subjectPublicKey || "") })
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"algorithm",
"subjectPublicKey"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PublicKeyInfo.schema({
names: {
algorithm: {
names: {
blockName: "algorithm"
}
},
subjectPublicKey: "subjectPublicKey"
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for PublicKeyInfo");
//endregion
//region Get internal properties from parsed schema
this.algorithm = new AlgorithmIdentifier({ schema: asn1.result.algorithm });
this.subjectPublicKey = asn1.result.subjectPublicKey;
switch(this.algorithm.algorithmId)
{
case "1.2.840.10045.2.1": // ECDSA
if("algorithmParams" in this.algorithm)
{
if(this.algorithm.algorithmParams.constructor.blockName() === asn1js.ObjectIdentifier.blockName())
{
try
{
this.parsedKey = new ECPublicKey({
namedCurve: this.algorithm.algorithmParams.valueBlock.toString(),
schema: this.subjectPublicKey.valueBlock.valueHex
});
}
catch(ex){} // Could be a problems during recognision of internal public key data here. Let's ignore them.
}
}
break;
case "1.2.840.113549.1.1.1": // RSA
{
const publicKeyASN1 = asn1js.fromBER(this.subjectPublicKey.valueBlock.valueHex);
if(publicKeyASN1.offset !== (-1))
{
try
{
this.parsedKey = new RSAPublicKey({ schema: publicKeyASN1.result });
}
catch(ex){} // Could be a problems during recognision of internal public key data here. Let's ignore them.
}
}
break;
default:
}
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: [
this.algorithm.toSchema(),
this.subjectPublicKey
]
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
//region Return common value in case we do not have enough info fo making JWK
if(("parsedKey" in this) === false)
{
return {
algorithm: this.algorithm.toJSON(),
subjectPublicKey: this.subjectPublicKey.toJSON()
};
}
//endregion
//region Making JWK
const jwk = {};
switch(this.algorithm.algorithmId)
{
case "1.2.840.10045.2.1": // ECDSA
jwk.kty = "EC";
break;
case "1.2.840.113549.1.1.1": // RSA
jwk.kty = "RSA";
break;
default:
}
const publicKeyJWK = this.parsedKey.toJSON();
for(const key of Object.keys(publicKeyJWK))
jwk[key] = publicKeyJWK[key];
return jwk;
//endregion
}
//**********************************************************************************
/**
* Convert JSON value into current object
* @param {Object} json
*/
fromJSON(json)
{
if("kty" in json)
{
switch(json.kty.toUpperCase())
{
case "EC":
this.parsedKey = new ECPublicKey({ json });
this.algorithm = new AlgorithmIdentifier({
algorithmId: "1.2.840.10045.2.1",
algorithmParams: new asn1js.ObjectIdentifier({ value: this.parsedKey.namedCurve })
});
break;
case "RSA":
this.parsedKey = new RSAPublicKey({ json });
this.algorithm = new AlgorithmIdentifier({
algorithmId: "1.2.840.113549.1.1.1",
algorithmParams: new asn1js.Null()
});
break;
default:
throw new Error(`Invalid value for "kty" parameter: ${json.kty}`);
}
this.subjectPublicKey = new asn1js.BitString({ valueHex: this.parsedKey.toSchema().toBER(false) });
}
}
//**********************************************************************************
importKey(publicKey)
{
//region Initial variables
let sequence = Promise.resolve();
const _this = this;
//endregion
//region Initial check
if(typeof publicKey === "undefined")
return Promise.reject("Need to provide publicKey input parameter");
//endregion
//region Get a "crypto" extension
const crypto = getCrypto();
if(typeof crypto === "undefined")
return Promise.reject("Unable to create WebCrypto object");
//endregion
//region Export public key
sequence = sequence.then(() =>
crypto.exportKey("spki", publicKey));
//endregion
//region Initialize internal variables by parsing exported value
sequence = sequence.then(
/**
* @param {ArrayBuffer} exportedKey
*/
exportedKey =>
{
const asn1 = asn1js.fromBER(exportedKey);
try
{
_this.fromSchema(asn1.result);
}
catch(exception)
{
return Promise.reject("Error during initializing object from schema");
}
return undefined;
},
error => Promise.reject(`Error during exporting public key: ${error}`)
);
//endregion
return sequence;
}
//**********************************************************************************
}
//**************************************************************************************

Some files were not shown because too many files have changed in this diff Show More