Skip to content

Commit 133cef8

Browse files
authored
feat: implement generateKeyPair DSA and DH key types (#925)
1 parent 314d8d2 commit 133cef8

20 files changed

Lines changed: 1149 additions & 7 deletions

.docs/implementation-coverage.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ These algorithms provide quantum-resistant cryptography.
127127
*`crypto.encapsulate(key[, callback])`
128128
* `-` `crypto.fips` deprecated, not applicable to RN
129129
*`crypto.generateKey(type, options, callback)`
130-
* 🚧 `crypto.generateKeyPair(type, options, callback)`
131-
* 🚧 `crypto.generateKeyPairSync(type, options)`
132-
* 🚧 `crypto.generateKeySync(type, options)`
130+
* `crypto.generateKeyPair(type, options, callback)`
131+
* `crypto.generateKeyPairSync(type, options)`
132+
* `crypto.generateKeySync(type, options)`
133133
*`crypto.generatePrime(size[, options[, callback]])`
134134
*`crypto.generatePrimeSync(size[, options])`
135135
*`crypto.getCipherInfo(nameOrNid[, options])`
@@ -182,26 +182,26 @@ These algorithms provide quantum-resistant cryptography.
182182
| --------- | :----: |
183183
| `rsa` ||
184184
| `rsa-pss` ||
185-
| `dsa` | |
185+
| `dsa` | |
186186
| `ec` ||
187187
| `ed25519` ||
188188
| `ed448` ||
189189
| `x25519` ||
190190
| `x448` ||
191-
| `dh` | |
191+
| `dh` | |
192192

193193
## `crypto.generateKeyPairSync`
194194
| type | Status |
195195
| --------- | :----: |
196196
| `rsa` ||
197197
| `rsa-pss` ||
198-
| `dsa` | |
198+
| `dsa` | |
199199
| `ec` ||
200200
| `ed25519` ||
201201
| `ed448` ||
202202
| `x25519` ||
203203
| `x448` ||
204-
| `dh` | |
204+
| `dh` | |
205205

206206
## `crypto.generateKeySync`
207207
| type | Status |

example/src/tests/keys/generate_keypair.ts

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,3 +636,202 @@ test(SUITE, 'generateKeyPairSync EC with DER encoding', () => {
636636
expect((privateKey as ArrayBuffer).byteLength).to.be.greaterThan(0);
637637
expect((publicKey as ArrayBuffer).byteLength).to.be.greaterThan(0);
638638
});
639+
640+
// --- DSA Key Generation Tests ---
641+
642+
test(SUITE, 'generateKeyPair DSA 2048-bit with PEM encoding', async () => {
643+
const { privateKey, publicKey } = await new Promise<{
644+
privateKey: string;
645+
publicKey: string;
646+
}>((resolve, reject) => {
647+
generateKeyPair(
648+
'dsa',
649+
{
650+
modulusLength: 2048,
651+
publicKeyEncoding: { type: 'spki', format: 'pem' },
652+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
653+
},
654+
(err, pubKey, privKey) => {
655+
if (err) reject(err);
656+
else
657+
resolve({
658+
privateKey: privKey as string,
659+
publicKey: pubKey as string,
660+
});
661+
},
662+
);
663+
});
664+
665+
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
666+
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);
667+
});
668+
669+
test(SUITE, 'generateKeyPair DSA with custom divisorLength', async () => {
670+
const { privateKey, publicKey } = await new Promise<{
671+
privateKey: string;
672+
publicKey: string;
673+
}>((resolve, reject) => {
674+
generateKeyPair(
675+
'dsa',
676+
{
677+
modulusLength: 2048,
678+
divisorLength: 256,
679+
publicKeyEncoding: { type: 'spki', format: 'pem' },
680+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
681+
},
682+
(err, pubKey, privKey) => {
683+
if (err) reject(err);
684+
else
685+
resolve({
686+
privateKey: privKey as string,
687+
publicKey: pubKey as string,
688+
});
689+
},
690+
);
691+
});
692+
693+
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
694+
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);
695+
});
696+
697+
test(SUITE, 'generateKeyPair DSA with DER encoding', async () => {
698+
const { privateKey, publicKey } = await new Promise<{
699+
privateKey: ArrayBuffer;
700+
publicKey: ArrayBuffer;
701+
}>((resolve, reject) => {
702+
generateKeyPair(
703+
'dsa',
704+
{
705+
modulusLength: 2048,
706+
publicKeyEncoding: { type: 'spki', format: 'der' },
707+
privateKeyEncoding: { type: 'pkcs8', format: 'der' },
708+
},
709+
(err, pubKey, privKey) => {
710+
if (err) reject(err);
711+
else
712+
resolve({
713+
privateKey: privKey as ArrayBuffer,
714+
publicKey: pubKey as ArrayBuffer,
715+
});
716+
},
717+
);
718+
});
719+
720+
expect(privateKey instanceof ArrayBuffer).to.equal(true);
721+
expect(publicKey instanceof ArrayBuffer).to.equal(true);
722+
expect(privateKey.byteLength).to.be.greaterThan(0);
723+
expect(publicKey.byteLength).to.be.greaterThan(0);
724+
});
725+
726+
test(SUITE, 'generateKeyPairSync DSA 2048-bit', () => {
727+
const { privateKey, publicKey } = generateKeyPairSync('dsa', {
728+
modulusLength: 2048,
729+
publicKeyEncoding: { type: 'spki', format: 'pem' },
730+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
731+
});
732+
733+
expect(typeof privateKey).to.equal('string');
734+
expect(typeof publicKey).to.equal('string');
735+
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
736+
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);
737+
});
738+
739+
test(SUITE, 'generateKeyPairSync DSA keys work for signing', () => {
740+
const { privateKey, publicKey } = generateKeyPairSync('dsa', {
741+
modulusLength: 2048,
742+
publicKeyEncoding: { type: 'spki', format: 'pem' },
743+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
744+
});
745+
746+
const testData = 'Test data for DSA signing';
747+
const signature = createSign('SHA256')
748+
.update(testData)
749+
.sign(privateKey as string);
750+
const isValid = createVerify('SHA256')
751+
.update(testData)
752+
.verify(publicKey as string, signature);
753+
754+
expect(isValid).to.equal(true);
755+
});
756+
757+
// --- DH Key Generation Tests ---
758+
759+
test(SUITE, 'generateKeyPair DH with named group modp14', async () => {
760+
const { privateKey, publicKey } = await new Promise<{
761+
privateKey: string;
762+
publicKey: string;
763+
}>((resolve, reject) => {
764+
generateKeyPair(
765+
'dh',
766+
{
767+
groupName: 'modp14',
768+
publicKeyEncoding: { type: 'spki', format: 'pem' },
769+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
770+
},
771+
(err, pubKey, privKey) => {
772+
if (err) reject(err);
773+
else
774+
resolve({
775+
privateKey: privKey as string,
776+
publicKey: pubKey as string,
777+
});
778+
},
779+
);
780+
});
781+
782+
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
783+
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);
784+
});
785+
786+
test(SUITE, 'generateKeyPair DH with primeLength', async () => {
787+
const { privateKey, publicKey } = await new Promise<{
788+
privateKey: string;
789+
publicKey: string;
790+
}>((resolve, reject) => {
791+
generateKeyPair(
792+
'dh',
793+
{
794+
primeLength: 2048,
795+
publicKeyEncoding: { type: 'spki', format: 'pem' },
796+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
797+
},
798+
(err, pubKey, privKey) => {
799+
if (err) reject(err);
800+
else
801+
resolve({
802+
privateKey: privKey as string,
803+
publicKey: pubKey as string,
804+
});
805+
},
806+
);
807+
});
808+
809+
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
810+
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);
811+
});
812+
813+
test(SUITE, 'generateKeyPairSync DH with named group', () => {
814+
const { privateKey, publicKey } = generateKeyPairSync('dh', {
815+
groupName: 'modp14',
816+
publicKeyEncoding: { type: 'spki', format: 'pem' },
817+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
818+
});
819+
820+
expect(typeof privateKey).to.equal('string');
821+
expect(typeof publicKey).to.equal('string');
822+
expect(privateKey).to.match(/^-----BEGIN PRIVATE KEY-----/);
823+
expect(publicKey).to.match(/^-----BEGIN PUBLIC KEY-----/);
824+
});
825+
826+
test(SUITE, 'generateKeyPairSync DH with DER encoding', () => {
827+
const { privateKey, publicKey } = generateKeyPairSync('dh', {
828+
groupName: 'modp14',
829+
publicKeyEncoding: { type: 'spki', format: 'der' },
830+
privateKeyEncoding: { type: 'pkcs8', format: 'der' },
831+
});
832+
833+
expect(privateKey instanceof ArrayBuffer).to.equal(true);
834+
expect(publicKey instanceof ArrayBuffer).to.equal(true);
835+
expect((privateKey as ArrayBuffer).byteLength).to.be.greaterThan(0);
836+
expect((publicKey as ArrayBuffer).byteLength).to.be.greaterThan(0);
837+
});

packages/react-native-quick-crypto/android/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ add_library(
3939
../cpp/cipher/ChaCha20Cipher.cpp
4040
../cpp/cipher/ChaCha20Poly1305Cipher.cpp
4141
../cpp/dh/HybridDiffieHellman.cpp
42+
../cpp/dh/HybridDhKeyPair.cpp
43+
../cpp/dsa/HybridDsaKeyPair.cpp
4244
../cpp/ec/HybridEcKeyPair.cpp
4345
../cpp/ecdh/HybridECDH.cpp
4446
../cpp/ed25519/HybridEdKeyPair.cpp
@@ -74,6 +76,7 @@ include_directories(
7476
"../cpp/certificate"
7577
"../cpp/cipher"
7678
"../cpp/dh"
79+
"../cpp/dsa"
7780
"../cpp/ec"
7881
"../cpp/ecdh"
7982
"../cpp/ed25519"

0 commit comments

Comments
 (0)