@hg-ts/rsa 0.7.26 → 0.8.0
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 +91 -0
- package/dist/X25519/key-pair.d.ts +4 -4
- package/dist/X25519/key-pair.d.ts.map +1 -1
- package/dist/X25519/key-pair.js +3 -5
- package/dist/X25519/key-pair.js.map +1 -1
- package/dist/X25519/private-key.d.ts +5 -4
- package/dist/X25519/private-key.d.ts.map +1 -1
- package/dist/X25519/private-key.js +8 -21
- package/dist/X25519/private-key.js.map +1 -1
- package/dist/X25519/public-key.d.ts +4 -6
- package/dist/X25519/public-key.d.ts.map +1 -1
- package/dist/X25519/public-key.js +8 -19
- package/dist/X25519/public-key.js.map +1 -1
- package/dist/X25519/x25519.test.d.ts +7 -6
- package/dist/X25519/x25519.test.d.ts.map +1 -1
- package/dist/X25519/x25519.test.js +70 -58
- package/dist/X25519/x25519.test.js.map +1 -1
- package/dist/aead/index.d.ts +2 -0
- package/dist/aead/index.d.ts.map +1 -0
- package/dist/aead/index.js +2 -0
- package/dist/aead/index.js.map +1 -0
- package/dist/aead/xchacha20-poly1305.d.ts +10 -0
- package/dist/aead/xchacha20-poly1305.d.ts.map +1 -0
- package/dist/aead/xchacha20-poly1305.js +48 -0
- package/dist/aead/xchacha20-poly1305.js.map +1 -0
- package/dist/aead/xchacha20-poly1305.test.d.ts +14 -0
- package/dist/aead/xchacha20-poly1305.test.d.ts.map +1 -0
- package/dist/aead/xchacha20-poly1305.test.js +140 -0
- package/dist/aead/xchacha20-poly1305.test.js.map +1 -0
- package/dist/base/index.d.ts +1 -0
- package/dist/base/index.d.ts.map +1 -1
- package/dist/base/index.js +1 -0
- package/dist/base/index.js.map +1 -1
- package/dist/base/key-capabilities.d.ts +36 -0
- package/dist/base/key-capabilities.d.ts.map +1 -0
- package/dist/base/key-capabilities.js +2 -0
- package/dist/base/key-capabilities.js.map +1 -0
- package/dist/base/key-pair.d.ts +3 -7
- package/dist/base/key-pair.d.ts.map +1 -1
- package/dist/base/key-pair.js.map +1 -1
- package/dist/base/key.d.ts +2 -1
- package/dist/base/key.d.ts.map +1 -1
- package/dist/base/key.js.map +1 -1
- package/dist/base/private-key.d.ts +2 -3
- package/dist/base/private-key.d.ts.map +1 -1
- package/dist/base/private-key.js.map +1 -1
- package/dist/base/public-key.d.ts +2 -3
- package/dist/base/public-key.d.ts.map +1 -1
- package/dist/base/public-key.js.map +1 -1
- package/dist/exceptions/hkdf-output-length.exception.d.ts +5 -0
- package/dist/exceptions/hkdf-output-length.exception.d.ts.map +1 -0
- package/dist/exceptions/hkdf-output-length.exception.js +7 -0
- package/dist/exceptions/hkdf-output-length.exception.js.map +1 -0
- package/dist/exceptions/index.d.ts +4 -0
- package/dist/exceptions/index.d.ts.map +1 -1
- package/dist/exceptions/index.js +4 -0
- package/dist/exceptions/index.js.map +1 -1
- package/dist/exceptions/invalid-encryption-key.exception.d.ts +5 -0
- package/dist/exceptions/invalid-encryption-key.exception.d.ts.map +1 -0
- package/dist/exceptions/invalid-encryption-key.exception.js +7 -0
- package/dist/exceptions/invalid-encryption-key.exception.js.map +1 -0
- package/dist/exceptions/invalid-pq-kem-key-length.exception.d.ts +5 -0
- package/dist/exceptions/invalid-pq-kem-key-length.exception.d.ts.map +1 -0
- package/dist/exceptions/invalid-pq-kem-key-length.exception.js +7 -0
- package/dist/exceptions/invalid-pq-kem-key-length.exception.js.map +1 -0
- package/dist/exceptions/invalid-pq-kem-message-length.exception.d.ts +5 -0
- package/dist/exceptions/invalid-pq-kem-message-length.exception.d.ts.map +1 -0
- package/dist/exceptions/invalid-pq-kem-message-length.exception.js +7 -0
- package/dist/exceptions/invalid-pq-kem-message-length.exception.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/pq-kem/algorithm.d.ts +16 -0
- package/dist/pq-kem/algorithm.d.ts.map +1 -0
- package/dist/pq-kem/algorithm.js +25 -0
- package/dist/pq-kem/algorithm.js.map +1 -0
- package/dist/pq-kem/index.d.ts +5 -0
- package/dist/pq-kem/index.d.ts.map +1 -0
- package/dist/pq-kem/index.js +5 -0
- package/dist/pq-kem/index.js.map +1 -0
- package/dist/pq-kem/key-pair.d.ts +15 -0
- package/dist/pq-kem/key-pair.d.ts.map +1 -0
- package/dist/pq-kem/key-pair.js +44 -0
- package/dist/pq-kem/key-pair.js.map +1 -0
- package/dist/pq-kem/pq-kem.test.d.ts +18 -0
- package/dist/pq-kem/pq-kem.test.d.ts.map +1 -0
- package/dist/pq-kem/pq-kem.test.js +173 -0
- package/dist/pq-kem/pq-kem.test.js.map +1 -0
- package/dist/pq-kem/private-key.d.ts +16 -0
- package/dist/pq-kem/private-key.d.ts.map +1 -0
- package/dist/pq-kem/private-key.js +43 -0
- package/dist/pq-kem/private-key.js.map +1 -0
- package/dist/pq-kem/public-key.d.ts +18 -0
- package/dist/pq-kem/public-key.d.ts.map +1 -0
- package/dist/pq-kem/public-key.js +70 -0
- package/dist/pq-kem/public-key.js.map +1 -0
- package/dist/rsa/key-pair.d.ts +2 -2
- package/dist/rsa/key-pair.d.ts.map +1 -1
- package/dist/rsa/key-pair.js.map +1 -1
- package/dist/rsa/private-key.d.ts +3 -2
- package/dist/rsa/private-key.d.ts.map +1 -1
- package/dist/rsa/private-key.js +2 -1
- package/dist/rsa/private-key.js.map +1 -1
- package/dist/rsa/public-key.d.ts +3 -2
- package/dist/rsa/public-key.d.ts.map +1 -1
- package/dist/rsa/public-key.js +2 -1
- package/dist/rsa/public-key.js.map +1 -1
- package/dist/rsa/rsa.test.d.ts.map +1 -1
- package/dist/rsa/rsa.test.js +1 -0
- package/dist/rsa/rsa.test.js.map +1 -1
- package/dist/utils/hkdf.d.ts +26 -0
- package/dist/utils/hkdf.d.ts.map +1 -0
- package/dist/utils/hkdf.js +43 -0
- package/dist/utils/hkdf.js.map +1 -0
- package/dist/utils/hkdf.test.d.ts +7 -0
- package/dist/utils/hkdf.test.d.ts.map +1 -0
- package/dist/utils/hkdf.test.js +79 -0
- package/dist/utils/hkdf.test.js.map +1 -0
- package/dist/utils/hmac.d.ts +5 -0
- package/dist/utils/hmac.d.ts.map +1 -0
- package/dist/utils/hmac.js +16 -0
- package/dist/utils/hmac.js.map +1 -0
- package/dist/utils/hmac.test.d.ts +6 -0
- package/dist/utils/hmac.test.d.ts.map +1 -0
- package/dist/utils/hmac.test.js +33 -0
- package/dist/utils/hmac.test.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/kdf.d.ts +3 -0
- package/dist/utils/kdf.d.ts.map +1 -0
- package/dist/utils/kdf.js +10 -0
- package/dist/utils/kdf.js.map +1 -0
- package/package.json +16 -10
- package/src/X25519/key-pair.ts +9 -9
- package/src/X25519/private-key.ts +15 -30
- package/src/X25519/public-key.ts +15 -31
- package/src/X25519/x25519.test.ts +81 -68
- package/src/aead/index.ts +1 -0
- package/src/aead/xchacha20-poly1305.test.ts +147 -0
- package/src/aead/xchacha20-poly1305.ts +80 -0
- package/src/base/index.ts +1 -0
- package/src/base/key-capabilities.ts +54 -0
- package/src/base/key-pair.ts +6 -11
- package/src/base/key.ts +3 -1
- package/src/base/private-key.ts +2 -5
- package/src/base/public-key.ts +4 -5
- package/src/exceptions/hkdf-output-length.exception.ts +7 -0
- package/src/exceptions/index.ts +4 -0
- package/src/exceptions/invalid-encryption-key.exception.ts +7 -0
- package/src/exceptions/invalid-pq-kem-key-length.exception.ts +7 -0
- package/src/exceptions/invalid-pq-kem-message-length.exception.ts +7 -0
- package/src/index.ts +3 -0
- package/src/pq-kem/algorithm.ts +51 -0
- package/src/pq-kem/index.ts +4 -0
- package/src/pq-kem/key-pair.ts +76 -0
- package/src/pq-kem/pq-kem.test.ts +144 -0
- package/src/pq-kem/private-key.ts +67 -0
- package/src/pq-kem/public-key.ts +99 -0
- package/src/rsa/key-pair.ts +11 -5
- package/src/rsa/private-key.ts +9 -2
- package/src/rsa/public-key.ts +9 -2
- package/src/rsa/rsa.test.ts +1 -0
- package/src/utils/hkdf.test.ts +77 -0
- package/src/utils/hkdf.ts +89 -0
- package/src/utils/hmac.test.ts +43 -0
- package/src/utils/hmac.ts +21 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/kdf.ts +17 -0
- package/tsconfig.json +3 -1
- package/dist/X25519/utils.d.ts +0 -2
- package/dist/X25519/utils.d.ts.map +0 -1
- package/dist/X25519/utils.js +0 -12
- package/dist/X25519/utils.js.map +0 -1
- package/src/X25519/utils.ts +0 -22
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Buffer from '@hg-ts/buffer';
|
|
2
2
|
import {
|
|
3
3
|
Describe,
|
|
4
4
|
expect,
|
|
@@ -9,79 +9,12 @@ import {
|
|
|
9
9
|
import { z } from '@hg-ts/validation';
|
|
10
10
|
import sodium from 'libsodium-wrappers';
|
|
11
11
|
|
|
12
|
-
import { InvalidDecryptionKeyExpection } from '../exceptions/index.js';
|
|
13
12
|
import { X25519KeyPair } from './key-pair.js';
|
|
14
13
|
import { X25519PrivateKey } from './private-key.js';
|
|
15
14
|
import { X25519PublicKey } from './public-key.js';
|
|
16
15
|
|
|
17
16
|
@Describe()
|
|
18
17
|
export class X25519Test extends Suite {
|
|
19
|
-
@Test()
|
|
20
|
-
public async encryption(): Promise<void> {
|
|
21
|
-
const bob = new X25519KeyPair();
|
|
22
|
-
const alice = new X25519KeyPair();
|
|
23
|
-
const value = Math.random().toString();
|
|
24
|
-
|
|
25
|
-
const encrypted = alice.encrypt(value, bob.publicKeyInstance);
|
|
26
|
-
|
|
27
|
-
expect(bob.decrypt(encrypted.toString('base64'), alice.publicKeyInstance)).toBe(value);
|
|
28
|
-
}
|
|
29
|
-
@Test()
|
|
30
|
-
public async duplicateEncryptionDifferences(): Promise<void> {
|
|
31
|
-
const alice = new X25519KeyPair();
|
|
32
|
-
const bob = new X25519KeyPair();
|
|
33
|
-
const value = Math.random().toString();
|
|
34
|
-
|
|
35
|
-
const encryptedA = alice.publicKeyInstance.encrypt(value, bob.privateKeyInstance);
|
|
36
|
-
const encryptedB = alice.publicKeyInstance.encrypt(value, bob.privateKeyInstance);
|
|
37
|
-
|
|
38
|
-
expect(encryptedA).not.toMatchObject(encryptedB);
|
|
39
|
-
expect(alice.decrypt(encryptedA, bob.publicKeyInstance)).toBe(value);
|
|
40
|
-
expect(alice.decrypt(encryptedB, bob.publicKeyInstance)).toBe(value);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@Test()
|
|
44
|
-
@ExpectException(InvalidDecryptionKeyExpection)
|
|
45
|
-
public async encryptionFailsForAnotherRecipient(): Promise<void> {
|
|
46
|
-
const bob = new X25519KeyPair();
|
|
47
|
-
const alice = new X25519KeyPair();
|
|
48
|
-
const anotherRecipient = new X25519KeyPair();
|
|
49
|
-
const value = Math.random().toString();
|
|
50
|
-
|
|
51
|
-
const encrypted = alice.encrypt(value, bob.publicKeyInstance);
|
|
52
|
-
|
|
53
|
-
anotherRecipient.decrypt(encrypted, alice.publicKeyInstance);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
@Test()
|
|
57
|
-
public async encryptionBuffer(): Promise<void> {
|
|
58
|
-
const bob = new X25519KeyPair();
|
|
59
|
-
const alice = new X25519KeyPair();
|
|
60
|
-
const value = Buffer.from(Math.random().toString(), 'utf8');
|
|
61
|
-
|
|
62
|
-
const encrypted = alice.encrypt(value, bob.publicKeyInstance);
|
|
63
|
-
|
|
64
|
-
expect(Buffer.from(bob.decrypt(encrypted, alice.publicKeyInstance))).toMatchObject(value);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
@Test()
|
|
68
|
-
@ExpectException(NotImplementedException)
|
|
69
|
-
public async signature(): Promise<void> {
|
|
70
|
-
const x25519 = new X25519KeyPair();
|
|
71
|
-
const value = Math.random().toString();
|
|
72
|
-
|
|
73
|
-
x25519.sign(value);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
@Test()
|
|
77
|
-
@ExpectException(NotImplementedException)
|
|
78
|
-
public async verify(): Promise<void> {
|
|
79
|
-
const x25519 = new X25519KeyPair();
|
|
80
|
-
const value = Math.random().toString();
|
|
81
|
-
|
|
82
|
-
x25519.verify(Buffer.from(value), value);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
18
|
@Test()
|
|
86
19
|
public async keyPairFromPrivateKeyString(): Promise<void> {
|
|
87
20
|
const x25519 = new X25519KeyPair();
|
|
@@ -131,6 +64,86 @@ export class X25519Test extends Suite {
|
|
|
131
64
|
expect(publicKey.toString()).toBe(x25519.publicKey);
|
|
132
65
|
}
|
|
133
66
|
|
|
67
|
+
@Test()
|
|
68
|
+
public async deriveSharedSecret(): Promise<void> {
|
|
69
|
+
const alice = new X25519KeyPair();
|
|
70
|
+
const bob = new X25519KeyPair();
|
|
71
|
+
|
|
72
|
+
const aliceSecret = alice.deriveSharedSecret(bob.publicKeyInstance);
|
|
73
|
+
const bobSecret = bob.deriveSharedSecret(alice.publicKeyInstance);
|
|
74
|
+
|
|
75
|
+
expect(aliceSecret).toMatchObject(bobSecret);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@Test()
|
|
79
|
+
public async deriveSharedSecretDifferentPairs(): Promise<void> {
|
|
80
|
+
const alice = new X25519KeyPair();
|
|
81
|
+
const bob = new X25519KeyPair();
|
|
82
|
+
const anotherBob = new X25519KeyPair();
|
|
83
|
+
|
|
84
|
+
const bobSecret = alice.deriveSharedSecret(bob.publicKeyInstance);
|
|
85
|
+
const anotherBobSecret = alice.deriveSharedSecret(anotherBob.publicKeyInstance);
|
|
86
|
+
|
|
87
|
+
expect(bobSecret).not.toMatchObject(anotherBobSecret);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@Test()
|
|
91
|
+
public async deriveSharedSecretFromRestoredKeys(): Promise<void> {
|
|
92
|
+
const alice = new X25519KeyPair();
|
|
93
|
+
const bob = new X25519KeyPair();
|
|
94
|
+
const restoredAlicePrivateKey = X25519PrivateKey.fromString(alice.privateKey);
|
|
95
|
+
const restoredBobPublicKey = X25519PublicKey.fromString(bob.publicKey);
|
|
96
|
+
|
|
97
|
+
const originalSecret = alice.deriveSharedSecret(bob.publicKeyInstance);
|
|
98
|
+
const restoredSecret = restoredAlicePrivateKey.deriveSharedSecret(restoredBobPublicKey);
|
|
99
|
+
|
|
100
|
+
expect(restoredSecret).toMatchObject(originalSecret);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Test()
|
|
104
|
+
public async signAndVerify(): Promise<void> {
|
|
105
|
+
const x25519 = new X25519KeyPair();
|
|
106
|
+
const value = Math.random().toString();
|
|
107
|
+
|
|
108
|
+
const signature = x25519.sign(value);
|
|
109
|
+
|
|
110
|
+
expect(x25519.verify(signature, value)).toBeTruthy();
|
|
111
|
+
expect(x25519.publicKeyInstance.verify(signature.toString('base64'), value)).toBeTruthy();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@Test()
|
|
115
|
+
public async signAndVerifyFromRestoredKeys(): Promise<void> {
|
|
116
|
+
const x25519 = new X25519KeyPair();
|
|
117
|
+
const privateKey = X25519PrivateKey.fromString(x25519.privateKey);
|
|
118
|
+
const publicKey = X25519PublicKey.fromString(x25519.publicKey);
|
|
119
|
+
const value = Math.random().toString();
|
|
120
|
+
|
|
121
|
+
const signature = privateKey.sign(value);
|
|
122
|
+
|
|
123
|
+
expect(publicKey.verify(signature, value)).toBeTruthy();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Test()
|
|
127
|
+
public async verifyFailsForAnotherMessage(): Promise<void> {
|
|
128
|
+
const x25519 = new X25519KeyPair();
|
|
129
|
+
const value = Math.random().toString();
|
|
130
|
+
|
|
131
|
+
const signature = x25519.sign(value);
|
|
132
|
+
|
|
133
|
+
expect(x25519.verify(signature, `${value}-another`)).toBeFalsy();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@Test()
|
|
137
|
+
public async verifyFailsForAnotherPublicKey(): Promise<void> {
|
|
138
|
+
const x25519 = new X25519KeyPair();
|
|
139
|
+
const anotherX25519 = new X25519KeyPair();
|
|
140
|
+
const value = Math.random().toString();
|
|
141
|
+
|
|
142
|
+
const signature = x25519.sign(value);
|
|
143
|
+
|
|
144
|
+
expect(anotherX25519.verify(signature, value)).toBeFalsy();
|
|
145
|
+
}
|
|
146
|
+
|
|
134
147
|
@Test()
|
|
135
148
|
public async publicKeyValidationValid(): Promise<void> {
|
|
136
149
|
const x25519 = new X25519KeyPair();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './xchacha20-poly1305.js';
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import Buffer from '@hg-ts/buffer';
|
|
2
|
+
import {
|
|
3
|
+
Describe,
|
|
4
|
+
expect,
|
|
5
|
+
ExpectException,
|
|
6
|
+
Suite,
|
|
7
|
+
Test,
|
|
8
|
+
} from '@hg-ts/tests';
|
|
9
|
+
import sodium from 'libsodium-wrappers';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
InvalidDecryptionKeyExpection,
|
|
13
|
+
InvalidEncryptionKeyException,
|
|
14
|
+
} from '../exceptions/index.js';
|
|
15
|
+
import { X25519KeyPair } from '../X25519/index.js';
|
|
16
|
+
import { XChaCha20Poly1305Aead } from './xchacha20-poly1305.js';
|
|
17
|
+
|
|
18
|
+
@Describe()
|
|
19
|
+
export class XChaCha20Poly1305AeadTest extends Suite {
|
|
20
|
+
@Test()
|
|
21
|
+
public async encryption(): Promise<void> {
|
|
22
|
+
const { aliceKey, bobKey } = this.deriveAliceBobKeys();
|
|
23
|
+
const value = Math.random().toString();
|
|
24
|
+
const aead = new XChaCha20Poly1305Aead();
|
|
25
|
+
|
|
26
|
+
const encrypted = aead.encrypt(value, aliceKey);
|
|
27
|
+
const decrypted = aead.decrypt(encrypted.toString('base64'), bobKey);
|
|
28
|
+
|
|
29
|
+
expect(decrypted.toString('utf8')).toBe(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Test()
|
|
33
|
+
public async encryptionBuffer(): Promise<void> {
|
|
34
|
+
const { aliceKey, bobKey } = this.deriveAliceBobKeys();
|
|
35
|
+
const value = Buffer.from(Math.random().toString(), 'utf8');
|
|
36
|
+
const aead = new XChaCha20Poly1305Aead();
|
|
37
|
+
|
|
38
|
+
const encrypted = aead.encrypt(value, aliceKey);
|
|
39
|
+
const decrypted = aead.decrypt(encrypted, bobKey);
|
|
40
|
+
|
|
41
|
+
expect(decrypted).toMatchObject(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Test()
|
|
45
|
+
public async duplicateEncryptionDifferences(): Promise<void> {
|
|
46
|
+
const { aliceKey, bobKey } = this.deriveAliceBobKeys();
|
|
47
|
+
const value = Math.random().toString();
|
|
48
|
+
const aead = new XChaCha20Poly1305Aead();
|
|
49
|
+
|
|
50
|
+
const encryptedA = aead.encrypt(value, aliceKey);
|
|
51
|
+
const encryptedB = aead.encrypt(value, aliceKey);
|
|
52
|
+
|
|
53
|
+
expect(encryptedA).not.toMatchObject(encryptedB);
|
|
54
|
+
expect(aead.decrypt(encryptedA, bobKey).toString('utf8')).toBe(value);
|
|
55
|
+
expect(aead.decrypt(encryptedB, bobKey).toString('utf8')).toBe(value);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Test()
|
|
59
|
+
public async associatedData(): Promise<void> {
|
|
60
|
+
const { aliceKey, bobKey } = this.deriveAliceBobKeys();
|
|
61
|
+
const value = Math.random().toString();
|
|
62
|
+
const associatedData = Buffer.from('message-header', 'utf8');
|
|
63
|
+
const aead = new XChaCha20Poly1305Aead();
|
|
64
|
+
|
|
65
|
+
const encrypted = aead.encrypt(value, aliceKey, associatedData);
|
|
66
|
+
const decrypted = aead.decrypt(encrypted, bobKey, associatedData);
|
|
67
|
+
|
|
68
|
+
expect(decrypted.toString('utf8')).toBe(value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Test()
|
|
72
|
+
@ExpectException(InvalidDecryptionKeyExpection)
|
|
73
|
+
public async decryptFailsForAnotherAssociatedData(): Promise<void> {
|
|
74
|
+
const { aliceKey, bobKey } = this.deriveAliceBobKeys();
|
|
75
|
+
const aead = new XChaCha20Poly1305Aead();
|
|
76
|
+
|
|
77
|
+
const encrypted = aead.encrypt(Math.random().toString(), aliceKey, 'message-header');
|
|
78
|
+
|
|
79
|
+
aead.decrypt(encrypted, bobKey, 'another-message-header');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@Test()
|
|
83
|
+
@ExpectException(InvalidDecryptionKeyExpection)
|
|
84
|
+
public async decryptFailsForAnotherKey(): Promise<void> {
|
|
85
|
+
const { aliceKey } = this.deriveAliceBobKeys();
|
|
86
|
+
const anotherKey = new X25519KeyPair().privateKeyInstance.nativeKey;
|
|
87
|
+
const aead = new XChaCha20Poly1305Aead();
|
|
88
|
+
|
|
89
|
+
const encrypted = aead.encrypt(Math.random().toString(), aliceKey);
|
|
90
|
+
|
|
91
|
+
aead.decrypt(encrypted, anotherKey);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@Test()
|
|
95
|
+
@ExpectException(InvalidEncryptionKeyException)
|
|
96
|
+
public async encryptionFailsForInvalidKeyLength(): Promise<void> {
|
|
97
|
+
const aead = new XChaCha20Poly1305Aead();
|
|
98
|
+
|
|
99
|
+
aead.encrypt(Math.random().toString(), Buffer.from('invalid-key', 'utf8'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@Test()
|
|
103
|
+
public async deriveKeyDependsOnPublicKeyOrder(): Promise<void> {
|
|
104
|
+
const alice = new X25519KeyPair();
|
|
105
|
+
const bob = new X25519KeyPair();
|
|
106
|
+
const sharedSecret = alice.deriveSharedSecret(bob.publicKeyInstance);
|
|
107
|
+
const aead = new XChaCha20Poly1305Aead();
|
|
108
|
+
|
|
109
|
+
const keyA = aead.deriveKey(
|
|
110
|
+
sharedSecret,
|
|
111
|
+
alice.publicKeyInstance.nativeKey,
|
|
112
|
+
bob.publicKeyInstance.nativeKey,
|
|
113
|
+
);
|
|
114
|
+
const keyB = aead.deriveKey(
|
|
115
|
+
sharedSecret,
|
|
116
|
+
bob.publicKeyInstance.nativeKey,
|
|
117
|
+
alice.publicKeyInstance.nativeKey,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
expect(keyA).not.toMatchObject(keyB);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public override async setUp(): Promise<void> {
|
|
124
|
+
await sodium.ready;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private deriveAliceBobKeys(): { aliceKey: Buffer; bobKey: Buffer } {
|
|
128
|
+
const alice = new X25519KeyPair();
|
|
129
|
+
const bob = new X25519KeyPair();
|
|
130
|
+
const aliceSecret = alice.deriveSharedSecret(bob.publicKeyInstance);
|
|
131
|
+
const bobSecret = bob.deriveSharedSecret(alice.publicKeyInstance);
|
|
132
|
+
const aead = new XChaCha20Poly1305Aead();
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
aliceKey: aead.deriveKey(
|
|
136
|
+
aliceSecret,
|
|
137
|
+
alice.publicKeyInstance.nativeKey,
|
|
138
|
+
bob.publicKeyInstance.nativeKey,
|
|
139
|
+
),
|
|
140
|
+
bobKey: aead.deriveKey(
|
|
141
|
+
bobSecret,
|
|
142
|
+
alice.publicKeyInstance.nativeKey,
|
|
143
|
+
bob.publicKeyInstance.nativeKey,
|
|
144
|
+
),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import Buffer from '@hg-ts/buffer';
|
|
2
|
+
import sodium from 'libsodium-wrappers';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
InvalidDecryptionKeyExpection,
|
|
6
|
+
InvalidEncryptionKeyException,
|
|
7
|
+
} from '../exceptions/index.js';
|
|
8
|
+
import { deriveKey } from '../utils/index.js';
|
|
9
|
+
|
|
10
|
+
const ALGORITHM = Buffer.from('X25519-XChaCha20-Poly1305', 'utf8');
|
|
11
|
+
|
|
12
|
+
sodium.ready.catch(() => {});
|
|
13
|
+
|
|
14
|
+
export class XChaCha20Poly1305Aead {
|
|
15
|
+
public deriveKey(...values: Buffer[]): Buffer {
|
|
16
|
+
return deriveKey(
|
|
17
|
+
sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
|
|
18
|
+
ALGORITHM,
|
|
19
|
+
...values,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public encrypt(value: string | Buffer, key: Buffer, associatedData?: string | Buffer): Buffer {
|
|
24
|
+
this.validateKey(key);
|
|
25
|
+
const nonce = Buffer.from(
|
|
26
|
+
sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES),
|
|
27
|
+
);
|
|
28
|
+
const ciphertext = Buffer.from(sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
|
29
|
+
this.toBuffer(value),
|
|
30
|
+
this.toOptionalBuffer(associatedData),
|
|
31
|
+
null,
|
|
32
|
+
nonce,
|
|
33
|
+
key,
|
|
34
|
+
));
|
|
35
|
+
|
|
36
|
+
return Buffer.concat([nonce, ciphertext]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public decrypt(value: string | Buffer, key: Buffer, associatedData?: string | Buffer): Buffer {
|
|
40
|
+
this.validateKey(key);
|
|
41
|
+
const encrypted = typeof value === 'string'
|
|
42
|
+
? Buffer.from(value, 'base64')
|
|
43
|
+
: value;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const nonce = encrypted.subarray(0, sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
|
|
47
|
+
const ciphertext = encrypted.subarray(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
|
|
48
|
+
|
|
49
|
+
return Buffer.from(sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
|
|
50
|
+
null,
|
|
51
|
+
ciphertext,
|
|
52
|
+
this.toOptionalBuffer(associatedData),
|
|
53
|
+
nonce,
|
|
54
|
+
key,
|
|
55
|
+
));
|
|
56
|
+
} catch {
|
|
57
|
+
throw new InvalidDecryptionKeyExpection();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private validateKey(key: Buffer): void {
|
|
62
|
+
if (key.length !== sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) {
|
|
63
|
+
throw new InvalidEncryptionKeyException();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private toBuffer(value: string | Buffer): Buffer {
|
|
68
|
+
return typeof value === 'string'
|
|
69
|
+
? Buffer.from(value, 'utf8')
|
|
70
|
+
: value;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private toOptionalBuffer(value?: string | Buffer): Buffer | null {
|
|
74
|
+
if (!value) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return this.toBuffer(value);
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/base/index.ts
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type SerializableKey<TKey = unknown> = {
|
|
2
|
+
readonly nativeKey: TKey;
|
|
3
|
+
|
|
4
|
+
toString(): string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type PublicKey<TKey = unknown> = SerializableKey<TKey>;
|
|
8
|
+
|
|
9
|
+
export type PrivateKey<
|
|
10
|
+
TNativeKey = unknown,
|
|
11
|
+
TPublicKey extends PublicKey = PublicKey,
|
|
12
|
+
> = SerializableKey<TNativeKey> & {
|
|
13
|
+
toPublicKey(): TPublicKey;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type EncryptingPublicKey = {
|
|
17
|
+
encrypt(value: string | Buffer): Buffer;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type DecryptingPrivateKey = {
|
|
21
|
+
decrypt(value: string | Buffer): string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type SigningPrivateKey = {
|
|
25
|
+
sign(value: string): Buffer;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type VerifyingPublicKey = {
|
|
29
|
+
verify(signature: string | Buffer, value: string): boolean;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type KemEncapsulation = {
|
|
33
|
+
cipherText: Buffer;
|
|
34
|
+
sharedSecret: Buffer;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type EncapsulatingPublicKey = {
|
|
38
|
+
encapsulate(message?: string | Buffer): KemEncapsulation;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type DecapsulatingPrivateKey = {
|
|
42
|
+
decapsulate(cipherText: string | Buffer): Buffer;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type KeyAgreementPublicKey<TKey = unknown> = PublicKey<TKey>;
|
|
46
|
+
|
|
47
|
+
export type KeyAgreementPrivateKey<
|
|
48
|
+
TPublicKey extends KeyAgreementPublicKey,
|
|
49
|
+
TNativeKey = unknown,
|
|
50
|
+
> = PrivateKey<TNativeKey, TPublicKey> & KeyAgreement<TPublicKey>;
|
|
51
|
+
|
|
52
|
+
export type KeyAgreement<TPublicKey extends KeyAgreementPublicKey> = {
|
|
53
|
+
deriveSharedSecret(peerPublicKey: TPublicKey): Buffer;
|
|
54
|
+
};
|
package/src/base/key-pair.ts
CHANGED
|
@@ -4,12 +4,15 @@ import {
|
|
|
4
4
|
} from './private-key.js';
|
|
5
5
|
import { BasePublicKey } from './public-key.js';
|
|
6
6
|
|
|
7
|
-
export type NewKeyPairOptions = {
|
|
7
|
+
export type NewKeyPairOptions<TBits extends number = number> = {
|
|
8
8
|
seed?: string;
|
|
9
|
-
bits?:
|
|
9
|
+
bits?: TBits;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
export type KeyPairOptions<
|
|
12
|
+
export type KeyPairOptions<
|
|
13
|
+
TPrivateKey extends BasePrivateKey,
|
|
14
|
+
TBits extends number = number
|
|
15
|
+
> = NewKeyPairOptions<TBits> & {
|
|
13
16
|
privateKey?: TPrivateKey | string;
|
|
14
17
|
};
|
|
15
18
|
|
|
@@ -36,14 +39,6 @@ export abstract class BaseKeyPair<
|
|
|
36
39
|
this.publicKeyInstance = keyPair.publicKey;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
public abstract encrypt(value: string | Buffer, ...additionalArguments: unknown[]): Buffer;
|
|
40
|
-
|
|
41
|
-
public abstract decrypt(value: string | Buffer, ...additionalArguments: unknown[]): string;
|
|
42
|
-
|
|
43
|
-
public abstract sign(value: string): Buffer;
|
|
44
|
-
|
|
45
|
-
public abstract verify(signature: string | Buffer, value: string): boolean;
|
|
46
|
-
|
|
47
42
|
public get publicKey(): string {
|
|
48
43
|
return this.publicKeyInstance.toString();
|
|
49
44
|
}
|
package/src/base/key.ts
CHANGED
package/src/base/private-key.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseKey } from './key.js';
|
|
2
|
+
import { PrivateKey } from './key-capabilities.js';
|
|
2
3
|
import { BasePublicKey } from './public-key.js';
|
|
3
4
|
|
|
4
5
|
export type StaticPrivateKey<T extends BasePrivateKey> = Class<T, any[]> & {
|
|
@@ -8,10 +9,6 @@ export type StaticPrivateKey<T extends BasePrivateKey> = Class<T, any[]> & {
|
|
|
8
9
|
export abstract class BasePrivateKey<
|
|
9
10
|
TNativeKey = unknown,
|
|
10
11
|
TPublicKey extends BasePublicKey = BasePublicKey,
|
|
11
|
-
> extends BaseKey<TNativeKey> {
|
|
12
|
-
public abstract decrypt(encrypted: string | Buffer, ...additionalArguments: unknown[]): string;
|
|
13
|
-
|
|
14
|
-
public abstract sign(value: string): Buffer;
|
|
15
|
-
|
|
12
|
+
> extends BaseKey<TNativeKey> implements PrivateKey<TNativeKey, TPublicKey> {
|
|
16
13
|
public abstract toPublicKey(): TPublicKey;
|
|
17
14
|
}
|
package/src/base/public-key.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { BaseKey } from './key.js';
|
|
2
|
+
import { PublicKey } from './key-capabilities.js';
|
|
2
3
|
|
|
3
|
-
export abstract class BasePublicKey<TNativeKey = unknown>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
public abstract verify(signature: string | Buffer, value: string): boolean;
|
|
7
|
-
}
|
|
4
|
+
export abstract class BasePublicKey<TNativeKey = unknown>
|
|
5
|
+
extends BaseKey<TNativeKey>
|
|
6
|
+
implements PublicKey<TNativeKey> {}
|
package/src/exceptions/index.ts
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
+
export * from './hkdf-output-length.exception.js';
|
|
1
2
|
export * from './invalid-decryption-key.expection.js';
|
|
3
|
+
export * from './invalid-encryption-key.exception.js';
|
|
4
|
+
export * from './invalid-pq-kem-key-length.exception.js';
|
|
5
|
+
export * from './invalid-pq-kem-message-length.exception.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BaseException } from '@hg-ts/exception';
|
|
2
|
+
|
|
3
|
+
export class InvalidPqKemMessageLengthException extends BaseException {
|
|
4
|
+
public constructor(expectedLength: number, actualLength: number) {
|
|
5
|
+
super(`PQKEM encapsulation message must be ${expectedLength} bytes, got ${actualLength}`);
|
|
6
|
+
}
|
|
7
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ml_kem1024 as mlKem1024,
|
|
3
|
+
ml_kem512 as mlKem512,
|
|
4
|
+
ml_kem768 as mlKem768,
|
|
5
|
+
} from '@noble/post-quantum/ml-kem.js';
|
|
6
|
+
import type { KEM } from '@noble/post-quantum/utils.js';
|
|
7
|
+
|
|
8
|
+
import { InvalidPqKemKeyLengthException } from '../exceptions/index.js';
|
|
9
|
+
|
|
10
|
+
export type PqKemAlgorithm = 512 | 768 | 1024;
|
|
11
|
+
|
|
12
|
+
export const DEFAULT_PQ_KEM_ALGORITHM: PqKemAlgorithm = 768;
|
|
13
|
+
|
|
14
|
+
export const PQ_KEM_ALGORITHMS = [
|
|
15
|
+
512,
|
|
16
|
+
768,
|
|
17
|
+
1024,
|
|
18
|
+
] as const satisfies readonly PqKemAlgorithm[];
|
|
19
|
+
|
|
20
|
+
export type PqKemImplementation = KEM & {
|
|
21
|
+
lengths: {
|
|
22
|
+
cipherText: number;
|
|
23
|
+
msg: number;
|
|
24
|
+
publicKey: number;
|
|
25
|
+
secretKey: number;
|
|
26
|
+
seed: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const IMPLEMENTATIONS: Record<PqKemAlgorithm, PqKemImplementation> = {
|
|
31
|
+
512: mlKem512 as PqKemImplementation,
|
|
32
|
+
768: mlKem768 as PqKemImplementation,
|
|
33
|
+
1024: mlKem1024 as PqKemImplementation,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function getPqKemImplementation(bits: PqKemAlgorithm): PqKemImplementation {
|
|
37
|
+
return IMPLEMENTATIONS[bits];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function inferPqKemBitsByKeyLength(
|
|
41
|
+
length: number,
|
|
42
|
+
keyType: 'publicKey' | 'secretKey',
|
|
43
|
+
): PqKemAlgorithm {
|
|
44
|
+
for (const [bits, candidate] of Object.entries(IMPLEMENTATIONS)) {
|
|
45
|
+
if (candidate.lengths[keyType] === length) {
|
|
46
|
+
return Number(bits) as PqKemAlgorithm;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
throw new InvalidPqKemKeyLengthException(keyType, length);
|
|
51
|
+
}
|