@owlmeans/basic-keys 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -431
- package/package.json +8 -4
- package/tests/auth.spec.ts +31 -0
- package/tests/context.ts +10 -0
- package/tests/keys.spec.ts +44 -0
- package/tsconfig.json +5 -9
package/README.md
CHANGED
|
@@ -1,474 +1,82 @@
|
|
|
1
1
|
# @owlmeans/basic-keys
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
ED25519 and XChaCha20 cryptographic key operations: generation, signing, verification, and credential packing.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- **Key Pair Management**: Generate, import, and export cryptographic key pairs
|
|
12
|
-
- **Digital Signatures**: Sign and verify data with ED25519
|
|
13
|
-
- **Encryption**: Encrypt and decrypt data with XChaCha20-Poly1305
|
|
14
|
-
- **Authentication**: Pack and unpack authentication credentials
|
|
15
|
-
- **Plugin System**: Extensible architecture for different cryptographic algorithms
|
|
16
|
-
- **CLI Tool**: Command-line interface for key generation
|
|
17
|
-
- **Multiple Export Formats**: Support for various key export formats
|
|
18
|
-
|
|
19
|
-
## Supported Algorithms
|
|
20
|
-
|
|
21
|
-
- **ED25519**: Digital signatures and key derivation
|
|
22
|
-
- **XChaCha20-Poly1305**: Symmetric encryption and decryption
|
|
7
|
+
- Generate ED25519 key pairs or import from existing public/private keys
|
|
8
|
+
- Sign and verify arbitrary payloads
|
|
9
|
+
- Pack/unpack `AuthCredentials` objects with cryptographic signatures
|
|
10
|
+
- Address derivation from public keys
|
|
23
11
|
|
|
24
12
|
## Installation
|
|
25
13
|
|
|
26
14
|
```bash
|
|
27
|
-
|
|
15
|
+
bun add @owlmeans/basic-keys
|
|
28
16
|
```
|
|
29
17
|
|
|
30
|
-
##
|
|
18
|
+
## Usage
|
|
31
19
|
|
|
32
|
-
|
|
20
|
+
Generate a key pair and sign data:
|
|
33
21
|
|
|
34
22
|
```typescript
|
|
35
|
-
|
|
36
|
-
import { makeKeyPairModel, KeyType, fromPubKey, matchAddress, inputToKeyPair, packAuthCredentials, unpackAuthCredentials } from '@owlmeans/basic-keys'
|
|
37
|
-
|
|
38
|
-
// Plugin exports - cryptographic algorithm implementations
|
|
39
|
-
import { plugins, ed25519Plugin, xChahaPlugin, KeyPlugin } from '@owlmeans/basic-keys/plugins'
|
|
23
|
+
import { makeKeyPairModel } from '@owlmeans/basic-keys'
|
|
40
24
|
|
|
41
|
-
//
|
|
42
|
-
|
|
25
|
+
const pair = makeKeyPairModel() // generates a new ED25519 keypair
|
|
26
|
+
const exported = pair.export() // string representation for storage
|
|
27
|
+
const signature = await pair.sign(payload) // sign arbitrary data
|
|
28
|
+
const valid = await pair.verify(payload, signature)
|
|
29
|
+
const address = pair.exportAddress() // derive address from public key
|
|
43
30
|
```
|
|
44
31
|
|
|
45
|
-
|
|
32
|
+
Load from an existing public key:
|
|
46
33
|
|
|
47
34
|
```typescript
|
|
48
|
-
import {
|
|
35
|
+
import { fromPubKey, matchAddress } from '@owlmeans/basic-keys'
|
|
49
36
|
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
// Sign data
|
|
54
|
-
const signature = await keyPair.sign("Hello, World!")
|
|
55
|
-
|
|
56
|
-
// Verify signature
|
|
57
|
-
const isValid = await keyPair.verify("Hello, World!", signature)
|
|
58
|
-
|
|
59
|
-
// Export keys
|
|
60
|
-
const privateKey = keyPair.export() // "ed25519:base64privatekey"
|
|
61
|
-
const publicKey = keyPair.exportPublic() // "ed25519:base64publickey"
|
|
62
|
-
const address = keyPair.exportAddress() // "ed25519:base58address"
|
|
37
|
+
const model = fromPubKey('ed25519:ABC123...')
|
|
38
|
+
const isMatch = matchAddress(knownAddress, publicKeyStr)
|
|
63
39
|
```
|
|
64
40
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
### Core Types
|
|
68
|
-
|
|
69
|
-
#### KeyPair
|
|
70
|
-
|
|
71
|
-
Represents a cryptographic key pair with metadata.
|
|
41
|
+
Pack authentication credentials for a request:
|
|
72
42
|
|
|
73
43
|
```typescript
|
|
74
|
-
|
|
75
|
-
privateKey: string // Base64-encoded private key
|
|
76
|
-
publicKey: string // Base64-encoded public key
|
|
77
|
-
address: string // Algorithm-specific address
|
|
78
|
-
type: string // Algorithm type (e.g., "ed25519", "xchacha")
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
#### KeyPairModel
|
|
44
|
+
import { packAuthCredentials, unpackAuthCredentials } from '@owlmeans/basic-keys'
|
|
83
45
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
interface KeyPairModel {
|
|
88
|
-
keyPair?: KeyPair
|
|
89
|
-
sign: (data: unknown) => Promise<string>
|
|
90
|
-
verify: (data: unknown, signature: string) => Promise<boolean>
|
|
91
|
-
export: () => string
|
|
92
|
-
exportPublic: () => string
|
|
93
|
-
exportAddress: () => string
|
|
94
|
-
encrypt: (data: unknown) => Promise<string>
|
|
95
|
-
decrypt: (data: unknown) => Promise<string>
|
|
96
|
-
dcrpt: (data: unknown) => Promise<Uint8Array>
|
|
97
|
-
}
|
|
46
|
+
const signed = await packAuthCredentials(authObj, extraData, keyPairModel)
|
|
47
|
+
const { isValid, extras } = await unpackAuthCredentials(signed, keyPairModel)
|
|
98
48
|
```
|
|
99
49
|
|
|
100
|
-
|
|
50
|
+
## API
|
|
101
51
|
|
|
102
|
-
|
|
52
|
+
### `makeKeyPairModel(input?): KeyPairModel`
|
|
103
53
|
|
|
104
|
-
Creates a KeyPairModel
|
|
54
|
+
Creates a `KeyPairModel` from an existing `KeyPair` object, encoded private key string, or algorithm type string. Generates a new ED25519 key pair when called with no arguments.
|
|
105
55
|
|
|
106
|
-
|
|
107
|
-
function makeKeyPairModel(input?: KeyPair | string): KeyPairModel
|
|
108
|
-
```
|
|
56
|
+
### `fromPubKey(pubKey, type?): KeyPairModel`
|
|
109
57
|
|
|
110
|
-
|
|
111
|
-
- `input` (optional):
|
|
112
|
-
- `KeyPair` object
|
|
113
|
-
- Algorithm type string (e.g., "ed25519", "xchacha")
|
|
114
|
-
- Encoded private key string (e.g., "ed25519:base64key")
|
|
58
|
+
Creates a verify-only `KeyPairModel` from a public key string. Supports `"type:key"` format.
|
|
115
59
|
|
|
116
|
-
|
|
60
|
+
### `matchAddress(address, pubKey): boolean`
|
|
117
61
|
|
|
118
|
-
|
|
62
|
+
Returns true if `address` was derived from `pubKey`.
|
|
119
63
|
|
|
120
|
-
|
|
121
|
-
// Generate new ED25519 key pair
|
|
122
|
-
const keyPair1 = makeKeyPairModel()
|
|
64
|
+
### `packAuthCredentials(auth, extra, signer): Promise<AuthCredentials>`
|
|
123
65
|
|
|
124
|
-
|
|
125
|
-
const keyPair2 = makeKeyPairModel(KeyType.XCHACHA)
|
|
66
|
+
Signs auth credentials, optionally embedding extra data in the credential field.
|
|
126
67
|
|
|
127
|
-
|
|
128
|
-
const keyPair3 = makeKeyPairModel("ed25519:abcd1234...")
|
|
129
|
-
|
|
130
|
-
// Import from KeyPair object
|
|
131
|
-
const keyPair4 = makeKeyPairModel({
|
|
132
|
-
privateKey: "abcd1234...",
|
|
133
|
-
publicKey: "efgh5678...",
|
|
134
|
-
address: "ijkl9012...",
|
|
135
|
-
type: "ed25519"
|
|
136
|
-
})
|
|
137
|
-
```
|
|
68
|
+
### `unpackAuthCredentials(auth, verifier?): Promise<UnpackedAuthCredentials>`
|
|
138
69
|
|
|
139
|
-
|
|
70
|
+
Extracts and optionally verifies signed auth credentials. Returns `isValid` boolean when a verifier is provided.
|
|
140
71
|
|
|
141
|
-
|
|
72
|
+
### `KeyType`
|
|
142
73
|
|
|
143
74
|
```typescript
|
|
144
|
-
|
|
75
|
+
enum KeyType { ED25519 = 'ed25519', XCHACHA = 'xchacha' }
|
|
145
76
|
```
|
|
146
77
|
|
|
147
|
-
|
|
148
|
-
- `pubKey`: Public key string (with or without type prefix)
|
|
149
|
-
- `type` (optional): Algorithm type if not included in pubKey
|
|
150
|
-
|
|
151
|
-
**Returns:** `KeyPairModel` instance (without private key operations)
|
|
152
|
-
|
|
153
|
-
**Examples:**
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
// With type prefix
|
|
157
|
-
const publicKeyModel = fromPubKey("ed25519:abcd1234...")
|
|
158
|
-
|
|
159
|
-
// Without type prefix (defaults to ED25519)
|
|
160
|
-
const publicKeyModel2 = fromPubKey("abcd1234...")
|
|
161
|
-
|
|
162
|
-
// Explicit type
|
|
163
|
-
const publicKeyModel3 = fromPubKey("abcd1234...", KeyType.ED25519)
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
#### matchAddress(address, pubKey)
|
|
167
|
-
|
|
168
|
-
Verifies if a public key matches an address.
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
function matchAddress(address: string, pubKey: string): boolean
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
**Parameters:**
|
|
175
|
-
- `address`: Address string to verify
|
|
176
|
-
- `pubKey`: Public key string
|
|
177
|
-
|
|
178
|
-
**Returns:** `boolean` - true if the public key matches the address
|
|
179
|
-
|
|
180
|
-
#### inputToKeyPair(input?)
|
|
181
|
-
|
|
182
|
-
Converts various input formats to a KeyPair object.
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
function inputToKeyPair(input?: KeyPair | string): KeyPair
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
**Parameters:**
|
|
189
|
-
- `input` (optional):
|
|
190
|
-
- `KeyPair` object
|
|
191
|
-
- Algorithm type string (generates new key)
|
|
192
|
-
- Encoded private key string (e.g., "ed25519:base64key")
|
|
193
|
-
|
|
194
|
-
**Returns:** `KeyPair` object
|
|
195
|
-
|
|
196
|
-
**Examples:**
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
// Generate new ED25519 key pair
|
|
200
|
-
const keyPair1 = inputToKeyPair()
|
|
201
|
-
|
|
202
|
-
// Generate new XChaCha20 key pair
|
|
203
|
-
const keyPair2 = inputToKeyPair(KeyType.XCHACHA)
|
|
204
|
-
|
|
205
|
-
// Import from private key
|
|
206
|
-
const keyPair3 = inputToKeyPair("ed25519:abcd1234...")
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Authentication Helpers
|
|
210
|
-
|
|
211
|
-
#### packAuthCredentials(auth, extra, signer)
|
|
212
|
-
|
|
213
|
-
Packs authentication credentials with a signature.
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
function packAuthCredentials<T>(
|
|
217
|
-
auth: UnsignedAuthCredentials,
|
|
218
|
-
extra: T,
|
|
219
|
-
signer: KeyPairModel | PayloadSigner
|
|
220
|
-
): Promise<AuthCredentials>
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
**Parameters:**
|
|
224
|
-
- `auth`: Unsigned authentication credentials
|
|
225
|
-
- `extra`: Additional data to include in credentials
|
|
226
|
-
- `signer`: KeyPairModel or custom signing function
|
|
227
|
-
|
|
228
|
-
**Returns:** Promise resolving to signed `AuthCredentials`
|
|
229
|
-
|
|
230
|
-
#### unpackAuthCredentials(auth, verifier?)
|
|
231
|
-
|
|
232
|
-
Unpacks and optionally verifies authentication credentials.
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
function unpackAuthCredentials<T>(
|
|
236
|
-
auth: AuthCredentials,
|
|
237
|
-
verifier?: KeyPairModel | PayloadVerifier
|
|
238
|
-
): Promise<UnpackedAuthCredentials<T>>
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
**Parameters:**
|
|
242
|
-
- `auth`: Signed authentication credentials
|
|
243
|
-
- `verifier` (optional): KeyPairModel or custom verification function
|
|
244
|
-
|
|
245
|
-
**Returns:** Promise resolving to `UnpackedAuthCredentials<T>`
|
|
246
|
-
|
|
247
|
-
### Constants
|
|
248
|
-
|
|
249
|
-
#### KeyType
|
|
250
|
-
|
|
251
|
-
Enumeration of supported cryptographic algorithm types.
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
enum KeyType {
|
|
255
|
-
ED25519 = 'ed25519',
|
|
256
|
-
XCHACHA = 'xchacha'
|
|
257
|
-
}
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### Utility Functions
|
|
261
|
-
|
|
262
|
-
#### toAddress(publicKey)
|
|
263
|
-
|
|
264
|
-
Converts a public key to its corresponding address.
|
|
265
|
-
|
|
266
|
-
```typescript
|
|
267
|
-
function toAddress(publicKey: Uint8Array): Uint8Array
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
**Parameters:**
|
|
271
|
-
- `publicKey`: Public key as Uint8Array
|
|
272
|
-
|
|
273
|
-
**Returns:** `Uint8Array` - Address bytes (last 20 bytes of Keccak-256 hash)
|
|
274
|
-
|
|
275
|
-
#### prepareData(data)
|
|
276
|
-
|
|
277
|
-
Converts various data types to Uint8Array for cryptographic operations.
|
|
278
|
-
|
|
279
|
-
```typescript
|
|
280
|
-
function prepareData(data: unknown): Uint8Array
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
#### prepareKey(key)
|
|
284
|
-
|
|
285
|
-
Converts a base64-encoded key string to Uint8Array.
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
function prepareKey(key: string): Uint8Array
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
#### assertType(type?)
|
|
292
|
-
|
|
293
|
-
Validates that a cryptographic algorithm type is supported.
|
|
294
|
-
|
|
295
|
-
```typescript
|
|
296
|
-
function assertType(type?: string): void
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
## Plugin System
|
|
300
|
-
|
|
301
|
-
The library uses a plugin architecture to support different cryptographic algorithms. Each plugin implements the `KeyPlugin` interface:
|
|
302
|
-
|
|
303
|
-
```typescript
|
|
304
|
-
interface KeyPlugin {
|
|
305
|
-
type: string
|
|
306
|
-
random: () => Uint8Array
|
|
307
|
-
fromSeed?: (seed: Uint8Array) => Uint8Array
|
|
308
|
-
derive?: (pk: Uint8Array, path: string) => Uint8Array
|
|
309
|
-
sign: (data: Uint8Array, pk: Uint8Array) => Uint8Array
|
|
310
|
-
verify: (data: Uint8Array, signature: Uint8Array, pub: Uint8Array) => boolean
|
|
311
|
-
toPublic: (pk: Uint8Array) => Uint8Array
|
|
312
|
-
toAdress: (pub: Uint8Array) => string
|
|
313
|
-
encrypt: (data: Uint8Array, pk: Uint8Array) => Uint8Array
|
|
314
|
-
decrypt: (data: Uint8Array, pk: Uint8Array) => Uint8Array
|
|
315
|
-
}
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
### Built-in Plugins
|
|
319
|
-
|
|
320
|
-
#### ED25519 Plugin
|
|
321
|
-
|
|
322
|
-
- **Type**: `ed25519`
|
|
323
|
-
- **Capabilities**: Digital signatures, key derivation
|
|
324
|
-
- **Address Format**: Base58-encoded Keccak-256 hash
|
|
325
|
-
- **Encryption**: Not supported (throws error)
|
|
326
|
-
|
|
327
|
-
#### XChaCha20-Poly1305 Plugin
|
|
328
|
-
|
|
329
|
-
- **Type**: `xchacha`
|
|
330
|
-
- **Capabilities**: Symmetric encryption and decryption
|
|
331
|
-
- **Address Format**: "no-address" (not applicable)
|
|
332
|
-
- **Signing**: Not supported (throws error)
|
|
333
|
-
|
|
334
|
-
### Using Plugins
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
import { plugins } from '@owlmeans/basic-keys/plugins'
|
|
338
|
-
// Or import individual plugins
|
|
339
|
-
import { ed25519Plugin, xChahaPlugin } from '@owlmeans/basic-keys/plugins'
|
|
340
|
-
|
|
341
|
-
// Access plugin directly
|
|
342
|
-
const ed25519Plugin = plugins['ed25519']
|
|
343
|
-
|
|
344
|
-
// Generate random private key
|
|
345
|
-
const privateKey = ed25519Plugin.random()
|
|
346
|
-
|
|
347
|
-
// Convert to public key
|
|
348
|
-
const publicKey = ed25519Plugin.toPublic(privateKey)
|
|
349
|
-
|
|
350
|
-
// Sign data
|
|
351
|
-
const signature = ed25519Plugin.sign(data, privateKey)
|
|
352
|
-
|
|
353
|
-
// Verify signature
|
|
354
|
-
const isValid = ed25519Plugin.verify(data, signature, publicKey)
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
## CLI Tool
|
|
358
|
-
|
|
359
|
-
The package includes a command-line tool for key generation:
|
|
360
|
-
|
|
361
|
-
```bash
|
|
362
|
-
# Generate keys
|
|
363
|
-
npx owlkeys
|
|
364
|
-
|
|
365
|
-
# Or if installed globally
|
|
366
|
-
owlkeys
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
The CLI tool generates and displays:
|
|
370
|
-
- ED25519 private key export
|
|
371
|
-
- ED25519 public key export
|
|
372
|
-
- ED25519 address (DID format)
|
|
373
|
-
- XChaCha20 key export
|
|
374
|
-
|
|
375
|
-
## Advanced Usage
|
|
376
|
-
|
|
377
|
-
### Custom Signing and Verification
|
|
378
|
-
|
|
379
|
-
```typescript
|
|
380
|
-
import type { PayloadSigner, PayloadVerifier } from '@owlmeans/basic-keys'
|
|
381
|
-
|
|
382
|
-
// Custom signer function
|
|
383
|
-
const customSigner: PayloadSigner = async (payload) => {
|
|
384
|
-
// Custom signing logic
|
|
385
|
-
return signature
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// Custom verifier function
|
|
389
|
-
const customVerifier: PayloadVerifier = async (payload, signature) => {
|
|
390
|
-
// Custom verification logic
|
|
391
|
-
return isValid
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Use with authentication helpers
|
|
395
|
-
const credentials = await packAuthCredentials(auth, extra, customSigner)
|
|
396
|
-
const unpacked = await unpackAuthCredentials(credentials, customVerifier)
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
### Working with Raw Data
|
|
400
|
-
|
|
401
|
-
```typescript
|
|
402
|
-
import { prepareData, prepareKey, toAddress, assertType } from '@owlmeans/basic-keys/utils'
|
|
403
|
-
|
|
404
|
-
// Prepare various data types for cryptographic operations
|
|
405
|
-
const data1 = prepareData("string data") // UTF-8 encoded
|
|
406
|
-
const data2 = prepareData({ key: "value" }) // JSON canonicalized
|
|
407
|
-
const data3 = prepareData(new Uint8Array([1, 2])) // As-is
|
|
408
|
-
|
|
409
|
-
// Prepare keys
|
|
410
|
-
const keyBytes = prepareKey("base64KeyString")
|
|
411
|
-
|
|
412
|
-
// Generate address from public key
|
|
413
|
-
const address = toAddress(publicKeyBytes)
|
|
414
|
-
|
|
415
|
-
// Assert algorithm type is supported
|
|
416
|
-
assertType("ed25519") // No error
|
|
417
|
-
assertType("unknown") // Throws error
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
### Encryption and Decryption
|
|
421
|
-
|
|
422
|
-
```typescript
|
|
423
|
-
// For encryption, use XChaCha20 keys
|
|
424
|
-
const encryptionKey = makeKeyPairModel(KeyType.XCHACHA)
|
|
425
|
-
|
|
426
|
-
// Encrypt data
|
|
427
|
-
const encrypted = await encryptionKey.encrypt("sensitive data")
|
|
428
|
-
|
|
429
|
-
// Decrypt data
|
|
430
|
-
const decrypted = await encryptionKey.decrypt(encrypted)
|
|
431
|
-
|
|
432
|
-
// Decrypt to raw bytes
|
|
433
|
-
const rawBytes = await encryptionKey.dcrpt(encrypted)
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
## Error Handling
|
|
437
|
-
|
|
438
|
-
The library throws descriptive errors for various failure conditions:
|
|
439
|
-
|
|
440
|
-
- `basic.keys:string-type-or-key` - Invalid key string format
|
|
441
|
-
- `basic.keys:missing-keypair` - KeyPair not available
|
|
442
|
-
- `basic.keys:missing-pk` - Private key not available
|
|
443
|
-
- `basic.keys:sign-data-type` - Invalid data type for signing
|
|
444
|
-
- `basic.keys:unknown-type` - Unsupported algorithm type
|
|
445
|
-
- `ed25519:encryption-support` - ED25519 doesn't support encryption
|
|
446
|
-
- `xchacha:signing` - XChaCha20 doesn't support signing
|
|
447
|
-
- `xchacha:verification` - XChaCha20 doesn't support verification
|
|
448
|
-
|
|
449
|
-
## Integration with OwlMeans Common
|
|
450
|
-
|
|
451
|
-
This package is designed to integrate seamlessly with other OwlMeans Common libraries:
|
|
452
|
-
|
|
453
|
-
- **@owlmeans/auth**: Authentication and authorization
|
|
454
|
-
- **@owlmeans/client-auth**: Client-side authentication
|
|
455
|
-
- **@owlmeans/server-auth**: Server-side authentication
|
|
456
|
-
|
|
457
|
-
## TypeScript Support
|
|
458
|
-
|
|
459
|
-
The library is written in TypeScript and provides comprehensive type definitions. All exports are properly typed for optimal developer experience.
|
|
460
|
-
|
|
461
|
-
## Security Considerations
|
|
462
|
-
|
|
463
|
-
- Private keys are stored as base64-encoded strings
|
|
464
|
-
- All cryptographic operations use well-established libraries (@noble/curves, @noble/ciphers)
|
|
465
|
-
- Data is canonicalized before signing to prevent signature malleability
|
|
466
|
-
- Key generation uses cryptographically secure random number generation
|
|
467
|
-
|
|
468
|
-
## Contributing
|
|
469
|
-
|
|
470
|
-
This package is part of the OwlMeans Common ecosystem. Please refer to the main repository for contribution guidelines.
|
|
471
|
-
|
|
472
|
-
## License
|
|
78
|
+
## Related Packages
|
|
473
79
|
|
|
474
|
-
|
|
80
|
+
- [`@owlmeans/basic-envelope`](../basic-envelope) — uses `KeyPairModel` for envelope signing
|
|
81
|
+
- [`@owlmeans/auth`](../auth) — `AuthCredentials` type consumed by this package
|
|
82
|
+
- [`@owlmeans/did`](../did) — DID documents built on top of key pairs
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/basic-keys",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "tsc -b",
|
|
8
8
|
"dev": "sleep 48 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
|
|
9
|
-
"watch": "tsc -b -w --preserveWatchOutput --pretty"
|
|
9
|
+
"watch": "tsc -b -w --preserveWatchOutput --pretty",
|
|
10
|
+
"test": "bun test ./tests"
|
|
10
11
|
},
|
|
11
12
|
"bin": {
|
|
12
13
|
"owlkeys": "./build/bin.js"
|
|
@@ -38,15 +39,18 @@
|
|
|
38
39
|
}
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
42
|
+
"@owlmeans/dep-config": "workspace:*",
|
|
43
|
+
"@owlmeans/test-auth": "^0.1.4",
|
|
44
|
+
"@types/bun": "^1.3.0",
|
|
41
45
|
"nodemon": "^3.1.11",
|
|
42
46
|
"npm-check": "^6.0.1",
|
|
43
|
-
"typescript": "^
|
|
47
|
+
"typescript": "^6.0.2"
|
|
44
48
|
},
|
|
45
49
|
"dependencies": {
|
|
46
50
|
"@noble/ciphers": "^1.2.1",
|
|
47
51
|
"@noble/curves": "^1.6.0",
|
|
48
52
|
"@noble/hashes": "^1.5.0",
|
|
49
|
-
"@owlmeans/auth": "^0.1.
|
|
53
|
+
"@owlmeans/auth": "^0.1.4",
|
|
50
54
|
"@scure/base": "^1.1.9",
|
|
51
55
|
"canonicalize": "^2.0.0"
|
|
52
56
|
},
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { packAuthCredentials, unpackAuthCredentials } from '@owlmeans/basic-keys'
|
|
3
|
+
import { AuthRole, AuthenticationType } from '@owlmeans/auth'
|
|
4
|
+
import type { AuthCredentials } from '@owlmeans/auth'
|
|
5
|
+
import { fixtureKey } from './context.js'
|
|
6
|
+
|
|
7
|
+
const baseCreds = (): Omit<AuthCredentials, 'credential'> => ({
|
|
8
|
+
type: AuthenticationType.BasicEd25519,
|
|
9
|
+
role: AuthRole.User,
|
|
10
|
+
userId: 'user-1',
|
|
11
|
+
challenge: 'challenge-string',
|
|
12
|
+
scopes: ['*'],
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
describe('@owlmeans/basic-keys — packAuthCredentials round-trip', () => {
|
|
16
|
+
test('signs and verifies credentials with extras attached', async () => {
|
|
17
|
+
const key = fixtureKey()
|
|
18
|
+
const signed = await packAuthCredentials(baseCreds(), { nonce: 'n-1' }, key)
|
|
19
|
+
const result = await unpackAuthCredentials<{ nonce: string }>(signed, key)
|
|
20
|
+
expect(result.isValid).toBe(true)
|
|
21
|
+
expect(result.extras?.nonce).toBe('n-1')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('verification fails for a different keypair', async () => {
|
|
25
|
+
const signing = fixtureKey('alice')
|
|
26
|
+
const other = fixtureKey('bob')
|
|
27
|
+
const signed = await packAuthCredentials(baseCreds(), { nonce: 'n-1' }, signing)
|
|
28
|
+
const result = await unpackAuthCredentials(signed, other)
|
|
29
|
+
expect(result.isValid).toBe(false)
|
|
30
|
+
})
|
|
31
|
+
})
|
package/tests/context.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { makeFixtureKeyPair } from '@owlmeans/test-auth'
|
|
2
|
+
import type { KeyPairModel } from '@owlmeans/basic-keys'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Per-suite fixture: deterministic Ed25519 keypair from `@owlmeans/test-auth`.
|
|
6
|
+
* Category-B packages reuse the same fixture across specs so signatures
|
|
7
|
+
* stay stable run-to-run.
|
|
8
|
+
*/
|
|
9
|
+
export const fixtureKey = (seed: string = 'basic-keys-pilot'): KeyPairModel =>
|
|
10
|
+
makeFixtureKeyPair(seed)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { fromPubKey, makeKeyPairModel, matchAddress } from '@owlmeans/basic-keys'
|
|
3
|
+
import { fixtureKey } from './context.js'
|
|
4
|
+
|
|
5
|
+
describe('@owlmeans/basic-keys — KeyPairModel sign/verify', () => {
|
|
6
|
+
// viable-agent/packages/template/packages/backend/src/owlmeans.ts:63 builds a key
|
|
7
|
+
// via `makeKeyPairModel(process.env.TRUSTED_PK)` and signs/verifies through it.
|
|
8
|
+
test('round-trips a signature with the same keypair', async () => {
|
|
9
|
+
const key = fixtureKey('keys-spec')
|
|
10
|
+
const sig = await key.sign({ payload: 'hello' })
|
|
11
|
+
expect(await key.verify({ payload: 'hello' }, sig)).toBe(true)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('verification fails when the payload is tampered', async () => {
|
|
15
|
+
const key = fixtureKey('keys-spec')
|
|
16
|
+
const sig = await key.sign({ payload: 'hello' })
|
|
17
|
+
expect(await key.verify({ payload: 'goodbye' }, sig)).toBe(false)
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('@owlmeans/basic-keys — public-key-only verification (fromPubKey)', () => {
|
|
22
|
+
// Mirrors viable/sources/backend/src/config.ts which loads peer public keys
|
|
23
|
+
// (`master-pub`, `auth-pub`, etc.) into trusted records — the reading service
|
|
24
|
+
// never holds the private key but must still verify those peers' signatures.
|
|
25
|
+
test('fromPubKey produces a model that verifies signatures from the original key', async () => {
|
|
26
|
+
const signing = fixtureKey('peer-keys-spec')
|
|
27
|
+
const sig = await signing.sign({ event: 'check' })
|
|
28
|
+
|
|
29
|
+
const verifying = fromPubKey(signing.exportPublic())
|
|
30
|
+
expect(await verifying.verify({ event: 'check' }, sig)).toBe(true)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('matchAddress agrees with exportAddress() on the same keypair', () => {
|
|
34
|
+
const key = fixtureKey('addr-spec')
|
|
35
|
+
expect(matchAddress(key.exportAddress(), key.exportPublic())).toBe(true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('export() round-trips into a new model that produces the same public key', async () => {
|
|
39
|
+
const key = fixtureKey('export-spec')
|
|
40
|
+
const reimported = makeKeyPairModel(key.export())
|
|
41
|
+
expect(reimported.exportPublic()).toBe(key.exportPublic())
|
|
42
|
+
expect(reimported.exportAddress()).toBe(key.exportAddress())
|
|
43
|
+
})
|
|
44
|
+
})
|
package/tsconfig.json
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": [
|
|
3
|
-
"
|
|
3
|
+
"@owlmeans/dep-config/tsconfig.base.json"
|
|
4
4
|
],
|
|
5
5
|
"compilerOptions": {
|
|
6
|
-
"rootDir": "./src/",
|
|
7
|
-
"outDir": "./build/"
|
|
6
|
+
"rootDir": "./src/",
|
|
7
|
+
"outDir": "./build/"
|
|
8
8
|
},
|
|
9
|
-
"exclude": [
|
|
10
|
-
|
|
11
|
-
"./build/**/*",
|
|
12
|
-
"./*.ts"
|
|
13
|
-
]
|
|
14
|
-
}
|
|
9
|
+
"exclude": ["./dist/**/*", "./build/**/*", "./tests/**/*", "./*.ts"]
|
|
10
|
+
}
|