diff --git a/package.json b/package.json index aa6b85a..22ca14a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "node": ">=22.0.0" }, "scripts": { - "build": "rm -rf dist && tsc -p tsconfig.build.cjs.json && tsc -p tsconfig.build.esm.json", + "build": "rm -rf dist && tsc -p tsconfig.build.cjs.json && tsc -p tsconfig.build.esm.json && node ./scripts/write-dist-package-json.mjs", "format:check": "prettier -c \"src/**/*.ts\" \"test/**/*.ts\" \"demo/**/*.*\" *.json README.md", "format:write": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"demo/**/*.*\" *.json README.md", "lint": "eslint src/ test/", diff --git a/scripts/write-dist-package-json.mjs b/scripts/write-dist-package-json.mjs new file mode 100644 index 0000000..44366a5 --- /dev/null +++ b/scripts/write-dist-package-json.mjs @@ -0,0 +1,15 @@ +import { mkdir, writeFile } from 'node:fs/promises'; +import { join } from 'node:path'; + +// TypeScript emits `.js` files for both outputs, so each dist folder needs its +// own package boundary to tell Node whether to load those files as ESM or CJS. +const distDirs = [ + ['cjs', { type: 'commonjs' }], + ['esm', { type: 'module' }], +]; + +for (const [dir, pkg] of distDirs) { + const outDir = join(process.cwd(), 'dist', dir); + await mkdir(outDir, { recursive: true }); + await writeFile(join(outDir, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`); +} diff --git a/src/decryption/decryption.ts b/src/decryption/decryption.ts index a87d121..cca80c9 100644 --- a/src/decryption/decryption.ts +++ b/src/decryption/decryption.ts @@ -1,9 +1,16 @@ -import { cipher, util } from 'node-forge'; -import { EncodingVersions } from '../encoding-versions'; -import { EncryptionKey } from '../encryption-key'; -import { DerivedKeyOptions } from '../key-derivation/derived-key'; -import { CipherStrategy, strategyToAlgorithm } from '../strategies'; -import { binaryStringToBytesBuffer, bytesToBinaryString, deSerialize, encodeUtf8 } from '../util'; +import forge from 'node-forge'; +import { EncodingVersions } from '../encoding-versions.js'; +import { EncryptionKey } from '../encryption-key.js'; +import { DerivedKeyOptions } from '../key-derivation/derived-key.js'; +import { CipherStrategy, strategyToAlgorithm } from '../strategies.js'; +import { + binaryStringToBytesBuffer, + bytesToBinaryString, + deSerialize, + encodeUtf8, +} from '../util.js'; + +const { cipher, util } = forge; interface IEncryptionOptions { iv: string; diff --git a/src/digests/hmac-digest.ts b/src/digests/hmac-digest.ts index 3f35fd8..0048789 100644 --- a/src/digests/hmac-digest.ts +++ b/src/digests/hmac-digest.ts @@ -1,7 +1,7 @@ -import { Hex, hmac } from 'node-forge'; +import forge from 'node-forge'; -export function hmacSha256Digest(key: string, message: string): Hex { - const hm = hmac.create(); +export function hmacSha256Digest(key: string, message: string): string { + const hm = forge.hmac.create(); hm.start('sha256', key); hm.update(message); return hm.digest().toHex(); diff --git a/src/encryption-key.ts b/src/encryption-key.ts index bcb4120..fcaeb6d 100644 --- a/src/encryption-key.ts +++ b/src/encryption-key.ts @@ -1,5 +1,7 @@ -import { random } from 'node-forge'; -import { binaryStringToBytes, bytesToBinaryString, decodeSafe64, encodeSafe64 } from './util'; +import forge from 'node-forge'; +import { binaryStringToBytes, bytesToBinaryString, decodeSafe64, encodeSafe64 } from './util.js'; + +const { random } = forge; /** * SymmetricKey that can be used to encrypt and decrypt data diff --git a/src/encryption/encryption.ts b/src/encryption/encryption.ts index 772650f..24b2d74 100644 --- a/src/encryption/encryption.ts +++ b/src/encryption/encryption.ts @@ -1,10 +1,12 @@ -import { cipher as forgeCipher, random, util } from 'node-forge'; -import { EncryptionKey } from '../encryption-key'; -import { IRandomKeyOptions } from '../key-derivation/derived-key'; -import { generateDerivedKey } from '../key-derivation/pbkdf2-hmac'; -import { SerializationFormat } from '../serialization-versions'; -import { CipherStrategy } from '../strategies'; -import { binaryStringToBytesBuffer, serialize } from '../util'; +import forge from 'node-forge'; +import { EncryptionKey } from '../encryption-key.js'; +import { IRandomKeyOptions } from '../key-derivation/derived-key.js'; +import { generateDerivedKey } from '../key-derivation/pbkdf2-hmac.js'; +import { SerializationFormat } from '../serialization-versions.js'; +import { CipherStrategy } from '../strategies.js'; +import { binaryStringToBytesBuffer, serialize } from '../util.js'; + +const { cipher: forgeCipher, random, util } = forge; export interface IEncryptionOptionsWithoutKey { /*** diff --git a/src/index.ts b/src/index.ts index fe73301..d3f8806 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,15 +5,15 @@ if (typeof window !== 'undefined' && typeof (window).global === 'undefined' (window).global = window; } -export * from './decryption/decryption'; -export * from './encryption/encryption'; -export * from './key-derivation/derived-key'; -export * from './key-derivation/pbkdf2-hmac'; -export * from './key-pairs/rsa'; -export * from './signing/rsa-signature'; -export * from './strategies'; -export * from './encryption-key'; -export * from './digests/hmac-digest'; +export * from './decryption/decryption.js'; +export * from './encryption/encryption.js'; +export * from './key-derivation/derived-key.js'; +export * from './key-derivation/pbkdf2-hmac.js'; +export * from './key-pairs/rsa.js'; +export * from './signing/rsa-signature.js'; +export * from './strategies.js'; +export * from './encryption-key.js'; +export * from './digests/hmac-digest.js'; export { encode64, decode64, @@ -40,4 +40,4 @@ export { generateEncryptionVerificationArtifacts, keyLengthFromPublicKeyPem, keyLengthFromPrivateKeyPem, -} from './util'; +} from './util.js'; diff --git a/src/key-derivation/derived-key.ts b/src/key-derivation/derived-key.ts index dab466a..d9b751b 100644 --- a/src/key-derivation/derived-key.ts +++ b/src/key-derivation/derived-key.ts @@ -1,7 +1,7 @@ -import { md, pkcs5, random } from 'node-forge'; -import { EncodingVersions } from '../encoding-versions'; -import { EncryptionKey } from '../encryption-key'; -import { SerializationFormat } from '../serialization-versions'; +import forge from 'node-forge'; +import { EncodingVersions } from '../encoding-versions.js'; +import { EncryptionKey } from '../encryption-key.js'; +import { SerializationFormat } from '../serialization-versions.js'; import { binaryStringToBytes, binaryStringToBytesBuffer, @@ -9,7 +9,9 @@ import { deSerializeDerivedKeyOptions, encodeUtf8, serializeDerivedKeyOptions, -} from '../util'; +} from '../util.js'; + +const { md, pkcs5, random } = forge; /** * Most of these values are copied directly from the Ruby library diff --git a/src/key-derivation/pbkdf2-hmac.ts b/src/key-derivation/pbkdf2-hmac.ts index ef9fd65..a36f52e 100644 --- a/src/key-derivation/pbkdf2-hmac.ts +++ b/src/key-derivation/pbkdf2-hmac.ts @@ -1,5 +1,5 @@ -import { EncryptionKey } from '../encryption-key'; -import { DerivedKeyOptions, IRandomKeyOptions, KeyDerivationStrategy } from './derived-key'; +import { EncryptionKey } from '../encryption-key.js'; +import { DerivedKeyOptions, IRandomKeyOptions, KeyDerivationStrategy } from './derived-key.js'; /** * Given a password/phrase, derive a fixed-length key from it using Pbkdf2Hmac. diff --git a/src/key-pairs/rsa.ts b/src/key-pairs/rsa.ts index a18360c..17b1cc2 100644 --- a/src/key-pairs/rsa.ts +++ b/src/key-pairs/rsa.ts @@ -1,6 +1,9 @@ -import { pki } from 'node-forge'; -import { SerializationFormat } from '../serialization-versions'; -import { deSerialize, keyLengthFromPublicKeyPem, serialize } from '../util'; +import forge from 'node-forge'; +import type { pki as ForgePki } from 'node-forge'; +import { SerializationFormat } from '../serialization-versions.js'; +import { deSerialize, keyLengthFromPublicKeyPem, serialize } from '../util.js'; + +const { pki } = forge; export function generateRSAKeyPair( bits = 4096 @@ -28,8 +31,8 @@ export function encryptPrivateKeyWithPassword({ privateKeyPem: string; password: string; }) { - const publicKey = pki.privateKeyFromPem(privateKeyPem); - return pki.encryptRsaPrivateKey(publicKey, password); + const privateKey = pki.privateKeyFromPem(privateKeyPem); + return pki.encryptRsaPrivateKey(privateKey, password); } export async function encryptWithPublicKey( @@ -44,7 +47,7 @@ export async function encryptWithPublicKey( }, serializationFormat: SerializationFormat = SerializationFormat.latest_version ) { - const pk = pki.publicKeyFromPem(publicKeyPem) as pki.rsa.PublicKey; + const pk = pki.publicKeyFromPem(publicKeyPem) as ForgePki.rsa.PublicKey; const encrypted = pk.encrypt(data, scheme); const bitLength = keyLengthFromPublicKeyPem(publicKeyPem); @@ -95,6 +98,6 @@ export async function decryptWithPrivateKey({ encrypted: string; scheme?: RsaEncryptionScheme; }) { - const pk = pki.decryptRsaPrivateKey(privateKeyPem, password) as pki.rsa.PrivateKey; + const pk = pki.decryptRsaPrivateKey(privateKeyPem, password) as ForgePki.rsa.PrivateKey; return pk.decrypt(encrypted, scheme); } diff --git a/src/signing/rsa-signature.ts b/src/signing/rsa-signature.ts index 5289eaf..a457c51 100644 --- a/src/signing/rsa-signature.ts +++ b/src/signing/rsa-signature.ts @@ -1,11 +1,14 @@ -import { md, pki } from 'node-forge'; +import forge from 'node-forge'; +import type { pki as ForgePki } from 'node-forge'; import { binaryStringToBytes, bytesToBinaryString, decodeSafe64, encodeSafe64, keyLengthFromPrivateKeyPem, -} from '../util'; +} from '../util.js'; + +const { md, pki } = forge; export interface ISignature { signature: string; @@ -16,7 +19,7 @@ export interface ISignature { export function signWithPrivateKey(privateKeyPem: string, data: Uint8Array): ISignature { const mdDigest = md.sha256.create(); - const key = pki.privateKeyFromPem(privateKeyPem) as pki.rsa.PrivateKey; + const key = pki.privateKeyFromPem(privateKeyPem) as ForgePki.rsa.PrivateKey; mdDigest.update(bytesToBinaryString(data)); const signature = key.sign(mdDigest); const keySize = keyLengthFromPrivateKeyPem(privateKeyPem); @@ -52,7 +55,7 @@ export function loadRsaSignature(serializedPayload: string): ISignature { } export function verifyWithPublicKey(publicKeyPem: string, signatureObj: ISignature) { - const key = pki.publicKeyFromPem(publicKeyPem) as pki.rsa.PublicKey; + const key = pki.publicKeyFromPem(publicKeyPem) as ForgePki.rsa.PublicKey; const mdDigest = md.sha256.create(); mdDigest.update(bytesToBinaryString(signatureObj.data)); return key.verify(mdDigest.digest().bytes(), signatureObj.signature); diff --git a/src/util.ts b/src/util.ts index a37d2bf..15b1b65 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,10 +1,13 @@ import { BSON } from 'bson'; import { Buffer as _buffer } from 'buffer'; -import { pki, random, util } from 'node-forge'; +import forge from 'node-forge'; +import type { pki as ForgePki } from 'node-forge'; import { parse as yamlParse, stringify as yamlStringify } from 'yaml'; -import { IEncryptionArtifacts } from './encryption/encryption'; -import { ICryppoSerializationArtifacts, IDerivedKey } from './key-derivation/derived-key'; -import { SerializationFormat } from './serialization-versions'; +import { IEncryptionArtifacts } from './encryption/encryption.js'; +import { ICryppoSerializationArtifacts, IDerivedKey } from './key-derivation/derived-key.js'; +import { SerializationFormat } from './serialization-versions.js'; + +const { pki, random, util } = forge; // 65 is the version byte for encryption artefacts encoded with BSON const ENCRYPTION_ARTEFACTS_CURRENT_VERSION = 'A'; @@ -234,7 +237,7 @@ export function generateEncryptionVerificationArtifacts() { } export function keyLengthFromPublicKeyPem(publicKeyPem: string) { - const pk = pki.publicKeyFromPem(publicKeyPem) as pki.rsa.PublicKey; + const pk = pki.publicKeyFromPem(publicKeyPem) as ForgePki.rsa.PublicKey; // Undocumented functionality but was the only way I could find to get // key length out of the public key. // https://github.com/digitalbazaar/forge/blob/master/lib/rsa.js#L1244 @@ -243,7 +246,7 @@ export function keyLengthFromPublicKeyPem(publicKeyPem: string) { } export function keyLengthFromPrivateKeyPem(privateKey: string) { - const pk = pki.privateKeyFromPem(privateKey) as pki.rsa.PrivateKey; + const pk = pki.privateKeyFromPem(privateKey) as ForgePki.rsa.PrivateKey; // Undocumented functionality but was the only way I could find to get // key length out of the public key. // https://github.com/digitalbazaar/forge/blob/master/lib/rsa.js#L1244 diff --git a/test/compatibility/compatibility.test.ts b/test/compatibility/compatibility.test.ts index 22040a4..1eac52d 100644 --- a/test/compatibility/compatibility.test.ts +++ b/test/compatibility/compatibility.test.ts @@ -1,5 +1,5 @@ import { readFileSync } from 'fs'; -import { util } from 'node-forge'; +import forge from 'node-forge'; import { join } from 'path'; import { CipherStrategy, @@ -20,6 +20,8 @@ import { } from '../../src/util'; import Compat from './compat.json'; +const { util } = forge; + describe('compatiblity test for all cryppo port', () => { Object.values(Compat.encryption_with_derived_key).forEach((objToValidate: any, index) => { it(`${index}. can successfully decrypt with AES-GCM Encryption and diff --git a/test/signing/rsa-signature.spec.ts b/test/signing/rsa-signature.spec.ts index eac889b..222a0bb 100644 --- a/test/signing/rsa-signature.spec.ts +++ b/test/signing/rsa-signature.spec.ts @@ -1,5 +1,5 @@ import { readFileSync } from 'fs'; -import { util } from 'node-forge'; +import forge from 'node-forge'; import { join } from 'path'; import { generateRSAKeyPair } from '../../src/key-pairs/rsa'; import { @@ -14,6 +14,8 @@ import { utf8ToBytes, } from '../../src/util'; +const { util } = forge; + describe('signing', () => { const data = 'Sign me! 񵿁R冎𵵖HخGؼ𝕖ƶ򠈛#؉�櫙㢍̇扑󺥈揥񠥥C񫉫𐣚ˏϹ끣Ϧ{󘕌󶨇󫊶ᣏ܊㋅i͸񦉑s蹇ҏ)6LJ𺘘ⶴ襰Յ󠪐쵏q;kbͥ%T껕̶ݸh齉͌.瘟D־򆷦ɍéʛڴʼǭ񤻲j򑶭';