Skip to content

Commit 9f77f31

Browse files
committed
first version using native crypto via WebCrypto, or alternatively
NodeRSA in non-browser contexts.
1 parent 25c675a commit 9f77f31

File tree

16 files changed

+145
-139
lines changed

16 files changed

+145
-139
lines changed

src/crypto/ciphers.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
export { RSA } from './ciphers/RSA';
2-
export { JSEncryptRSA as RSAImpl } from './ciphers/JSEncryptRSA';
1+
export * from './ciphers/RSA';
2+
export * from './ciphers/NodeRSA';
3+
export * from './ciphers/WebCryptoRSA';
4+
5+
//export { JSEncryptRSA as RSAImpl } from './ciphers/JSEncryptRSA';
36

47
export { ChaCha20 } from './ciphers/ChaCha20';
58
export { ChaCha20js as ChaCha20Impl } from './ciphers/ChaCha20js';

src/crypto/ciphers/DelegatingRSAImpl.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ class DelegatingRSAImpl implements RSA {
2525

2626
}
2727

28-
async loadKeyPair(format: string, publicKey: string, privateKey?: string): Promise<void> {
29-
30-
format; // ignore
28+
async loadKeyPair(publicKey: string, privateKey?: string): Promise<void> {
3129

3230
if (this.initialized) {
3331
throw new Error('RSA key cannot be re-initialized.')

src/crypto/ciphers/JSEncryptRSA.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,7 @@ class JSEncryptRSA implements RSA {
5252
this.crypto.getKey();
5353
};
5454

55-
async loadKeyPair(format: string, publicKey: string, privateKey?: string) {
56-
57-
if (format !== JSEncryptRSA.PKCS8) {
58-
throw new Error("Currently only pkcs8 encoded RSA keys are supported, sorry");
59-
}
55+
async loadKeyPair(publicKey: string, privateKey?: string) {
6056

6157
this.crypto = new JSEncrypt();
6258
this.crypto.setPublicKey(publicKey);

src/crypto/ciphers/RSA.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ interface RSA {
66

77

88
generateKey(bits: number) : Promise<void>;
9-
loadKeyPair(format: string, publicKey: string, privateKey?: string) : Promise<void>;
9+
loadKeyPair(publicKey: string, privateKey?: string) : Promise<void>;
1010

1111
getPublicKey() : string;
1212
getPrivateKey() : string | undefined;

src/crypto/ciphers/WebCryptoRSAEncKP.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,7 @@ class WebCryptoRSAEncKP implements EncodingKeyPair {
2929
},
3030
true,
3131
['encrypt', 'decrypt']
32-
);
33-
34-
if (!(keyPair instanceof CryptoKeyPair)) {
35-
throw new Error('Could not generate RSA key pair using WebCrypto, params: ' + params);
36-
}
32+
);
3733

3834
this.privateKey = keyPair.privateKey;
3935

src/crypto/sign/NodeRSASigKP.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@ class NodeRSASigKP implements SignatureKeyPair {
1111
keyPair?: any;
1212

1313
async generateKey(params?: {b?: number}): Promise<void> {
14-
15-
this.keyPair = new NodeRSA({b: params?.b || 2048});
14+
15+
this.keyPair = new NodeRSA();
16+
17+
this.keyPair.setOptions({
18+
environment: 'browser'
19+
});
20+
21+
this.keyPair.generateKeyPair(params?.b || 2048);
1622

1723
this.publicKeyPEM = this.keyPair.exportKey('pkcs8-public-pem');
1824
this.privateKeyPEM = this.keyPair.exportKey('pkcs8-private-pem');
@@ -24,6 +30,11 @@ class NodeRSASigKP implements SignatureKeyPair {
2430
if (privateKey !== undefined) {
2531

2632
this.keyPair = new NodeRSA();
33+
34+
this.keyPair.setOptions({
35+
environment: 'browser'
36+
});
37+
2738
this.keyPair.importKey(privateKey, 'pkcs8-private-pem');
2839

2940
this.privateKeyPEM = privateKey;
@@ -36,6 +47,11 @@ class NodeRSASigKP implements SignatureKeyPair {
3647

3748
} else if (publicKey !== undefined) {
3849
this.keyPair = new NodeRSA();
50+
51+
this.keyPair.setOptions({
52+
environment: 'browser'
53+
});
54+
3955
this.keyPair.importKey(publicKey, 'pkcs8-public-pem');
4056

4157
this.publicKeyPEM = publicKey;

src/crypto/sign/WebCryptoRSASigKP.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,7 @@ class WebCryptoRSASigKP implements SignatureKeyPair {
2727
},
2828
true,
2929
['sign', 'verify']
30-
);
31-
32-
if (!(keyPair instanceof CryptoKeyPair)) {
33-
throw new Error('Could not generate RSA key pair using WebCrypto, params: ' + params);
34-
}
30+
);
3531

3632
this.privateKey = keyPair.privateKey;
3733

src/data/identity/Identity.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,21 @@ class Identity extends HashedObject {
4646
}
4747

4848
verifySignature(text: string, signature: string) {
49-
//text; signature; return true; // mock
50-
49+
5150
if (this.publicKey === undefined) {
5251
throw new Error('Cannot verify signature, Identity is uninitialized')
5352
}
5453

55-
5654
return this.publicKey.verifySignature(text, signature);
5755
}
5856

5957
encrypt(text: string) {
60-
// return text; // mock
61-
return this.publicKey?.encrypt(text);
58+
59+
if (this.publicKey === undefined) {
60+
throw new Error('Cannot ecnrypt, Identity is uninitialized')
61+
}
62+
63+
return this.publicKey.encrypt(text);
6264
}
6365

6466
getPublicKey() {

src/data/identity/RSAKeyPair.ts

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { RSA, RSAImpl } from 'crypto/ciphers';
1+
import { RSA, RSADefaults } from 'crypto/ciphers';
22
import { HashedObject } from '../model/HashedObject';
33
import { RSAPublicKey } from './RSAPublicKey';
44
import { Hashing } from 'data/model/Hashing';
@@ -17,57 +17,52 @@ class RSAKeyPair extends HashedObject {
1717
static className = 'hhs/v0/RSAKeyPair';
1818

1919
static async generate(bits: number) {
20-
let rsa = new RSAImpl();
21-
rsa.generateKey(bits);
20+
let rsa = new RSADefaults.impl();
21+
await rsa.generateKey(bits);
2222

23-
return RSAKeyPair.fromKeys(rsa.getFormat(), rsa.getPublicKey(), rsa.getPrivateKey());
23+
return RSAKeyPair.fromKeys(rsa.getPublicKey(), rsa.getPrivateKey());
2424
}
2525

26-
static async fromKeys(format: string, publicKey: string, privateKey: string) {
26+
static async fromKeys(publicKey: string, privateKey?: string) {
2727
let keyPair = new RSAKeyPair();
28-
keyPair.format = format;
2928
keyPair.publicKey = publicKey;
3029
keyPair.privateKey = privateKey;
31-
await keyPair.initRSA();
30+
keyPair.init();
3231
await keyPair.selfSign();
3332
return keyPair;
3433
}
3534

36-
format?: string;
3735
publicKey?: string;
3836
privateKey?: string;
3937
privateKeySignature?: string;
4038

41-
_rsa?: RSA;
39+
_rsaPromise?: Promise<RSA>;
4240

4341
constructor() {
4442
super();
4543
}
4644

47-
async init() {
48-
this.initRSA();
49-
if (!this.checkSelfSignature()) {
50-
throw new Error("Self signature check failed for private key");
51-
}
45+
init() {
46+
this._rsaPromise = this.initRSA();
5247
}
5348

5449
async validate() {
55-
await this.initRSA();
5650
return this.checkSelfSignature();
5751
}
5852

59-
private async initRSA() {
60-
this._rsa = new RSAImpl();
61-
this._rsa.loadKeyPair(this.getFormat(), this.getPublicKey(), this.getPrivateKey());
53+
private async initRSA(): Promise<RSA> {
54+
const _rsa = new RSADefaults.impl();
55+
await _rsa.loadKeyPair(this.getPublicKey(), this.getPrivateKey());
56+
return _rsa;
6257
}
6358

6459
private async selfSign() {
6560

66-
if (this._rsa === undefined) {
61+
if (this._rsaPromise === undefined) {
6762
throw new Error('Attempting to self sign keypair, but RSA has not been initialized.');
6863
}
6964

70-
this.privateKeySignature = await this._rsa.sign(this.privateKey as string);
65+
this.privateKeySignature = await (await this._rsaPromise).sign(this.privateKey as string);
7166
}
7267

7368
private checkSelfSignature() {
@@ -79,11 +74,7 @@ class RSAKeyPair extends HashedObject {
7974
}
8075

8176
customHash(seed?: string) {
82-
return RSAKeyPair.hashPublicKeyPart(this.format as string, this.publicKey as string, seed);
83-
}
84-
85-
getFormat(): string {
86-
return this.format as string;
77+
return RSAKeyPair.hashPublicKeyPart(this.publicKey as string, seed);
8778
}
8879

8980
getPublicKey() {
@@ -95,42 +86,47 @@ class RSAKeyPair extends HashedObject {
9586
}
9687

9788
makePublicKey() {
98-
return RSAPublicKey.fromKeys(this.getFormat(), this.getPublicKey());
89+
return RSAPublicKey.fromKeys(this.getPublicKey());
9990
}
10091

101-
sign(text: string) {
92+
async sign(text: string) {
10293

103-
if (this._rsa === undefined) {
94+
if (this._rsaPromise === undefined) {
10495
throw new Error('Attempting to create signature, but RSA has not been initialized.');
10596
}
10697

107-
return this._rsa.sign(text);
98+
return (await this._rsaPromise).sign(text);
10899
}
109100

110-
verifySignature(text: string, signature: string) {
111-
return this._rsa?.verify(text, signature);
101+
async verifySignature(text: string, signature: string) {
102+
103+
if (this._rsaPromise === undefined) {
104+
throw new Error('Attempting to verify signature, but RSA has not been initialized.');
105+
}
106+
107+
return (await this._rsaPromise).verify(text, signature);
112108
}
113109

114-
encrypt(plainText: string) {
110+
async encrypt(plainText: string) {
115111

116-
if (this._rsa === undefined) {
112+
if (this._rsaPromise === undefined) {
117113
throw new Error('Attempting to encrypt, but RSA has not been initialized.');
118114
}
119115

120-
return this._rsa.encrypt(plainText);
116+
return (await this._rsaPromise).encrypt(plainText);
121117
}
122118

123-
decrypt(cypherText : string) {
119+
async decrypt(cypherText : string) {
124120

125-
if (this._rsa === undefined) {
121+
if (this._rsaPromise === undefined) {
126122
throw new Error('Attempting to decrypt, but RSA has not been initialized.');
127123
}
128124

129-
return this._rsa?.decrypt(cypherText);
125+
return (await this._rsaPromise).decrypt(cypherText);
130126
}
131127

132-
static hashPublicKeyPart(format: string, publicKey: string, seed?: string) {
133-
return Hashing.forValue({'_type': 'custom_hashed_object', '_class': RSAKeyPair.className, '_contents': {'format' : format, 'publicKey': publicKey}}, seed);
128+
static hashPublicKeyPart(publicKey: string, seed?: string) {
129+
return Hashing.forValue({'_type': 'custom_hashed_object', '_class': RSAKeyPair.className, '_contents': {'publicKey': publicKey}}, seed);
134130
}
135131
}
136132

src/data/identity/RSAPublicKey.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,73 @@
1-
import { RSA, RSAImpl } from 'crypto/ciphers';
1+
import { RSA, RSADefaults } from 'crypto/ciphers';
22
import { HashedObject } from 'data/model/HashedObject';
33
import { RSAKeyPair } from './RSAKeyPair';
44

55
class RSAPublicKey extends HashedObject {
66

77
static className = 'hhs/v0/RSAPublicKey';
88

9-
static fromKeys(format: string, publicKey: string) : RSAPublicKey {
9+
static fromKeys(publicKey: string) : RSAPublicKey {
1010

1111
let pk = new RSAPublicKey();
1212

13-
pk.format = format;
1413
pk.publicKey = publicKey;
1514

1615
pk.init();
1716

1817
return pk;
1918
}
2019

21-
format?: string;
2220
publicKey?: string;
2321

24-
_rsa?: RSA;
22+
_rsaPromise?: Promise<RSA>;
2523

2624
constructor() {
2725
super();
2826
}
2927

3028
init() {
31-
this._rsa = new RSAImpl();
32-
this._rsa.loadKeyPair(this.getFormat(), this.getPublicKey());
29+
this._rsaPromise = this.initRSA();
30+
}
31+
32+
private async initRSA(): Promise<RSA> {
33+
const _rsa = new RSADefaults.impl();
34+
await _rsa.loadKeyPair(this.getPublicKey());
35+
return _rsa;
3336
}
3437

3538
async validate() {
39+
// TODO: self sign??
3640
return true;
3741
}
3842

3943
getClassName() {
4044
return RSAPublicKey.className;
4145
}
4246

43-
getFormat() {
44-
return this.format as string;
45-
}
46-
4747
getPublicKey() {
4848
return this.publicKey as string;
4949
}
5050

5151
getKeyPairHash() {
52-
return RSAKeyPair.hashPublicKeyPart(this.format as string, this.publicKey as string);
52+
return RSAKeyPair.hashPublicKeyPart(this.publicKey as string);
5353
}
5454

55-
verifySignature(text: string, signature: string) {
55+
async verifySignature(text: string, signature: string) {
5656

57-
if (this._rsa === undefined) {
57+
if (this._rsaPromise === undefined) {
5858
throw new Error('RSA public key is empty, cannot verify signature');
5959
}
6060

61-
return this._rsa.verify(text, signature);
61+
return (await this._rsaPromise).verify(text, signature);
6262
}
6363

64-
encrypt(plainText: string) {
65-
return this._rsa?.encrypt(plainText);
64+
async encrypt(plainText: string) {
65+
66+
if (this._rsaPromise === undefined) {
67+
throw new Error('RSA public key is empty, cannot encrypt');
68+
}
69+
70+
return (await this._rsaPromise).encrypt(plainText);
6671
}
6772

6873
}

0 commit comments

Comments
 (0)