diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9936285..33c4355 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,5 +1,5 @@ -# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions +# Runs on push to master: install deps, test, build, then publish to npm (if version bumped). +# Requires: NPM_TOKEN secret in repo Settings > Secrets and variables > Actions. name: NPM publish @@ -8,25 +8,37 @@ on: branches: [ master ] jobs: - build: - + build-and-publish: runs-on: ubuntu-latest strategy: matrix: - node-version: [18.x] + node-version: [22.x] steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - run: npm run build - - - id: publish - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ secrets.NPM_TOKEN}} - - if: steps.publish.outputs.type != 'none' - run: | - echo "Version changed: ${{ steps.publish.outputs.old-version }} => ${{ steps.publish.outputs.version }}" \ No newline at end of file + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install dependencies + run: npm install + + - name: Run tests + run: npm test + env: + HUSKY: 0 + + - name: Build + run: npm run build + + - name: Publish to npm + id: publish + uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ secrets.NPM_TOKEN }} + + - if: steps.publish.outputs.type != 'none' + run: | + echo "Published version ${{ steps.publish.outputs.version }} (previous: ${{ steps.publish.outputs.old-version }})" \ No newline at end of file diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 133ccfc..d4babe5 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: - node-version: [18.x] + node-version: [22.x] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} diff --git a/.versionrc.json b/.versionrc.json new file mode 100644 index 0000000..720676b --- /dev/null +++ b/.versionrc.json @@ -0,0 +1,19 @@ +{ + "types": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance Improvements" }, + { "type": "revert", "section": "Reverts" }, + { "type": "docs", "section": "Documentation", "hidden": false }, + { "type": "style", "section": "Styles", "hidden": true }, + { "type": "chore", "section": "Miscellaneous Chores", "hidden": true }, + { "type": "refactor", "section": "Code Refactoring", "hidden": false }, + { "type": "test", "section": "Tests", "hidden": true }, + { "type": "build", "section": "Build System", "hidden": false } + ], + "commitUrlFormat": "{{host}}/{{owner}}/{{repository}}/commit/{{hash}}", + "compareUrlFormat": "{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}", + "issueUrlFormat": "{{host}}/{{owner}}/{{repository}}/issues/{{id}}", + "userUrlFormat": "{{host}}/{{user}}", + "releaseCommitMessageFormat": "chore(release): {{currentTag}}" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c6ef58..e2a8ff6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,32 @@ # Changelog -All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +All notable changes to this project will be documented in this file. + +**Generating the changelog** + +- **From commits (since last tag):** `npm run changelog` — updates CHANGELOG.md from [conventional commits](https://www.conventionalcommits.org/) (e.g. `feat:`, `fix:`, `BREAKING CHANGE:`). +- **Full release (bump version + changelog + tag):** `npm run release -- --release-as major|minor|patch` — uses [standard-version](https://github.com/conventional-changelog/standard-version); bump and changelog are driven by commit messages. + +## [4.0.0](https://github.com/miladezzat/encrypt-rsa/compare/v3.3.0...v4.0.0) - 2025-02-01 + +### BREAKING CHANGES + +- **Async API**: All crypto methods now return Promises instead of synchronous values. Migrate by adding `await` or `.then()` to every call to: + - `encryptStringWithRsaPublicKey` + - `decryptStringWithRsaPrivateKey` + - `encrypt` + - `decrypt` + - `createPrivateAndPublicKeys` + - `encryptBufferWithRsaPublicKey` + - `decryptBufferWithRsaPrivateKey` +- **Entry points**: Package now ships separate Node and Web builds. `main`/`module`/`types` point to `./build/node/node/index.js`; `browser` and conditional `exports` point to the Web build. The old single entry `./build/index.js` is no longer published. +- **Buffer return type**: `decryptBufferWithRsaPrivateKey` return type is now `Promise` (was `Buffer`). In Node the runtime value is still a `Buffer`; use `Uint8Array` in shared or cross-environment code. + +### Features + +- **Node and Web builds**: Same package works in Node.js and in the browser. Conditional exports select the correct build; same `NodeRSA` class and `INodeRSA` interface in both environments. +- **Web Crypto (browser)**: Browser build uses the Web Crypto API (RSA-OAEP, SHA-1) for encrypt/decrypt with public/private key and for key generation. `encrypt(privateKey)` / `decrypt(publicKey)` throw in the browser (not supported by Web Crypto). +- **Shared interface**: Both builds implement the shared `INodeRSA` interface; buffer methods use `Uint8Array` in the type signature. ## [3.3.0](https://github.com/miladezzat/encrypt-rsa/compare/v3.1.0...v3.3.0) (2024-10-12) diff --git a/README.md b/README.md index bf74840..93a2398 100644 --- a/README.md +++ b/README.md @@ -1,237 +1,300 @@ # NodeRSA -**NodeRSA** is a library that provides easy-to-use methods for RSA encryption and decryption. It allows generating RSA key pairs, encrypting, and decrypting strings with RSA public and private keys. This library is ideal for secure data transmission, authentication systems, and any application requiring cryptographic security. +**NodeRSA** is a library that provides easy-to-use methods for RSA encryption and decryption. It supports **Node.js** and **browser** (web) with the same API. Generate RSA key pairs, encrypt and decrypt strings with public and private keys. Ideal for secure data transmission, authentication systems, and any application requiring cryptographic security. ## Installation + ```bash npm install encrypt-rsa -// OR +# OR yarn add encrypt-rsa ``` +## Breaking changes (vs 3.x) + +If you are upgrading from **3.x**, this release includes breaking changes. You should bump to **4.0.0** when publishing: + +1. **Async API** – All crypto methods now return **Promises** (sync → async). You must use `await` or `.then()`. + - **Before (3.x):** `const encrypted = nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey });` + - **After (4.x):** `const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey });` +2. **Entry points** – The package now has separate Node and Web builds. `main`/`module`/`types` point to the Node build; the `browser` field and conditional `exports` point to the Web build. If you required a specific path (e.g. `encrypt-rsa/build/index.js`), update to the new entry points or use the package root `encrypt-rsa`. +3. **Buffer methods** – `decryptBufferWithRsaPrivateKey` now returns `Promise` (type). In Node the runtime value is still a `Buffer` (extends `Uint8Array`). Prefer `Uint8Array` in types; avoid relying on `instanceof Buffer` in shared code. + +See [CHANGELOG.md](./CHANGELOG.md) for the full list of changes. + +## Node and Web (browser) + +One package, two environments: + +- **Node.js**: Uses the built-in `crypto` module. All crypto methods return **Promises** (async API). +- **Browser**: Uses the Web Crypto API. Same async API; bundlers resolve the web build via `exports` / `browser` field. + +You use the same import; the correct implementation is chosen at build/runtime: + +```ts +import NodeRSA from 'encrypt-rsa'; +``` + +- In Node (or when your bundler targets Node), you get the Node build. +- When your bundler targets the browser, you get the web build. + +**Browser note:** In the browser build, `encrypt(privateKey)` and `decrypt(publicKey)` are not supported (Web Crypto does not support that flow) and will throw. Use `encryptStringWithRsaPublicKey` / `decryptStringWithRsaPrivateKey` for encryption and decryption. + +### Same interface (Node and Web) + +Both the Node and Web builds expose the **same class and method signatures** (they implement the shared `INodeRSA` interface). You write the same code; only the resolved implementation changes: + +- **Same class:** `NodeRSA` +- **Same constructor:** `(publicKey?: string, privateKey?: string, modulusLength?: number)` +- **Same methods:** `encryptStringWithRsaPublicKey`, `decryptStringWithRsaPrivateKey`, `encrypt`, `decrypt`, `createPrivateAndPublicKeys`, `encryptBufferWithRsaPublicKey`, `decryptBufferWithRsaPrivateKey` +- **Same parameter and return types:** All crypto methods return `Promise<...>`; buffer methods use `Uint8Array` (in Node, `Buffer` extends `Uint8Array` so it works as well). + +See [docs/FEATURE_PARITY.md](docs/FEATURE_PARITY.md) for a feature-by-feature comparison of Node vs Web (including the browser limitation for `encrypt`/`decrypt` with private/public key). + ## Usage -**Importing the Library** + +### Example: Node.js ```ts +// Node (CommonJS or ESM) import NodeRSA from 'encrypt-rsa'; + +const nodeRSA = new NodeRSA(); +const { publicKey, privateKey } = await nodeRSA.createPrivateAndPublicKeys(2048); + +const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({ + text: 'Secret message', + publicKey, +}); +console.log('Encrypted:', encrypted); + +const decrypted = await nodeRSA.decryptStringWithRsaPrivateKey({ + text: encrypted, + privateKey, +}); +console.log('Decrypted:', decrypted); +``` + +### Example: Browser (Web) + +```ts +// Browser (ESM or bundled) – same API +import NodeRSA from 'encrypt-rsa'; + +const nodeRSA = new NodeRSA(); +const { publicKey, privateKey } = await nodeRSA.createPrivateAndPublicKeys(2048); + +const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({ + text: 'Secret message', + publicKey, +}); +console.log('Encrypted:', encrypted); + +const decrypted = await nodeRSA.decryptStringWithRsaPrivateKey({ + text: encrypted, + privateKey, +}); +console.log('Decrypted:', decrypted); ``` -## Creating an Instance +When your bundler targets the browser, it resolves the web build; the code above is unchanged. -You can create an instance of the NodeRSA class with optional public and private keys and modulus length. +### Creating an instance ```ts -const nodeRSA = new NodeRSA(publicKey, privateKey, modulusLength); +const nodeRSA = new NodeRSA(publicKey?, privateKey?, modulusLength?); ``` -## Generating RSA Key Pairs -To generate a new pair of RSA keys: +### Generating RSA key pairs + +All crypto methods return **Promises**. Use `await` or `.then()`: ```ts -const { publicKey, privateKey } = nodeRSA.createPrivateAndPublicKeys(modulusLength); +const { publicKey, privateKey } = await nodeRSA.createPrivateAndPublicKeys(modulusLength); console.log('Public Key:', publicKey); console.log('Private Key:', privateKey); ``` -## Encrypting and Decrypting Strings -### Encrypting with RSA Public Key +### Encrypting and decrypting strings + +#### Encrypt with public key, decrypt with private key + ```ts -const text = "Hello, World!"; -const encryptedString = nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey }); +const text = 'Hello, World!'; +const encryptedString = await nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey }); console.log('Encrypted:', encryptedString); -``` -### Decrypting with RSA Private Key -```ts -const decryptedString = nodeRSA.decryptStringWithRsaPrivateKey({ text: encryptedString, privateKey }); +const decryptedString = await nodeRSA.decryptStringWithRsaPrivateKey({ + text: encryptedString, + privateKey, +}); console.log('Decrypted:', decryptedString); ``` -### Encrypting with RSA Private Key +#### Encrypt with private key, decrypt with public key (Node only) + +In the **browser build**, these methods throw. In **Node**, they work: + ```ts -const text = "Hello, World!"; -const encryptedString = nodeRSA.encrypt({ text, privateKey }); +const encryptedString = await nodeRSA.encrypt({ text, privateKey }); console.log('Encrypted with Private Key:', encryptedString); + +const decryptedString = await nodeRSA.decrypt({ text: encryptedString, publicKey }); +console.log('Decrypted with Public Key:', decryptedString); ``` -### Decrypting with RSA Public Key +### Buffer encryption (same interface: `Uint8Array`) + +Both Node and Web use **`Uint8Array`** in the method signature. In Node, `Buffer` extends `Uint8Array`, so you can pass a `Buffer` as well. Return type is `Promise` in both environments. + ```ts -const decryptedString = nodeRSA.decrypt({ text: encryptedString, publicKey }); -console.log('Decrypted with Public Key:', decryptedString); +// Node +const buffer = Buffer.from('This is some binary data'); + +// Browser (or shared code) +const buffer = new TextEncoder().encode('This is some binary data'); + +const encryptedBuffer = await nodeRSA.encryptBufferWithRsaPublicKey(buffer, publicKey); +const decryptedBuffer = await nodeRSA.decryptBufferWithRsaPrivateKey( + encryptedBuffer, + privateKey +); + +// Node: decryptedBuffer is Buffer +// Browser: decryptedBuffer is Uint8Array +console.log( + decryptedBuffer instanceof Uint8Array + ? new TextDecoder().decode(decryptedBuffer) + : decryptedBuffer.toString() +); ``` ## API -### NodeRSA Class -### Constructor +### NodeRSA class + +#### Constructor + ```ts constructor(publicKey?: string, privateKey?: string, modulusLength?: number) ``` -- `publicKey`: Optional. The RSA public key. -- `privateKey`: Optional. The RSA private key. -- `modulusLength`: Optional. The modulus length for the RSA key pair (default is 2048). - -### Methods -1. `createPrivateAndPublicKeys(modulusLength: number = this.modulusLength): returnCreateKeys` - - Generates a new pair of RSA keys. - - `modulusLength`: Optional. The modulus length for the RSA key pair (default is the instance's modulus length). - - Returns an object containing the `publicKey` and `privateKey`. -2. `encryptStringWithRsaPublicKey(args: parametersOfEncrypt): string` - - Encrypts a string with the given RSA public key. - - `args`: Object containing `text` and optionally `publicKey`. - - Returns the encrypted string in base64 format. - -3. `decryptStringWithRsaPrivateKey(args: parametersOfDecrypt): string` - - Decrypts a string with the given RSA private key. - - `args`: Object containing `text` and optionally `privateKey`. - - Returns the decrypted string. - -4. `encrypt(args: parametersOfEncryptPrivate): string` - - Encrypts a string with the given RSA private key. - - `args`: Object containing `text` and optionally `privateKey`. - - Returns the encrypted string in base64 format. - - -5. `decrypt(args: parametersOfDecryptPublic): string` - - Decrypts a string with the given RSA public key. - - `args`: Object containing `text` and optionally `publicKey`. - - Returns the decrypted string. - -### Types -1. parametersOfEncrypt -```ts -{ - text: string; - publicKey?: string; -} -``` -2. parametersOfDecrypt -```ts -{ - text: string; - privateKey?: string; -} -``` -3. parametersOfEncryptPrivate -```ts -{ - text: string; - privateKey?: string; -} -``` -4. parametersOfDecryptPublic -```ts -{ - text: string; - publicKey?: string; -} -``` -5. returnCreateKeys -```ts -{ - publicKey: string; - privateKey: string; -} -``` -### Utilities -1. `convertKetToBase64(key: string): string` - Converts a given key to base64 format. +- `publicKey`: Optional. RSA public key (PEM). +- `privateKey`: Optional. RSA private key (PEM). +- `modulusLength`: Optional. Modulus length in bits (default 2048). -### Helper Functions -1. `encode` -Encodes a string to base64. +#### Methods (all crypto methods return `Promise<...>`) -2. `decode` -Decodes a base64 string. +| Method | Returns | Description | +|--------|---------|-------------| +| `createPrivateAndPublicKeys(modulusLength?)` | `Promise<{ publicKey, privateKey }>` | Generate RSA key pair (PEM). | +| `encryptStringWithRsaPublicKey(args)` | `Promise` | Encrypt with public key. | +| `decryptStringWithRsaPrivateKey(args)` | `Promise` | Decrypt with private key. | +| `encrypt(args)` | `Promise` | Encrypt with private key. **Node only.** | +| `decrypt(args)` | `Promise` | Decrypt with public key. **Node only.** | +| `encryptBufferWithRsaPublicKey(buffer, publicKey?)` | `Promise` | Encrypt buffer; returns base64 string. | +| `decryptBufferWithRsaPrivateKey(encryptedText, privateKey?)` | `Promise` | Decrypt to buffer (same type in Node and Web). | -### Use Cases +#### Parameter types -#### Secure Data Transmission +- `parametersOfEncrypt`: `{ text: string; publicKey?: string }` +- `parametersOfDecrypt`: `{ text: string; privateKey?: string }` +- `parametersOfEncryptPrivate`: `{ text: string; privateKey?: string }` +- `parametersOfDecryptPublic`: `{ text: string; publicKey?: string }` +- `returnCreateKeys`: `{ publicKey: string; privateKey: string }` -NodeRSA can be used to securely transmit sensitive data over insecure channels. Encrypt data with the recipient's public key before sending it. Only the recipient can decrypt the data with their private key. +### Algorithm and key format +- **RSA-OAEP** with **SHA-1** for encrypt/decrypt with public/private key (cross-compatible between Node and browser). +- Keys are **PEM** (SPKI for public, PKCS#8 for private). Keys generated on one side work on the other. + +## Use cases + +### Secure data transmission ```ts // Sender -const encryptedMessage = nodeRSA.encryptStringWithRsaPublicKey({ text: "Sensitive data", publicKey: recipientPublicKey }); -// Send `encryptedMessage` to the recipient +const encryptedMessage = await nodeRSA.encryptStringWithRsaPublicKey({ + text: 'Sensitive data', + publicKey: recipientPublicKey, +}); +// Send encryptedMessage to the recipient // Recipient -const decryptedMessage = nodeRSA.decryptStringWithRsaPrivateKey({ text: encryptedMessage, privateKey: recipientPrivateKey }); +const decryptedMessage = await nodeRSA.decryptStringWithRsaPrivateKey({ + text: encryptedMessage, + privateKey: recipientPrivateKey, +}); console.log('Decrypted Message:', decryptedMessage); ``` -#### Authentication Systems -NodeRSA can be used in authentication systems to encrypt credentials and sensitive information. - +### Authentication ```ts -// Encrypting user credentials -const encryptedCredentials = nodeRSA.encryptStringWithRsaPublicKey({ text: "username:password", publicKey: serverPublicKey }); - -// Decrypting credentials on the server -const decryptedCredentials = nodeRSA.decryptStringWithRsaPrivateKey({ text: encryptedCredentials, privateKey: serverPrivateKey }); +const encryptedCredentials = await nodeRSA.encryptStringWithRsaPublicKey({ + text: 'username:password', + publicKey: serverPublicKey, +}); + +const decryptedCredentials = await nodeRSA.decryptStringWithRsaPrivateKey({ + text: encryptedCredentials, + privateKey: serverPrivateKey, +}); console.log('Decrypted Credentials:', decryptedCredentials); ``` -### Buffer Encryption/Decryption Methods: +## Testing -1. `encryptBufferWithRsaPublicKey`: Converts a buffer to Base64 and then encrypts the Base64 string. -2. `decryptBufferWithRsaPrivateKey`: Decrypts the Base64 string and converts it back to a buffer. +The project includes tests for both the **Node** and **web** builds: -#### Example Usage -```ts -const nodeRSA = new NodeRSA(); +- **Node tests** (`tests/functionalty.node.spec.ts`): Run against the Node build; cover all methods including encrypt/decrypt with private/public key and buffer operations. +- **Web tests** (`tests/functionalty.web.spec.ts`): Run against the web build; require `crypto.subtle` (Node 19+ or a browser). Skipped automatically when Web Crypto is not available. -// Generate keys -const { publicKey, privateKey } = nodeRSA.createPrivateAndPublicKeys(); +```bash +npm test +``` -// Example buffer -const buffer = Buffer.from('This is some binary data'); +Both suites run with `npm test`. Web tests are skipped when `crypto.subtle` is not available (e.g. Node below 19). + +## Documentation -// Encrypt the buffer -const encryptedBuffer = nodeRSA.encryptBufferWithRsaPublicKey(buffer, publicKey); -console.log('Encrypted Buffer:', encryptedBuffer); +API documentation is generated with [Compodoc](https://compodoc.app). To generate the docs (from the Node build source): -// Decrypt back to buffer -const decryptedBuffer = nodeRSA.decryptBufferWithRsaPrivateKey(encryptedBuffer, privateKey); -console.log('Decrypted Buffer:', decryptedBuffer.toString()); // should log: 'This is some binary data' +```bash +npm run docs +``` + +Generated files are written to the `docs/` folder. To serve them locally: + +```bash +npm run docs:serve ``` +The docs reflect the **NodeRSA** class and its async API (Node and web share the same interface). -#### Digital Signatures +## Releasing -Although not directly covered by the current implementation, RSA can also be used for creating and verifying digital signatures to ensure data integrity and authenticity. +- **Changelog from commits:** Run `npm run changelog` to update [CHANGELOG.md](./CHANGELOG.md) from conventional commits since the last tag (`feat:`, `fix:`, `BREAKING CHANGE:`, etc.). +- **Full release:** Run `npm run release -- --release-as major|minor|patch` to bump version, update the changelog, commit, and tag. Push to `master` to trigger the publish workflow (see [.github/workflows/publish.yml](./.github/workflows/publish.yml)). +**Publish via GitHub Actions (on merge to `master`):** + +1. **Secret:** In the repo go to **Settings → Secrets and variables → Actions** and add **NPM_TOKEN** (npm automation token with “Publish” permission). +2. **Trigger:** Merging (or pushing) to the **master** branch runs the workflow: install → test → build → publish. The [JS-DevTools/npm-publish](https://github.com/JS-DevTools/npm-publish) action publishes only if the version in `package.json` is **greater** than the latest on npm; otherwise the job succeeds but skips publishing. +3. **Optional:** To make installs reproducible in CI, commit `package-lock.json` (remove it from `.gitignore`) and change the workflow step from `npm install` to `npm ci`. ## Contribution -We welcome contributions to the NodeRSA library! If you'd like to contribute, please follow these steps: 1. Fork the repository on GitHub. -2. Clone your forked repository to your local machine. -```bash -git clone git@github.com:miladezzat/encrypt-rsa.git -``` -3. Create a new branch for your feature or bugfix. -```bash -git checkout -b feature/your-feature-name -``` -4. Make your changes to the codebase. -5. Commit your changes with a clear and descriptive commit message. -```bash -git commit -m "Description of your feature or fix" -``` -6. Push your changes to your forked repository. -```bash -git push origin feature/your-feature-name -``` -7. Create a pull request on the original repository. Be sure to include a detailed description of your changes and the problem they solve. +2. Clone your fork: `git clone git@github.com:miladezzat/encrypt-rsa.git` +3. Create a branch: `git checkout -b feature/your-feature-name` +4. Make your changes, then commit with a clear message. +5. Push to your fork and open a pull request with a description of your changes. -## Code of Conduct -Please note that this project is released with a [Contributor Code of Conduct](./CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. +## Code of conduct +This project is released with a [Contributor Code of Conduct](./CODE_OF_CONDUCT.md). By participating you agree to abide by its terms. -## Reporting Issues -If you encounter any issues, please report them using the GitHub issue tracker. Include details about the problem and your environment (OS, Node.js version, etc.). +## Reporting issues -Thank you for contributing to NodeRSA! \ No newline at end of file +Please report issues via the [GitHub issue tracker](https://github.com/miladezzat/encrypt-rsa/issues). Include details about the problem and your environment (OS, Node.js version, bundler, etc.). diff --git a/docs/FEATURE_PARITY.md b/docs/FEATURE_PARITY.md new file mode 100644 index 0000000..85d8e7f --- /dev/null +++ b/docs/FEATURE_PARITY.md @@ -0,0 +1,34 @@ +# Feature parity: Node vs Web + +Both builds implement the same **INodeRSA** interface. This table shows what is implemented where and any limitations. + +| Feature | Node (`src/node/`) | Web (`src/web/`) | Notes | +|--------|--------------------|------------------|--------| +| **Constructor** `(publicKey?, privateKey?, modulusLength?)` | Yes | Yes | Same signature; default `modulusLength` 2048. | +| **encryptStringWithRsaPublicKey** `(args) → Promise` | Yes | Yes | Encrypt with public key. RSA-OAEP + SHA-1 in both. | +| **decryptStringWithRsaPrivateKey** `(args) → Promise` | Yes | Yes | Decrypt with private key. RSA-OAEP + SHA-1 in both. | +| **encrypt** `(args) → Promise` | Yes | **Throws** | Encrypt with **private** key. Web Crypto has no equivalent; web build throws with a clear message. | +| **decrypt** `(args) → Promise` | Yes | **Throws** | Decrypt with **public** key. Web Crypto has no equivalent; web build throws with a clear message. | +| **createPrivateAndPublicKeys** `(modulusLength?) → Promise<{ publicKey, privateKey }>` | Yes | Yes | PEM (SPKI + PKCS#8). Keys interchangeable between Node and Web. | +| **encryptBufferWithRsaPublicKey** `(buffer, publicKey?) → Promise` | Yes | Yes | Buffer → base64 string, then encrypt. Node: `Buffer`/`Uint8Array`; Web: `Uint8Array`. | +| **decryptBufferWithRsaPrivateKey** `(encryptedText, privateKey?) → Promise` | Yes | Yes | Decrypt then base64 → bytes. Node returns `Buffer` (extends `Uint8Array`); Web returns `Uint8Array`. | + +## Shared behavior + +- **Key format:** PEM (SPKI public, PKCS#8 private). Keys generated on one build work with the other for **encryptStringWithRsaPublicKey** / **decryptStringWithRsaPrivateKey** and **createPrivateAndPublicKeys**. +- **Algorithm:** RSA-OAEP with SHA-1 for public encrypt / private decrypt so ciphertext is compatible between Node and Web. +- **Key normalization:** Both use `convertKetToBase64` (from `shared` helpers) so keys can be passed with optional leading spaces. + +## Intended differences + +| Item | Node | Web | +|------|------|-----| +| **encrypt(privateKey)** / **decrypt(publicKey)** | Implemented (Node `crypto`). | Not supported; methods throw with a message directing users to the public/private encrypt/decrypt methods. | +| **Buffer type** | Accepts `Buffer \| Uint8Array`; returns `Buffer` from buffer methods. | Accepts `Uint8Array`; returns `Uint8Array`. | + +## Verification + +- **Node:** `tests/functionalty.node.spec.ts` (all 8 features covered, including encrypt/decrypt with private/public and buffers). +- **Web:** `tests/functionalty.web.spec.ts` (all supported features; encrypt/decrypt with private/public assert that they throw). + +Run both: `npm test`. diff --git a/docs/changelog.html b/docs/changelog.html index 7540a05..c8f69ca 100644 --- a/docs/changelog.html +++ b/docs/changelog.html @@ -61,6 +61,20 @@ } toggleDarkMode(darkModeState); +