diff --git a/README.md b/README.md
index 3493b57..4adbade 100644
--- a/README.md
+++ b/README.md
@@ -25,9 +25,9 @@ const decoded = base64Decode(encoded); // Restored.
```ts
const file = await Deno.readFile("/path/to/binary.bin");
-const hash = await deriveHash(false, file); // byte array of hash value.
-const keyEcdh = await generateKeyPair(false); // public/private key pair for ECDH, each in byte array.
-const keyEcdsa = await generateKeyPair(true); // public/private key pair for ECDSA, each in byte array.
+const hash = await cryptoHash(false, file); // byte array of hash value.
+const keyEcdh = await cryptoGenerateKey(false); // public/private key pair for ECDH, each in byte array.
+const keyEcdsa = await cryptoGenerateKey(true); // public/private key pair for ECDSA, each in byte array.
const encrypted = await cryptoEncrypt(keyEcdh, file); // encrypted byte array.
const decrypted = await cryptoDecrypt(keyEcdh, encrypted); // Restored.
const signature = await cryptoSign(keyEcdsa.privateKey, data); // signature byte array.
@@ -137,5 +137,18 @@ const {default: data} = await import("./data.json", {assert: {type: "json"}});
+# Browser Compatible
+Some methods and classes in this module don't use Deno objects internally and are browser compatible.
+
+I have prepared browser compatible code only export as [mod.compatible.ts](./mod.compatible.ts).
+
+By bundling this, you can easily create universal utility scripts.
+
+```sh
+deno bundle https://deno.land/x/simple_utility@(version)/mod.compatible.ts | esbuild --minify | head -c -1 | tee ./simple_utility.esm.min.js
+```
+
+The example uses [esbuild](https://esbuild.github.io) to minify.
+
# API
See [Deno Document](https://deno.land/x/simple_utility/mod.ts) for details.
\ No newline at end of file
diff --git a/src/crypto.ts b/src/crypto.ts
index 5bb32b4..cde26b4 100644
--- a/src/crypto.ts
+++ b/src/crypto.ts
@@ -6,23 +6,12 @@ export type PortableCryptoKey = Uint8Array;
/**
* Each is `PortableCryptoKey` public/private key pair.
*/
-export interface PortableCryptoKeyPair{
- privateKey: PortableCryptoKey;
- publicKey: PortableCryptoKey;
-}
+export type PortableCryptoKeyPair = Record;
-async function parseCommonKey(kp:PortableCryptoKeyPair){
+async function deriveSecretKey(kp:PortableCryptoKeyPair){
const ec:EcKeyAlgorithm = {
- namedCurve: "P-384",
- name: "ECDH"
- };
-
- const publicKey = await crypto.subtle.importKey("spki", kp.publicKey, ec, false, []);
- const privateKey = await crypto.subtle.importKey("pkcs8", kp.privateKey, ec, false, ["deriveKey", "deriveBits"]);
-
- const dh:EcdhKeyDeriveParams = {
name: "ECDH",
- public: publicKey
+ namedCurve: "P-384"
};
const aes:AesDerivedKeyParams = {
@@ -30,29 +19,25 @@ async function parseCommonKey(kp:PortableCryptoKeyPair){
length: 256
};
- return await crypto.subtle.deriveKey(dh, privateKey, aes, false, ["encrypt", "decrypt"]);
-}
-
-async function parseSignKey(k:PortableCryptoKey, isPrivate:boolean){
- const format:KeyFormat = isPrivate ? "pkcs8" : "spki";
- const usage:KeyUsage[] = isPrivate ? ["sign"] : ["verify"];
+ const publicKey = await crypto.subtle.importKey("spki", kp.publicKey, ec, false, []);
+ const privateKey = await crypto.subtle.importKey("pkcs8", kp.privateKey, ec, false, ["deriveKey", "deriveBits"]);
- const ec:EcKeyAlgorithm = {
- namedCurve: "P-384",
- name: "ECDSA"
+ const dh:EcdhKeyDeriveParams = {
+ name: "ECDH",
+ public: publicKey
};
- return await crypto.subtle.importKey(format, k, ec, false, usage);
+ return await crypto.subtle.deriveKey(dh, privateKey, aes, false, ["encrypt", "decrypt"]);
}
/**
* Derive SHA2 hash value from byte array.
-* @param isHalf Use the hash length 256 bits if `true`, 512 bits if `false`.
+* @param is256 Use the hash length 256 bits if `true`, 512 bits if `false`.
* @param data byte array.
* @return byte array of hash value.
*/
-export async function deriveHash(isHalf:boolean, data:Uint8Array){
- const sha = isHalf ? "SHA-256" : "SHA-512";
+export async function cryptoHash(is256:boolean, data:Uint8Array){
+ const sha = is256 ? "SHA-256" : "SHA-512";
return new Uint8Array(await crypto.subtle.digest(sha, data));
}
@@ -62,12 +47,12 @@ export async function deriveHash(isHalf:boolean, data:Uint8Array){
* @param isDsa Outputs the key for ECDSA if `true`, for ECDH if `false`.
* @return public/private key pair, each in byte array.
*/
-export async function generateKeyPair(isDsa:boolean){
+export async function cryptoGenerateKey(isDsa:boolean){
const usage:KeyUsage[] = isDsa ? ["sign", "verify"] : ["deriveKey", "deriveBits"];
const ec:EcKeyAlgorithm = {
- namedCurve: "P-384",
- name: isDsa ? "ECDSA" : "ECDH"
+ name: isDsa ? "ECDSA" : "ECDH",
+ namedCurve: "P-384"
};
const {publicKey, privateKey} = await crypto.subtle.generateKey(ec, true, usage);
@@ -95,11 +80,11 @@ export async function cryptoEncrypt(kp:PortableCryptoKeyPair, data:Uint8Array){
iv: crypto.getRandomValues(new Uint8Array(sizeIv))
};
- const commonKey = await parseCommonKey(kp);
+ const secretKey = await deriveSecretKey(kp);
const output = new Uint8Array(sizeTag + sizeIv + data.byteLength);
output.set(gcm.iv, 0);
- output.set(new Uint8Array(await crypto.subtle.encrypt(gcm, commonKey, data)), gcm.iv.byteLength);
+ output.set(new Uint8Array(await crypto.subtle.encrypt(gcm, secretKey, data)), gcm.iv.byteLength);
return output;
}
@@ -121,7 +106,7 @@ export async function cryptoDecrypt(kp:PortableCryptoKeyPair, data:Uint8Array){
iv: data.subarray(0, sizeIv)
};
- const commonKey = await parseCommonKey(kp);
+ const commonKey = await deriveSecretKey(kp);
return new Uint8Array(await crypto.subtle.decrypt(gcm, commonKey, data.subarray(sizeIv)));
}
@@ -133,6 +118,11 @@ export async function cryptoDecrypt(kp:PortableCryptoKeyPair, data:Uint8Array){
* @return signature byte array.
*/
export async function cryptoSign(k:PortableCryptoKey, data:Uint8Array){
+ const ec:EcKeyAlgorithm = {
+ name: "ECDSA",
+ namedCurve: "P-384"
+ };
+
const dsa:EcdsaParams = {
name: "ECDSA",
hash: {
@@ -140,7 +130,7 @@ export async function cryptoSign(k:PortableCryptoKey, data:Uint8Array){
}
};
- const privateKey = await parseSignKey(k, true);
+ const privateKey = await crypto.subtle.importKey("pkcs8", k, ec, false, ["sign"]);
return new Uint8Array(await crypto.subtle.sign(dsa, privateKey, data));
}
@@ -153,6 +143,11 @@ export async function cryptoSign(k:PortableCryptoKey, data:Uint8Array){
* @return `true` if correct.
*/
export async function cryptoVerify(signature:Uint8Array, k:PortableCryptoKey, data:Uint8Array){
+ const ec:EcKeyAlgorithm = {
+ name: "ECDSA",
+ namedCurve: "P-384"
+ };
+
const dsa:EcdsaParams = {
name: "ECDSA",
hash: {
@@ -160,7 +155,7 @@ export async function cryptoVerify(signature:Uint8Array, k:PortableCryptoKey, da
}
};
- const publicKey = await parseSignKey(k, false);
+ const publicKey = await crypto.subtle.importKey("spki", k, ec, false, ["verify"]);
return await crypto.subtle.verify(dsa, publicKey, signature, data);
}
\ No newline at end of file
diff --git a/src/minipack.ts b/src/minipack.ts
index 7e5438e..e485a4c 100644
--- a/src/minipack.ts
+++ b/src/minipack.ts
@@ -1,11 +1,11 @@
-import {deriveHash} from "./crypto.ts";
+import {cryptoHash} from "./crypto.ts";
import {ucEncode, ucDecode, hexEncode} from "./text.ts";
-const sizeOf = {
+const sizeOf = Object.freeze({
hash: 32,
name: 1,
body: 4
-};
+});
const sizeTotal = Object.values(sizeOf).reduce((a, c) => a + c, 0);
@@ -23,7 +23,7 @@ export async function minipackEncode(files:[string, Uint8Array][]){
const name = ucEncode(k);
const body = v;
- archive.set(await deriveHash(true, body), offset);
+ archive.set(await cryptoHash(true, body), offset);
offset += sizeOf.hash;
new DataView(archive.buffer, offset).setUint8(0, name.byteLength);
@@ -65,7 +65,7 @@ export async function minipackDecode(archive:Uint8Array){
const body = archive.subarray(offset, offset += bs);
- if(hexEncode(hash) !== hexEncode(await deriveHash(true, body))){
+ if(hexEncode(hash) !== hexEncode(await cryptoHash(true, body))){
throw new Error();
}
diff --git a/src/platform.ts b/src/platform.ts
index 40ba43a..b69139e 100644
--- a/src/platform.ts
+++ b/src/platform.ts
@@ -4,20 +4,14 @@ import {dirname, fromFileUrl} from "../deps.ts";
* @return `true` if running on Windows.
*/
export function isWin(){
- const os = Deno?.build.os;
-
- if(!os){
- throw new Error();
- }
-
- return os === "windows";
+ return Deno.build.os === "windows";
}
/**
* @return `"C:/Windows/Temp"` if running on Windows, or `"/tmp"` if running on Linux or Mac.
*/
export function tmpPath(){
- switch(Deno?.build.os){
+ switch(Deno.build.os){
case "windows": return "C:/Windows/Temp";
case "linux": return "/tmp";
case "darwin": return "/tmp";
@@ -29,11 +23,5 @@ export function tmpPath(){
* Move current directory to `Deno.mainModule`.
*/
export function cwdMain(){
- const ep = Deno?.mainModule;
-
- if(!ep){
- throw new Error();
- }
-
- Deno?.chdir(fromFileUrl(dirname(ep)));
+ Deno.chdir(fromFileUrl(dirname(Deno.mainModule)));
}
\ No newline at end of file
diff --git a/src/web.d.ts b/src/web.d.ts
index 7046177..7f2478e 100644
--- a/src/web.d.ts
+++ b/src/web.d.ts
@@ -1,7 +1,7 @@
/**
* Possible types of JSON.
*/
-export type JsonStruct = string | number | boolean | null | JsonStruct[] | {[key: string]: JsonStruct;};
+export type JsonStruct = string | number | boolean | null | JsonStruct[] | {[key in string]: JsonStruct};
/**
* Possible input types for `URLSearchParams`.
diff --git a/test/crypto.test.ts b/test/crypto.test.ts
index 3455027..943d828 100644
--- a/test/crypto.test.ts
+++ b/test/crypto.test.ts
@@ -1,5 +1,5 @@
import {assertEquals} from "../deps.test.ts";
-import {deriveHash, generateKeyPair, cryptoEncrypt, cryptoDecrypt, cryptoSign, cryptoVerify} from "../src/crypto.ts";
+import {cryptoHash, cryptoGenerateKey, cryptoEncrypt, cryptoDecrypt, cryptoSign, cryptoVerify} from "../src/crypto.ts";
const sample = new Uint8Array([0x02, 0xF2, 0x5D, 0x1F, 0x1C, 0x34, 0xB9, 0x2F]);
@@ -17,7 +17,7 @@ const hashResult = new Uint8Array([
Deno.test({
name: "Crypto: Hash",
async fn(){
- const hash = await deriveHash(false, sample);
+ const hash = await cryptoHash(false, sample);
assertEquals(hash, hashResult);
}
@@ -26,8 +26,8 @@ Deno.test({
Deno.test({
name: "Crypto: Encrypt and Decrypt",
async fn(){
- const key1 = await generateKeyPair(false);
- const key2 = await generateKeyPair(false);
+ const key1 = await cryptoGenerateKey(false);
+ const key2 = await cryptoGenerateKey(false);
const encrypt = await cryptoEncrypt({
publicKey: key1.publicKey,
@@ -46,7 +46,7 @@ Deno.test({
Deno.test({
name: "Crypto: Sign and Verify",
async fn(){
- const key = await generateKeyPair(true);
+ const key = await cryptoGenerateKey(true);
const signature = await cryptoSign(key.privateKey, sample);
const verify = await cryptoVerify(signature, key.publicKey, sample);