Removed project-specific AGENTS.md and moved info to new CRYPTO_IMPLEMENTATION.md file.
This commit is contained in:
parent
e94dc4f291
commit
e99ec57806
59
AGENTS.md
59
AGENTS.md
@ -1,59 +0,0 @@
|
||||
# AGENTS.md
|
||||
|
||||
Guidance for AI agents (Claude Code, etc.) working on the **jsrsasign removal** in this repo.
|
||||
|
||||
Read [plan-jsrsasign.md](plan-jsrsasign.md) for the full migration plan. This file captures cross-PR conventions and workflow that must survive between sessions.
|
||||
|
||||
## Workflow
|
||||
|
||||
- **One PR per session.** Six PRs total — see the plan's "Phased plan" section. Don't try to fuse phases; each PR has its own test bundle that acts as the correctness gate.
|
||||
- **Stop at "ready to commit." The human handles git.** Do all the implementation, fixture updates, lint/test/build runs, and plan updates — but do NOT `git add`, `git commit`, `git push`, or `gh pr create`. Leave the working tree dirty and hand back a summary of what's staged-worthy. Leon commits and opens the PR himself.
|
||||
- **At the end of each session, update [plan-jsrsasign.md](plan-jsrsasign.md):**
|
||||
- Tick the PR in the "Status" block at the top.
|
||||
- Add an entry to the "Changelog" section at the bottom for any deviation from the original plan (chosen API differed, extra dep added, test fixture updated, gotcha discovered, scope adjusted).
|
||||
- Leave a one-line "Notes for next session" if anything is partially done or worth flagging.
|
||||
- **Per-PR verification (must all pass before handing back):**
|
||||
- `npm run lint`
|
||||
- `npm test`
|
||||
- `npm run build`
|
||||
- `grep -rn "from \"jsrsasign\"" src/core/` — count strictly decreases from the previous PR
|
||||
|
||||
## Library decisions (don't relitigate)
|
||||
|
||||
- **ECDSA: `@noble/curves`, NOT Web Crypto.** Web Crypto refuses MD5/SHA-1 digests, which the existing UI exposes. `@noble/hashes/legacy` provides MD5/SHA-1.
|
||||
- **X.509/CSR/CRL: `@peculiar/x509`** (plus `@peculiar/asn1-*` schemas as needed). Not `pkijs`.
|
||||
- **SM2: `@noble/curves/sm2`.** Not `sm-crypto`.
|
||||
- **DSA in `PubKeyFromPrivKey`: keep using `node-forge`** (already a dep, no new lib for a single op).
|
||||
- **Generic ASN.1 dump: `asn1js`** (transitive via `@peculiar/x509` anyway).
|
||||
- The "Not adopted, with reasons" list in the plan is final — don't reopen these choices without user input.
|
||||
|
||||
## Cross-PR coding conventions
|
||||
|
||||
- **PEM line endings: `\n` only.** No `\r\n`. The old jsrsasign output used `\r\n` in places; tests have been (or will be) updated to expect `\n`.
|
||||
- **Hex coord padding: `.padStart(64, "0")`** after `bigint.toString(16)` for SM2/P-256 point coords. **P-521 uses 66 bytes (132 hex chars)**, not 64.
|
||||
- **JWK field order:** build the object literal in this exact order so `JSON.stringify` emits it correctly: `{ kty, crv, x, y, d? }` for EC, `{ kty, n, e, d?, p?, q?, dp?, dq?, qi? }` for RSA. Insertion order is the serialization order.
|
||||
- **ECDSA r/s leading-zero quirk:** `parseSigHexInHexRS` historically prepends `00` to r or s when the MSB is set (DER 2's-complement artefact). Replicate this — existing tests depend on it.
|
||||
- **RFC 6979 determinism:** signature outputs *should* match jsrsasign byte-for-byte. If they diverge for a curve+digest combo, the signature is still valid — update the fixture and note it in the PR's changelog entry. Don't try to massage `@noble/curves` into matching.
|
||||
- **Cosmetic drift in golden text outputs is accepted** for `ParseX509Certificate`, `ParseCSR`, `ParseX509CRL`, `ParseASN1HexString`. Update fixtures. Note in CHANGELOG in PR 6.
|
||||
- **Cryptographic correctness is NOT negotiable.** SM2 ciphertext→plaintext fixtures in [tests/operations/tests/SM2.mjs](tests/operations/tests/SM2.mjs) must pass unchanged — those pin actual crypto behavior, not formatting.
|
||||
|
||||
## Shared helper modules
|
||||
|
||||
Created in PR 1 and PR 3. Use these instead of duplicating logic across operations:
|
||||
|
||||
- [src/core/lib/Asn1.mjs](src/core/lib/Asn1.mjs) (PR 1): `oidHexToInt`, `oidIntToHex`, `derToPem`, `dumpAsn1Hex`.
|
||||
- [src/core/lib/Ecdsa.mjs](src/core/lib/Ecdsa.mjs) (PR 3): `loadEcKey`, `signEcdsa`, `verifyEcdsa`, signature-format converters, `isAsn1Hex`, `generateEcKeyPair`.
|
||||
- [src/core/lib/PublicKey.mjs](src/core/lib/PublicKey.mjs) (extended in PR 5): `formatDnObj` accepts both legacy and `@peculiar/x509` `JsonName` shapes.
|
||||
- [src/core/lib/SM2.mjs](src/core/lib/SM2.mjs) (rewritten in PR 2): preserves both GMT 0009 BBB and GMT 0010 C1C2C3/C1C3C2 ciphertext layouts.
|
||||
|
||||
## Key file locations
|
||||
|
||||
- Operations being migrated: [src/core/operations/](src/core/operations/) — 14 files, listed in the plan.
|
||||
- Tests: [tests/operations/tests/](tests/operations/tests/) — golden fixtures live here.
|
||||
- Dependency manifest: [package.json](package.json).
|
||||
|
||||
## When in doubt
|
||||
|
||||
- Check the gotcha section of the relevant PR in [plan-jsrsasign.md](plan-jsrsasign.md) before writing code.
|
||||
- If you discover something the plan didn't anticipate, add a Changelog entry — don't silently work around it.
|
||||
- If a test fixture needs updating, decide: is it cosmetic drift (OK, update it) or cryptographic divergence (stop and surface to the user)?
|
||||
136
CRYPTO_IMPLEMENTATION.md
Normal file
136
CRYPTO_IMPLEMENTATION.md
Normal file
@ -0,0 +1,136 @@
|
||||
# Cryptography Implementation Guide
|
||||
|
||||
This document captures the architectural decisions and coding conventions established during the migration away from `jsrsasign`. It serves as a reference for understanding library choices, implementation patterns, and maintenance of cryptographic operations.
|
||||
|
||||
## Library Choices
|
||||
|
||||
The following libraries were selected for specific cryptographic operations. These decisions prioritize compatibility with the existing CyberChef API surface and cryptographic correctness.
|
||||
|
||||
### ECDSA: `@noble/curves`
|
||||
|
||||
Uses `@noble/curves` with `@noble/hashes/legacy` for MD5/SHA-1 digests.
|
||||
|
||||
**Why not Web Crypto?** The Web Crypto API refuses MD5 and SHA-1 digests, which the CyberChef UI exposes to users. `@noble/curves` with legacy hash support maintains this functionality.
|
||||
|
||||
### X.509 / CSR / CRL: `@peculiar/x509`
|
||||
|
||||
Uses `@peculiar/x509` for certificate, CSR, and CRL parsing and generation, with `@peculiar/asn1-*` schema packages as needed.
|
||||
|
||||
**Why not pkijs?** `@peculiar/x509` offers a cleaner API better suited to CyberChef's operation model.
|
||||
|
||||
### SM2: `@noble/curves/sm2`
|
||||
|
||||
Uses the SM2 implementation from `@noble/curves`.
|
||||
|
||||
**Why not sm-crypto?** `@noble/curves` provides a reliable, actively maintained SM2 implementation.
|
||||
|
||||
### DSA: `node-forge`
|
||||
|
||||
The `PubKeyFromPrivKey` operation continues to use `node-forge` for DSA key generation.
|
||||
|
||||
**Why?** `node-forge` is already a project dependency, and DSA is a single, localized operation not worth introducing a new library for.
|
||||
|
||||
### Generic ASN.1 Dump: `asn1js`
|
||||
|
||||
Uses `asn1js` for generic ASN.1 hex structure dumping.
|
||||
|
||||
**Note:** This is a transitive dependency of `@peculiar/x509` and requires no additional imports for most use cases.
|
||||
|
||||
## Coding Conventions
|
||||
|
||||
These conventions ensure consistency across cryptographic operations and maintain compatibility with existing test fixtures.
|
||||
|
||||
### PEM Line Endings
|
||||
|
||||
Use `\n` only for PEM-encoded output. Do not use `\r\n`.
|
||||
|
||||
The legacy `jsrsasign` output used `\r\n` in some cases. All test fixtures have been updated to expect `\n` exclusively.
|
||||
|
||||
### Hex Coordinate Padding
|
||||
|
||||
When converting elliptic curve point coordinates from `bigint` to hex:
|
||||
|
||||
- **P-256 and SM2:** Use `.padStart(64, "0")` (32 bytes = 64 hex characters)
|
||||
- **P-521:** Use `.padStart(132, "0")` (66 bytes = 132 hex characters)
|
||||
|
||||
Apply this after calling `bigint.toString(16)`.
|
||||
|
||||
### JWK Field Ordering
|
||||
|
||||
Build JWK objects using literal syntax in this exact order so `JSON.stringify` emits fields in the correct sequence:
|
||||
|
||||
**Elliptic Curve:**
|
||||
```javascript
|
||||
{ kty, crv, x, y, d? }
|
||||
```
|
||||
|
||||
**RSA:**
|
||||
```javascript
|
||||
{ kty, n, e, d?, p?, q?, dp?, dq?, qi? }
|
||||
```
|
||||
|
||||
JavaScript preserves insertion order for object properties, and downstream consumers may depend on this ordering.
|
||||
|
||||
### ECDSA r/s Leading-Zero Quirk
|
||||
|
||||
The `parseSigHexInHexRS` function historically prepends `00` to the r or s component when its most significant bit is set. This is a quirk from DER 2's-complement encoding.
|
||||
|
||||
**Replicate this behavior.** Existing test fixtures depend on it, and changing it breaks compatibility.
|
||||
|
||||
### RFC 6979 Determinism
|
||||
|
||||
ECDSA signature outputs from `@noble/curves` *should* match `jsrsasign` byte-for-byte when using the same curve and digest algorithm.
|
||||
|
||||
**If they diverge:** The signature is still cryptographically valid. Update the test fixture and document the divergence in the changelog. Do not attempt to massage the library output to force a match.
|
||||
|
||||
### Golden Output Cosmetic Drift
|
||||
|
||||
For text-based operations that dump parsed structures (`ParseX509Certificate`, `ParseCSR`, `ParseX509CRL`, `ParseASN1HexString`), minor formatting differences between `jsrsasign` and the new implementation are acceptable.
|
||||
|
||||
**Action:** Update the test fixture and note the change in the changelog.
|
||||
|
||||
### Cryptographic Correctness is Non-Negotiable
|
||||
|
||||
Fixture data that exercises actual cryptographic operations (e.g., SM2 ciphertext→plaintext transformations in [tests/operations/tests/SM2.mjs](tests/operations/tests/SM2.mjs)) must pass unchanged. These fixtures pin real cryptographic behavior and cannot be updated cosmetically.
|
||||
|
||||
## Shared Helper Modules
|
||||
|
||||
To avoid duplicating logic across operations, use these utility modules:
|
||||
|
||||
### [src/core/lib/Asn1.mjs](src/core/lib/Asn1.mjs)
|
||||
|
||||
ASN.1 utilities for OID and DER manipulation:
|
||||
- `oidHexToInt()` — convert ASN.1 OID hex encoding to integer dotted notation
|
||||
- `oidIntToHex()` — convert integer dotted notation to ASN.1 OID hex
|
||||
- `derToPem()` — wrap DER bytes in PEM format
|
||||
- `dumpAsn1Hex()` — generic ASN.1 hex structure dump (uses `asn1js`)
|
||||
|
||||
### [src/core/lib/Ecdsa.mjs](src/core/lib/Ecdsa.mjs)
|
||||
|
||||
ECDSA operations with `@noble/curves`:
|
||||
- `loadEcKey()` — parse EC private/public keys from PEM or JWK
|
||||
- `signEcdsa()` — sign data with an EC private key
|
||||
- `verifyEcdsa()` — verify an ECDSA signature
|
||||
- Signature format converters (ASN.1 DER ↔ r/s hex format)
|
||||
- `isAsn1Hex()` — detect ASN.1 DER-encoded data
|
||||
- `generateEcKeyPair()` — generate a new EC key pair
|
||||
|
||||
### [src/core/lib/PublicKey.mjs](src/core/lib/PublicKey.mjs)
|
||||
|
||||
Public key utilities and DN formatting. The `formatDnObj()` function handles both legacy jsrsasign DN objects and `@peculiar/x509` `JsonName` shapes for backward compatibility.
|
||||
|
||||
### [src/core/lib/SM2.mjs](src/core/lib/SM2.mjs)
|
||||
|
||||
SM2 encryption and decryption with support for multiple ciphertext layouts:
|
||||
- GMT 0009 BBB format
|
||||
- GMT 0010 C1C2C3 format
|
||||
- GMT 0010 C1C3C2 format
|
||||
|
||||
Preserves compatibility with existing test fixtures for all three layouts.
|
||||
|
||||
## Key File Locations
|
||||
|
||||
- **Cryptographic operations:** [src/core/operations/](src/core/operations/)
|
||||
- **Test fixtures:** [tests/operations/tests/](tests/operations/tests/) — golden output fixtures live here
|
||||
- **Dependencies:** [package.json](package.json)
|
||||
- **Library modules:** [src/core/lib/](src/core/lib/)
|
||||
Loading…
x
Reference in New Issue
Block a user