Skip to content

Commit 0ba8d48

Browse files
committed
chore: wip
1 parent e39935e commit 0ba8d48

16 files changed

Lines changed: 1202 additions & 0 deletions

File tree

packages/pem/README.md

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
<p align="center"><img src="../../.github/art/cover.jpg" alt="Social Card of this repo"></p>
2+
3+
[![npm version][npm-version-src]][npm-version-href]
4+
[![GitHub Actions][github-actions-src]][github-actions-href]
5+
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
6+
<!-- [![npm downloads][npm-downloads-src]][npm-downloads-href] -->
7+
<!-- [![Codecov][codecov-src]][codecov-href] -->
8+
9+
# ts-pki
10+
11+
> A TypeScript implementation of the SHA family of cryptographic hash functions with a focus on type safety, security, and performance.
12+
13+
## Features
14+
15+
- 🔒 **Comprehensive SHA Implementation**
16+
- SHA-1 _(legacy, not recommended for security-critical applications)_
17+
- SHA-256 _(part of the SHA-2 family)_
18+
- SHA-512 with variants _(SHA-384, SHA-512/256, SHA-512/224)_
19+
20+
- 🛡️ **Secure Implementation**
21+
- Follows NIST standards and specifications
22+
- Passes standard test vectors
23+
- Handles edge cases properly
24+
25+
- 🎯 **Type Safety**
26+
- Full TypeScript support
27+
- Comprehensive type definitions
28+
- Strict type checking
29+
30+
- 🧰 **Flexible API**
31+
- Incremental hashing support
32+
- Multiple digest formats
33+
- UTF-8 encoding support
34+
- ByteStringBuffer input support
35+
36+
## Install
37+
38+
```bash
39+
# bun
40+
bun install ts-pki
41+
42+
# npm
43+
npm install ts-pki
44+
45+
# pnpm
46+
pnpm install ts-pki
47+
```
48+
49+
## Get Started
50+
51+
After installing the package, you can import and use the various hash functions:
52+
53+
```ts
54+
import { sha1, sha256, sha512, sha384, sha512_256, sha512_224 } from 'ts-pki'
55+
56+
// SHA-1 Hashing (legacy, not recommended for security-critical applications)
57+
const md1 = sha1.create()
58+
md1.update('Hello, World!')
59+
const hash1 = md1.digest().toHex()
60+
61+
// SHA-256 Hashing
62+
const md2 = sha256.create()
63+
md2.update('Hello, World!')
64+
const hash2 = md2.digest().toHex()
65+
66+
// SHA-512 Hashing
67+
const md3 = sha512.create()
68+
md3.update('Hello, World!')
69+
const hash3 = md3.digest().toHex()
70+
71+
// SHA-384 Hashing
72+
const md4 = sha384
73+
md4.update('Hello, World!')
74+
const hash4 = md4.digest().toHex()
75+
76+
// SHA-512/256 Hashing
77+
const md5 = sha512_256
78+
md5.update('Hello, World!')
79+
const hash5 = md5.digest().toHex()
80+
81+
// SHA-512/224 Hashing
82+
const md6 = sha512_224
83+
md6.update('Hello, World!')
84+
const hash6 = md6.digest().toHex()
85+
86+
// Incremental hashing
87+
const md = sha256.create()
88+
md.update('Part 1 of the message')
89+
md.update('Part 2 of the message')
90+
md.update('Part 3 of the message')
91+
const hash = md.digest().toHex()
92+
```
93+
94+
## API Reference
95+
96+
### Common Interface
97+
98+
All hash functions implement the `MessageDigest` interface:
99+
100+
```ts
101+
interface MessageDigest {
102+
algorithm: string; // The name of the algorithm (e.g., 'sha256')
103+
blockLength: number; // The block size in bytes
104+
digestLength: number; // The digest size in bytes
105+
messageLength: number; // The current message length
106+
107+
// Resets the hash state
108+
start(): MessageDigest;
109+
110+
// Updates the hash with new data
111+
update(msg: string | ByteStringBuffer, encoding?: string): MessageDigest;
112+
113+
// Finalizes the hash computation and returns the digest
114+
digest(): ByteStringBuffer;
115+
}
116+
```
117+
118+
### SHA-1
119+
120+
```ts
121+
const md = sha1.create();
122+
```
123+
124+
- Block size: 64 bytes
125+
- Digest size: 20 bytes (160 bits)
126+
127+
### SHA-256
128+
129+
```ts
130+
const md = sha256.create();
131+
```
132+
133+
- Block size: 64 bytes
134+
- Digest size: 32 bytes (256 bits)
135+
136+
### SHA-512
137+
138+
```ts
139+
const md = sha512.create();
140+
```
141+
142+
- Block size: 128 bytes
143+
- Digest size: 64 bytes (512 bits)
144+
145+
### SHA-384
146+
147+
```ts
148+
const md = sha384;
149+
```
150+
151+
- Block size: 128 bytes
152+
- Digest size: 48 bytes (384 bits)
153+
154+
### SHA-512/256
155+
156+
```ts
157+
const md = sha512_256;
158+
```
159+
160+
- Block size: 128 bytes
161+
- Digest size: 32 bytes (256 bits)
162+
163+
### SHA-512/224
164+
165+
```ts
166+
const md = sha512_224;
167+
```
168+
169+
- Block size: 128 bytes
170+
- Digest size: 28 bytes (224 bits)
171+
172+
## Testing
173+
174+
```bash
175+
bun test
176+
```
177+
178+
## Changelog
179+
180+
Please see our [releases](https://github.com/stacksjs/ts-pki/releases) page for more information on what has changed recently.
181+
182+
## Contributing
183+
184+
Please review the [Contributing Guide](https://github.com/stacksjs/contributing) for details.
185+
186+
## Community
187+
188+
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
189+
190+
[Discussions on GitHub](https://github.com/stacksjs/stacks/discussions)
191+
192+
For casual chit-chat with others using this package:
193+
194+
[Join the Stacks Discord Server](https://discord.gg/stacksjs)
195+
196+
## Postcardware
197+
198+
"Software that is free, but hopes for a postcard." We love receiving postcards from around the world showing where `ts-pki` is being used! We showcase them on our website too.
199+
200+
Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States 🌎
201+
202+
## Sponsors
203+
204+
We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.
205+
206+
- [JetBrains](https://www.jetbrains.com/)
207+
- [The Solana Foundation](https://solana.com/)
208+
209+
## Credits
210+
211+
- [Dave Longley](https://github.com/dlongley)
212+
- [node-forge](https://github.com/digitalbazaar/forge)
213+
- [Chris Breuer](https://github.com/chrisbbreuer)
214+
- [All Contributors](../../contributors)
215+
216+
## License
217+
218+
The MIT License (MIT). Please see [LICENSE](https://github.com/stacksjs/stacks/tree/main/LICENSE.md) for more information.
219+
220+
Made with 💙
221+
222+
<!-- Badges -->
223+
[npm-version-src]: https://img.shields.io/npm/v/@stacksjs/ts-pki?style=flat-square
224+
[npm-version-href]: https://npmjs.com/package/@stacksjs/ts-pki
225+
[github-actions-src]: https://img.shields.io/github/actions/workflow/status/stacksjs/ts-pki/ci.yml?style=flat-square&branch=main
226+
[github-actions-href]: https://github.com/stacksjs/ts-pki/actions?query=workflow%3Aci
227+
228+
<!-- [codecov-src]: https://img.shields.io/codecov/c/gh/stacksjs/ts-pki/main?style=flat-square
229+
[codecov-href]: https://codecov.io/gh/stacksjs/ts-pki -->

packages/pem/build.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import process from 'node:process'
2+
import { dts } from 'bun-plugin-dtsx'
3+
4+
console.log('Building...')
5+
6+
const result = await Bun.build({
7+
entrypoints: ['./src/index.ts'],
8+
outdir: './dist',
9+
minify: true,
10+
plugins: [
11+
dts(),
12+
],
13+
})
14+
15+
if (!result.success) {
16+
console.error('Build failed')
17+
18+
for (const message of result.logs) {
19+
// Bun will pretty print the message object
20+
console.error(message)
21+
}
22+
23+
process.exit(1)
24+
}
25+
26+
console.log('Build complete')

packages/pem/package.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"name": "ts-pki",
3+
"type": "module",
4+
"version": "0.0.0",
5+
"description": "A PKI library.",
6+
"author": "Chris Breuer <chris@stacksjs.org> (https://github.com/chrisbbreuer)",
7+
"license": "MIT",
8+
"homepage": "https://github.com/stacksjs/ts-security#readme",
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/stacksjs/ts-security.git"
12+
},
13+
"bugs": {
14+
"url": "https://github.com/stacksjs/ts-security/issues"
15+
},
16+
"keywords": [
17+
"pki",
18+
"crypto",
19+
"cryptography",
20+
"bun",
21+
"stacks",
22+
"node-forge",
23+
"typescript",
24+
"javascript"
25+
],
26+
"exports": {
27+
".": {
28+
"import": "./dist/index.js",
29+
"types": "./dist/index.d.ts"
30+
}
31+
},
32+
"module": "./dist/index.js",
33+
"types": "./dist/index.d.ts",
34+
"files": ["README.md", "dist"],
35+
"scripts": {
36+
"build": "bun build.ts",
37+
"lint": "bunx --bun eslint .",
38+
"lint:fix": "bunx --bun eslint . --fix",
39+
"fresh": "bunx rimraf node_modules/ bun.lock && bun i",
40+
"changelog": "bunx changelogen --output CHANGELOG.md",
41+
"prepublishOnly": "bun --bun run build",
42+
"release": "bun run changelog && bunx bumpp package.json --all",
43+
"test": "bun test",
44+
"typecheck": "bun --bun tsc --noEmit"
45+
},
46+
"dependencies": {
47+
"ts-security-utils": "workspace:*"
48+
},
49+
"simple-git-hooks": {
50+
"pre-commit": "bun lint-staged"
51+
},
52+
"lint-staged": {
53+
"*.{js,ts}": "bunx eslint . --fix"
54+
}
55+
}

packages/pem/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './types'
2+
export * from './pem'

packages/pem/src/pem.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Converts an RSA private key from PEM format.
3+
*
4+
* @param pem the PEM-formatted private key.
5+
*
6+
* @return the private key.
7+
*/
8+
export function privateKeyFromPem(pem: string) {
9+
var msg = forge.pem.decode(pem)[0];
10+
11+
if (msg.type !== 'PRIVATE KEY' && msg.type !== 'RSA PRIVATE KEY') {
12+
var error = new Error('Could not convert private key from PEM; PEM ' +
13+
'header type is not "PRIVATE KEY" or "RSA PRIVATE KEY".');
14+
error.headerType = msg.type;
15+
throw error;
16+
}
17+
if (msg.procType && msg.procType.type === 'ENCRYPTED') {
18+
throw new Error('Could not convert private key from PEM; PEM is encrypted.');
19+
}
20+
21+
// convert DER to ASN.1 object
22+
var obj = asn1.fromDer(msg.body);
23+
24+
return pki.privateKeyFromAsn1(obj);
25+
};
26+
27+
/**
28+
* Converts an RSA private key to PEM format.
29+
*
30+
* @param key the private key.
31+
* @param maxline the maximum characters per line, defaults to 64.
32+
*
33+
* @return the PEM-formatted private key.
34+
*/
35+
export function privateKeyToPem(key: any, maxline: number) {
36+
// convert to ASN.1, then DER, then PEM-encode
37+
var msg = {
38+
type: 'RSA PRIVATE KEY',
39+
body: asn1.toDer(pki.privateKeyToAsn1(key)).getBytes()
40+
};
41+
return forge.pem.encode(msg, { maxline: maxline });
42+
};
43+
44+
/**
45+
* Converts a PrivateKeyInfo to PEM format.
46+
*
47+
* @param pki the PrivateKeyInfo.
48+
* @param maxline the maximum characters per line, defaults to 64.
49+
*
50+
* @return the PEM-formatted private key.
51+
*/
52+
export function privateKeyInfoToPem(pki: any, maxline: number) {
53+
// convert to DER, then PEM-encode
54+
var msg = {
55+
type: 'PRIVATE KEY',
56+
body: asn1.toDer(pki).getBytes()
57+
};
58+
return forge.pem.encode(msg, { maxline: maxline });
59+
};

packages/pem/src/types.ts

Whitespace-only changes.

0 commit comments

Comments
 (0)