Compare commits
2 Commits
d271b59da4
...
7e6b8a0eba
Author | SHA1 | Date | |
---|---|---|---|
7e6b8a0eba | |||
fd0a0e06b0 |
@ -55,11 +55,25 @@ let encIV = encForm.createMediumTextBox({
|
|||||||
label: "IV",
|
label: "IV",
|
||||||
dataType: "b64",
|
dataType: "b64",
|
||||||
advanced: true,
|
advanced: true,
|
||||||
enabledFunc: function() {return encManualIV.value}
|
enabledFunc: function() {return encManualIV.value},
|
||||||
|
visibleFunc: function() {return ["AES-GCM", "AES-CBC"].includes(encMode.value)}
|
||||||
});
|
});
|
||||||
let encManualIV = encForm.createCheckBox({
|
let encManualIV = encForm.createCheckBox({
|
||||||
label: "Use fixed IV instead of random",
|
label: "Use fixed IV instead of random",
|
||||||
advanced: true
|
advanced: true,
|
||||||
|
visibleFunc: function() {return ["AES-GCM", "AES-CBC"].includes(encMode.value)}
|
||||||
|
});
|
||||||
|
let encCounter = encForm.createMediumTextBox({
|
||||||
|
label: "Counter",
|
||||||
|
dataType: "b64",
|
||||||
|
advanced: true,
|
||||||
|
enabledFunc: function() {return encManualCounter.value},
|
||||||
|
visibleFunc: function() {return encMode.value === "AES-CTR"}
|
||||||
|
});
|
||||||
|
let encManualCounter = encForm.createCheckBox({
|
||||||
|
label: "Use fixed counter instead of random",
|
||||||
|
advanced: true,
|
||||||
|
visibleFunc: function() {return encMode.value === "AES-CTR"}
|
||||||
});
|
});
|
||||||
let encMode = encForm.createDropDown({
|
let encMode = encForm.createDropDown({
|
||||||
label: "AES mode",
|
label: "AES mode",
|
||||||
@ -73,6 +87,10 @@ let encMode = encForm.createDropDown({
|
|||||||
name: "AES-CBC (Cipher Block Chaining)",
|
name: "AES-CBC (Cipher Block Chaining)",
|
||||||
value: "AES-CBC"
|
value: "AES-CBC"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "AES-CTR (Counter)",
|
||||||
|
value: "AES-CTR"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
let encButton = encForm.createButton({label: "Encrypt"});
|
let encButton = encForm.createButton({label: "Encrypt"});
|
||||||
@ -159,6 +177,17 @@ async function aesCbcEnc(key, iv, msgEncoded) {
|
|||||||
msgEncoded
|
msgEncoded
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
async function aesCtrEnc(key, counter, msgEncoded) {
|
||||||
|
return window.crypto.subtle.encrypt(
|
||||||
|
{
|
||||||
|
"name": "AES-CTR",
|
||||||
|
"counter": counter,
|
||||||
|
"length": 64
|
||||||
|
},
|
||||||
|
key,
|
||||||
|
msgEncoded
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
encButton.handle.addEventListener("click", async function() {
|
encButton.handle.addEventListener("click", async function() {
|
||||||
let keyMaterial = await getKeyMaterial(encPass.value);
|
let keyMaterial = await getKeyMaterial(encPass.value);
|
||||||
@ -192,11 +221,23 @@ encButton.handle.addEventListener("click", async function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let iv;
|
let iv;
|
||||||
if (encManualIV.value) {
|
if (["AES-GCM", "AES-CBC"].includes(encMode.value)) {
|
||||||
iv = encIV.value;
|
if (encManualIV.value) {
|
||||||
} else {
|
iv = encIV.value;
|
||||||
iv = window.crypto.getRandomValues(new Uint8Array(16));
|
} else {
|
||||||
encIV.value = iv;
|
iv = window.crypto.getRandomValues(new Uint8Array(16));
|
||||||
|
encIV.value = iv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let counter;
|
||||||
|
if (encMode.value === "AES-CTR") {
|
||||||
|
if (encManualCounter.value) {
|
||||||
|
counter = encCounter.value;
|
||||||
|
} else {
|
||||||
|
counter = window.crypto.getRandomValues(new Uint8Array(16));
|
||||||
|
encCounter.value = counter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let enc = new TextEncoder();
|
let enc = new TextEncoder();
|
||||||
@ -210,6 +251,9 @@ encButton.handle.addEventListener("click", async function() {
|
|||||||
case "AES-CBC":
|
case "AES-CBC":
|
||||||
ciphertext = await aesCbcEnc(key, iv, msgEncoded);
|
ciphertext = await aesCbcEnc(key, iv, msgEncoded);
|
||||||
break;
|
break;
|
||||||
|
case "AES-CTR":
|
||||||
|
ciphertext = await aesCtrEnc(key, counter, msgEncoded);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
let e = Error(`Mode '${encMode.value}' is not implemented.`);
|
let e = Error(`Mode '${encMode.value}' is not implemented.`);
|
||||||
encMode.handleError(e);
|
encMode.handleError(e);
|
||||||
@ -222,6 +266,7 @@ encButton.handle.addEventListener("click", async function() {
|
|||||||
"ciphertext": bufToB64(ciphertext),
|
"ciphertext": bufToB64(ciphertext),
|
||||||
"salt": bufToB64(salt),
|
"salt": bufToB64(salt),
|
||||||
"iv": bufToB64(iv),
|
"iv": bufToB64(iv),
|
||||||
|
"counter": bufToB64(counter),
|
||||||
"encMode": encMode.value,
|
"encMode": encMode.value,
|
||||||
"pbkdf2Iters": pbkdf2Iters,
|
"pbkdf2Iters": pbkdf2Iters,
|
||||||
}
|
}
|
||||||
@ -247,14 +292,26 @@ async function aesCbcDec(key, iv, ciphertext) {
|
|||||||
ciphertext
|
ciphertext
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
async function aesCtrDec(key, counter, ciphertext) {
|
||||||
|
return window.crypto.subtle.decrypt(
|
||||||
|
{
|
||||||
|
"name": "AES-CTR",
|
||||||
|
"counter": counter,
|
||||||
|
"length": 64
|
||||||
|
},
|
||||||
|
key,
|
||||||
|
ciphertext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
decButton.handle.addEventListener("click", async function() {
|
decButton.handle.addEventListener("click", async function() {
|
||||||
let msgEncoded = decMsg.value;
|
let msgEncoded = decMsg.value;
|
||||||
|
|
||||||
let ciphertext, iv, salt, encMode, pbkdf2Iters;
|
let ciphertext, iv, counter, salt, encMode, pbkdf2Iters;
|
||||||
try {
|
try {
|
||||||
ciphertext = new b64ToBuf(msgEncoded.ciphertext);
|
ciphertext = new b64ToBuf(msgEncoded.ciphertext);
|
||||||
iv = new Uint8Array(b64ToBuf(msgEncoded.iv));
|
iv = new Uint8Array(b64ToBuf(msgEncoded.iv));
|
||||||
|
counter = new Uint8Array(b64ToBuf(msgEncoded.counter));
|
||||||
salt = new Uint8Array(b64ToBuf(msgEncoded.salt));
|
salt = new Uint8Array(b64ToBuf(msgEncoded.salt));
|
||||||
encMode = msgEncoded.encMode;
|
encMode = msgEncoded.encMode;
|
||||||
pbkdf2Iters = msgEncoded.pbkdf2Iters;
|
pbkdf2Iters = msgEncoded.pbkdf2Iters;
|
||||||
@ -303,6 +360,9 @@ decButton.handle.addEventListener("click", async function() {
|
|||||||
case "AES-CBC":
|
case "AES-CBC":
|
||||||
plaintext = await aesCbcDec(key, iv, ciphertext);
|
plaintext = await aesCbcDec(key, iv, ciphertext);
|
||||||
break;
|
break;
|
||||||
|
case "AES-CTR":
|
||||||
|
plaintext = await aesCtrDec(key, counter, ciphertext);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw Error(`Mode '${encMode.value}' is not implemented.`);
|
throw Error(`Mode '${encMode.value}' is not implemented.`);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|||||||
class InterfaceElement {
|
class InterfaceElement {
|
||||||
rootNodes = [];
|
rootNodes = [];
|
||||||
|
|
||||||
constructor({fragment, enabledFunc}) {
|
constructor({fragment, enabledFunc, visibleFunc}) {
|
||||||
if (fragment === undefined) {
|
if (fragment === undefined) {
|
||||||
this.fragment = new DocumentFragment();
|
this.fragment = new DocumentFragment();
|
||||||
} else {
|
} else {
|
||||||
@ -27,6 +27,16 @@ class InterfaceElement {
|
|||||||
} else {
|
} else {
|
||||||
this.enabledFunc = enabledFunc;
|
this.enabledFunc = enabledFunc;
|
||||||
}
|
}
|
||||||
|
if (visibleFunc === undefined) {
|
||||||
|
this.visibleFunc = function(){ return true; };
|
||||||
|
} else {
|
||||||
|
this.visibleFunc = visibleFunc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.enabled = this.enabledFunc();
|
||||||
|
this.hidden = !this.visibleFunc();
|
||||||
}
|
}
|
||||||
|
|
||||||
scanNodes() {
|
scanNodes() {
|
||||||
@ -128,11 +138,7 @@ class Form extends InterfaceElement {
|
|||||||
this.#advanced = x;
|
this.#advanced = x;
|
||||||
for (const element of this.elements) {
|
for (const element of this.elements) {
|
||||||
if (element.advanced === true) {
|
if (element.advanced === true) {
|
||||||
if (this.advanced === true) {
|
element.update();
|
||||||
element.hidden = false;
|
|
||||||
} else {
|
|
||||||
element.hidden = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,7 +149,9 @@ class Form extends InterfaceElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appendElement(elem) {
|
createElem(params) {
|
||||||
|
params.form = this;
|
||||||
|
let elem = new FormElement(params);
|
||||||
elem.mount(this.handle);
|
elem.mount(this.handle);
|
||||||
this.elements.push(elem);
|
this.elements.push(elem);
|
||||||
this.rootNodes.push(...elem.rootNodes);
|
this.rootNodes.push(...elem.rootNodes);
|
||||||
@ -160,27 +168,27 @@ class Form extends InterfaceElement {
|
|||||||
let labelTag = document.createTextNode(params.label);
|
let labelTag = document.createTextNode(params.label);
|
||||||
params.tag.appendChild(labelTag);
|
params.tag.appendChild(labelTag);
|
||||||
params.label = undefined;
|
params.label = undefined;
|
||||||
return this.appendElement(new FormElement(params));
|
return this.createElem(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
createTextBox(params) {
|
createTextBox(params) {
|
||||||
params.tag = document.createElement("input");
|
params.tag = document.createElement("input");
|
||||||
dataTypeSupports(params, ["plaintext", "b64", "json-b64"]);
|
dataTypeSupports(params, ["plaintext", "b64", "json-b64"]);
|
||||||
return this.appendElement(new FormElement(params));
|
return this.createElem(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
createMediumTextBox(params) {
|
createMediumTextBox(params) {
|
||||||
params.tag = document.createElement("textarea");
|
params.tag = document.createElement("textarea");
|
||||||
params.tag.classList.add("mediumbox")
|
params.tag.classList.add("mediumbox")
|
||||||
dataTypeSupports(params, ["plaintext", "b64", "json-b64"]);
|
dataTypeSupports(params, ["plaintext", "b64", "json-b64"]);
|
||||||
return this.appendElement(new FormElement(params));
|
return this.createElem(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
createPasswordInput(params) {
|
createPasswordInput(params) {
|
||||||
params.tag = document.createElement("input");
|
params.tag = document.createElement("input");
|
||||||
params.tag.setAttribute("type", "password");
|
params.tag.setAttribute("type", "password");
|
||||||
dataTypeSupports(params, ["plaintext"]);
|
dataTypeSupports(params, ["plaintext"]);
|
||||||
return this.appendElement(new FormElement(params));
|
return this.createElem(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
createNumberInput(params) {
|
createNumberInput(params) {
|
||||||
@ -191,7 +199,7 @@ class Form extends InterfaceElement {
|
|||||||
if (params.minValue !== undefined) params.tag.min = params.minValue;
|
if (params.minValue !== undefined) params.tag.min = params.minValue;
|
||||||
if (params.step !== undefined) params.tag.step = params.step;
|
if (params.step !== undefined) params.tag.step = params.step;
|
||||||
if (params.required !== undefined) params.tag.required = params.required;
|
if (params.required !== undefined) params.tag.required = params.required;
|
||||||
return this.appendElement(new FormElement(params));
|
return this.createElem(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
createDropDown(params) {
|
createDropDown(params) {
|
||||||
@ -221,13 +229,18 @@ class Form extends InterfaceElement {
|
|||||||
optTag.appendChild(document.createTextNode(option.name));
|
optTag.appendChild(document.createTextNode(option.name));
|
||||||
params.tag.appendChild(optTag);
|
params.tag.appendChild(optTag);
|
||||||
}
|
}
|
||||||
return this.appendElement(new FormElement(params));
|
params.tag.addEventListener("change", function() {
|
||||||
|
for (const elem of this.elements) {
|
||||||
|
elem.update();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
return this.createElem(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
createTextArea(params) {
|
createTextArea(params) {
|
||||||
params.tag = document.createElement("textarea");
|
params.tag = document.createElement("textarea");
|
||||||
dataTypeSupports(params, ["plaintext", "b64", "json-b64"]);
|
dataTypeSupports(params, ["plaintext", "b64", "json-b64"]);
|
||||||
return this.appendElement(new FormElement(params));
|
return this.createElem(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
createButton(params) {
|
createButton(params) {
|
||||||
@ -237,7 +250,7 @@ class Form extends InterfaceElement {
|
|||||||
params.tag.appendChild(params.labelTag);
|
params.tag.appendChild(params.labelTag);
|
||||||
params.fragment.appendChild(params.tag);
|
params.fragment.appendChild(params.tag);
|
||||||
dataTypeSupports(params, ["none"]);
|
dataTypeSupports(params, ["none"]);
|
||||||
return this.appendElement(new FormElement(params));
|
return this.createElem(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
createCheckBox(params) {
|
createCheckBox(params) {
|
||||||
@ -254,17 +267,17 @@ class Form extends InterfaceElement {
|
|||||||
dataTypeSupports(params, ["bool"]);
|
dataTypeSupports(params, ["bool"]);
|
||||||
params.tag.addEventListener("change", function() {
|
params.tag.addEventListener("change", function() {
|
||||||
for (const elem of this.elements) {
|
for (const elem of this.elements) {
|
||||||
elem.enabled = elem.enabledFunc();
|
elem.update();
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
return this.appendElement(new FormElement(params));
|
return this.createElem(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
createOutput(params) {
|
createOutput(params) {
|
||||||
params.tag = document.createElement("textarea");
|
params.tag = document.createElement("textarea");
|
||||||
params.tag.setAttribute("readonly", true);
|
params.tag.setAttribute("readonly", true);
|
||||||
dataTypeSupports(params, ["plaintext", "b64", "json-b64"]);
|
dataTypeSupports(params, ["plaintext", "b64", "json-b64"]);
|
||||||
return this.appendElement(new FormElement(params));
|
return this.createElem(params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,8 +301,36 @@ function bufToB64 (buf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FormElement extends InterfaceElement {
|
class FormElement extends InterfaceElement {
|
||||||
constructor({tag, labelTag, label="", value, fragment, dataType, advanced=false, enabled=true, enabledFunc}) {
|
constructor({tag, fragment, advanced=false, form,
|
||||||
super({fragment, enabled, enabledFunc});
|
value, dataType,
|
||||||
|
labelTag, label="",
|
||||||
|
enabled=true, enabledFunc,
|
||||||
|
visibleFunc
|
||||||
|
}) {
|
||||||
|
let oriVisibleFunc = visibleFunc;
|
||||||
|
|
||||||
|
super({
|
||||||
|
fragment,
|
||||||
|
enabledFunc,
|
||||||
|
visibleFunc: function() {
|
||||||
|
let res;
|
||||||
|
if (oriVisibleFunc) {
|
||||||
|
res = oriVisibleFunc()
|
||||||
|
} else {
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form !== undefined) {
|
||||||
|
if (advanced) {
|
||||||
|
if (form.advanced) return res;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.form = form;
|
||||||
|
|
||||||
this.labelText = label;
|
this.labelText = label;
|
||||||
if (labelTag === undefined) {
|
if (labelTag === undefined) {
|
||||||
@ -307,8 +348,6 @@ class FormElement extends InterfaceElement {
|
|||||||
this.dataType = dataType;
|
this.dataType = dataType;
|
||||||
this.advanced = advanced;
|
this.advanced = advanced;
|
||||||
|
|
||||||
if (this.advanced === true) this.hidden = true;
|
|
||||||
|
|
||||||
if (value !== undefined) this.value = value;
|
if (value !== undefined) this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user