Skip to content

Commit 18dd3cb

Browse files
authored
fix: add public/private key type disambiguators (#2698)
To give typescript some hints about object types, add `isPrivateKey` and `isPublicKey` functions.
1 parent 1210884 commit 18dd3cb

File tree

4 files changed

+78
-0
lines changed

4 files changed

+78
-0
lines changed

packages/crypto/test/keys/ed25519.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* eslint-env mocha */
2+
import { isPrivateKey, isPublicKey } from '@libp2p/interface'
23
import { expect } from 'aegir/chai'
34
import { Uint8ArrayList } from 'uint8arraylist'
45
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
@@ -156,6 +157,20 @@ describe('ed25519', function () {
156157
expect(key.publicKey.equals(imported)).to.be.true()
157158
})
158159

160+
it('is PrivateKey', async () => {
161+
const key = await generateKeyPair('Ed25519')
162+
163+
expect(isPrivateKey(key)).to.be.true()
164+
expect(isPublicKey(key)).to.be.false()
165+
})
166+
167+
it('is PublicKey', async () => {
168+
const key = await generateKeyPair('Ed25519')
169+
170+
expect(isPrivateKey(key.publicKey)).to.be.false()
171+
expect(isPublicKey(key.publicKey)).to.be.true()
172+
})
173+
159174
describe('go interop', () => {
160175
// @ts-check
161176
it('verifies with data from go', async () => {

packages/crypto/test/keys/rsa.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint max-nested-callbacks: ["error", 8] */
22
/* eslint-env mocha */
3+
import { isPrivateKey, isPublicKey } from '@libp2p/interface'
34
import { expect } from 'aegir/chai'
45
import { Uint8ArrayList } from 'uint8arraylist'
56
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
@@ -115,6 +116,20 @@ describe('RSA', function () {
115116
expect(key.publicKey.equals(imported)).to.be.true()
116117
})
117118

119+
it('is PrivateKey', async () => {
120+
const key = await generateKeyPair('RSA', 512)
121+
122+
expect(isPrivateKey(key)).to.be.true()
123+
expect(isPublicKey(key)).to.be.false()
124+
})
125+
126+
it('is PublicKey', async () => {
127+
const key = await generateKeyPair('RSA', 512)
128+
129+
expect(isPrivateKey(key.publicKey)).to.be.false()
130+
expect(isPublicKey(key.publicKey)).to.be.true()
131+
})
132+
118133
describe('key equals', () => {
119134
it('equals itself', () => {
120135
expect(key.equals(key)).to.be.true()

packages/crypto/test/keys/secp256k1.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable @typescript-eslint/await-thenable */ // secp is sync in node, async in browsers
22
/* eslint-env mocha */
3+
import { isPrivateKey, isPublicKey } from '@libp2p/interface'
34
import { expect } from 'aegir/chai'
45
import { Uint8ArrayList } from 'uint8arraylist'
56
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
@@ -83,6 +84,20 @@ describe('secp256k1 keys', () => {
8384
expect(key.publicKey.equals(imported)).to.be.true()
8485
})
8586

87+
it('is PrivateKey', async () => {
88+
const key = await generateKeyPair('secp256k1')
89+
90+
expect(isPrivateKey(key)).to.be.true()
91+
expect(isPublicKey(key)).to.be.false()
92+
})
93+
94+
it('is PublicKey', async () => {
95+
const key = await generateKeyPair('secp256k1')
96+
97+
expect(isPrivateKey(key.publicKey)).to.be.false()
98+
expect(isPublicKey(key.publicKey)).to.be.true()
99+
})
100+
86101
describe('key equals', () => {
87102
it('equals itself', () => {
88103
expect(key.equals(key)).to.be.true()

packages/interface/src/keys/index.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,23 @@ export interface Ed25519PublicKey extends PublicKeyBase<'Ed25519', 0x0> {}
5656
export interface Secp256k1PublicKey extends PublicKeyBase<'secp256k1', 0x0> {}
5757
export type PublicKey = RSAPublicKey | Ed25519PublicKey | Secp256k1PublicKey
5858

59+
/**
60+
* Returns true if the passed argument has type overlap with the `PublicKey`
61+
* interface. Can be used to disambiguate object types.
62+
*/
63+
export function isPublicKey (key?: any): key is PublicKey {
64+
if (key == null) {
65+
return false
66+
}
67+
68+
return (key.type === 'RSA' || key.type === 'Ed25519' || key.type === 'secp256k1') &&
69+
key.raw instanceof Uint8Array &&
70+
typeof key.equals === 'function' &&
71+
typeof key.toMultihash === 'function' &&
72+
typeof key.toCID === 'function' &&
73+
typeof key.verify === 'function'
74+
}
75+
5976
/**
6077
* Generic private key interface
6178
*/
@@ -92,3 +109,19 @@ export interface RSAPrivateKey extends PrivateKeyBase<'RSA', RSAPublicKey> {}
92109
export interface Ed25519PrivateKey extends PrivateKeyBase<'Ed25519', Ed25519PublicKey> {}
93110
export interface Secp256k1PrivateKey extends PrivateKeyBase<'secp256k1', Secp256k1PublicKey> {}
94111
export type PrivateKey = RSAPrivateKey | Ed25519PrivateKey | Secp256k1PrivateKey
112+
113+
/**
114+
* Returns true if the passed argument has type overlap with the `PrivateKey`
115+
* interface. Can be used to disambiguate object types.
116+
*/
117+
export function isPrivateKey (key?: any): key is PrivateKey {
118+
if (key == null) {
119+
return false
120+
}
121+
122+
return (key.type === 'RSA' || key.type === 'Ed25519' || key.type === 'secp256k1') &&
123+
isPublicKey(key.publicKey) &&
124+
key.raw instanceof Uint8Array &&
125+
typeof key.equals === 'function' &&
126+
typeof key.sign === 'function'
127+
}

0 commit comments

Comments
 (0)