Skip to content
This repository has been archived by the owner on Jul 21, 2023. It is now read-only.

Commit

Permalink
feat: add typescript types + linting/tests (#161)
Browse files Browse the repository at this point in the history
* feat: adds typescript types + linting/tests

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>

* feat: much better types testing

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>

* chore: revert eslintignore

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>

* feat: update types entry

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>

* chore: exclude has no effect here

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>

* feat: more nuanced return types on keypair

Signed-off-by: Carson Farmer <carson.farmer@gmail.com>
  • Loading branch information
carsonfarmer authored and jacobheun committed Jan 17, 2020
1 parent b5d94ec commit e01977c
Show file tree
Hide file tree
Showing 13 changed files with 449 additions and 12 deletions.
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "0.17.1",
"description": "Crypto primitives for libp2p",
"main": "src/index.js",
"types": "src/index.d.ts",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"browser": {
"./src/hmac/index.js": "./src/hmac/index-browser.js",
Expand All @@ -25,7 +26,8 @@
"release-minor": "aegir release --type minor",
"release-major": "aegir release --type major",
"coverage": "aegir coverage --ignore src/keys/keys.proto.js",
"size": "aegir build --bundlesize"
"size": "aegir build --bundlesize",
"test:types": "npx tsc"
},
"keywords": [
"IPFS",
Expand Down Expand Up @@ -53,6 +55,12 @@
"ursa-optional": "~0.10.1"
},
"devDependencies": {
"@types/bn.js": "^4.11.6",
"@types/chai": "^4.2.7",
"@types/chai-string": "^1.4.2",
"@types/dirty-chai": "^2.0.2",
"@types/mocha": "^5.2.7",
"@types/sinon": "^7.5.1",
"aegir": "^20.4.1",
"benchmark": "^2.1.4",
"chai": "^4.2.0",
Expand Down
345 changes: 345 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,345 @@
/// <reference types="node" />

/**
* Supported key types.
*/
export type KeyType = "Ed25519" | "RSA" | "secp256k1";

/**
* Maps an IPFS hash name to its node-forge equivalent.
* See https://github.com/multiformats/multihash/blob/master/hashtable.csv
*/
export type HashType = "SHA1" | "SHA256" | "SHA512";

/**
* Supported curve types.
*/
export type CurveType = "P-256" | "P-384" | "P-521";

/**
* Supported cipher types.
*/
export type CipherType = "AES-128" | "AES-256" | "Blowfish";

/**
* Exposes an interface to AES encryption (formerly Rijndael),
* as defined in U.S. Federal Information Processing Standards Publication 197.
* This uses CTR mode.
*/
export namespace aes {
/**
* AES Cipher in CTR mode.
*/
interface Cipher {
encrypt(data: Buffer): Promise<Buffer>;
decrypt(data: Buffer): Promise<Buffer>;
}
/**
* Create a new AES Cipher.
* @param key The key, if length 16 then AES 128 is used. For length 32, AES 256 is used.
* @param iv Must have length 16.
*/
function create(key: Buffer, iv: Buffer): Promise<Cipher>;
}

/**
* Exposes an interface to the Keyed-Hash Message Authentication Code (HMAC)
* as defined in U.S. Federal Information Processing Standards Publication 198.
* An HMAC is a cryptographic hash that uses a key to sign a message.
* The receiver verifies the hash by recomputing it using the same key.
*/
export namespace hmac {
/**
* HMAC Digest.
*/
interface Digest {
digest(data: Buffer): Promise<Buffer>;
length: 20 | 32 | 64 | number;
}
/**
* Create a new HMAC Digest.
*/
function create(
hash: "SHA1" | "SHA256" | "SHA512" | string,
secret: Buffer
): Promise<Digest>;
}

/**
* Generic public key interface.
*/
export interface PublicKey {
readonly bytes: Buffer;
verify(data: Buffer, sig: Buffer): Promise<boolean>;
marshal(): Buffer;
equals(key: PublicKey): boolean;
hash(): Promise<Buffer>;
}

/**
* Generic private key interface.
*/
export interface PrivateKey {
readonly public: PublicKey;
readonly bytes: Buffer;
sign(data: Buffer): Promise<Buffer>;
marshal(): Buffer;
equals(key: PrivateKey): boolean;
hash(): Promise<Buffer>;
/**
* Gets the ID of the key.
*
* The key id is the base58 encoding of the SHA-256 multihash of its public key.
* The public key is a protobuf encoding containing a type and the DER encoding
* of the PKCS SubjectPublicKeyInfo.
*/
id(): Promise<string>;
}

export interface Keystretcher {
(res: Buffer): Keystretcher;
iv: Buffer;
cipherKey: Buffer;
macKey: Buffer;
}

export interface StretchPair {
k1: Keystretcher;
k2: Keystretcher;
}

/**
* Exposes an interface to various cryptographic key generation routines.
* Currently the 'RSA' and 'ed25519' types are supported, although ed25519 keys
* support only signing and verification of messages. For encryption / decryption
* support, RSA keys should be used.
* Installing the libp2p-crypto-secp256k1 module adds support for the 'secp256k1'
* type, which supports ECDSA signatures using the secp256k1 elliptic curve
* popularized by Bitcoin. This module is not installed by default, and should be
* explicitly depended on if your project requires secp256k1 support.
*/
export namespace keys {
export {};
export namespace supportedKeys {
namespace rsa {
class RsaPublicKey implements PublicKey {
constructor(key: Buffer);
readonly bytes: Buffer;
verify(data: Buffer, sig: Buffer): Promise<boolean>;
marshal(): Buffer;
encrypt(bytes: Buffer): Buffer;
equals(key: PublicKey): boolean;
hash(): Promise<Buffer>;
}

// Type alias for export method
export type KeyInfo = any;

class RsaPrivateKey implements PrivateKey {
constructor(key: any, publicKey: Buffer);
readonly public: RsaPublicKey;
readonly bytes: Buffer;
genSecret(): Buffer;
sign(data: Buffer): Promise<Buffer>;
decrypt(bytes: Buffer): Buffer;
marshal(): Buffer;
equals(key: PrivateKey): boolean;
hash(): Promise<Buffer>;
id(): Promise<string>;
/**
* Exports the key into a password protected PEM format
*
* @param password The password to read the encrypted PEM
* @param format Defaults to 'pkcs-8'.
*/
export(password: string, format?: "pkcs-8" | string): KeyInfo;
}
function unmarshalRsaPublicKey(buf: Buffer): RsaPublicKey;
function unmarshalRsaPrivateKey(buf: Buffer): Promise<RsaPrivateKey>;
function generateKeyPair(bits: number): Promise<RsaPrivateKey>;
function fromJwk(jwk: Buffer): Promise<RsaPrivateKey>;
}

namespace ed25519 {
class Ed25519PublicKey implements PublicKey {
constructor(key: Buffer);
readonly bytes: Buffer;
verify(data: Buffer, sig: Buffer): Promise<boolean>;
marshal(): Buffer;
encrypt(bytes: Buffer): Buffer;
equals(key: PublicKey): boolean;
hash(): Promise<Buffer>;
}

class Ed25519PrivateKey implements PrivateKey {
constructor(key: Buffer, publicKey: Buffer);
readonly public: Ed25519PublicKey;
readonly bytes: Buffer;
sign(data: Buffer): Promise<Buffer>;
marshal(): Buffer;
equals(key: PrivateKey): boolean;
hash(): Promise<Buffer>;
id(): Promise<string>;
}

function unmarshalEd25519PrivateKey(
buf: Buffer
): Promise<Ed25519PrivateKey>;
function unmarshalEd25519PublicKey(buf: Buffer): Ed25519PublicKey;
function generateKeyPair(): Promise<Ed25519PrivateKey>;
function generateKeyPairFromSeed(
seed: Buffer
): Promise<Ed25519PrivateKey>;
}

namespace secp256k1 {
class Secp256k1PublicKey implements PublicKey {
constructor(key: Buffer);
readonly bytes: Buffer;
verify(data: Buffer, sig: Buffer): Promise<boolean>;
marshal(): Buffer;
encrypt(bytes: Buffer): Buffer;
equals(key: PublicKey): boolean;
hash(): Promise<Buffer>;
}

class Secp256k1PrivateKey implements PrivateKey {
constructor(key: Uint8Array | Buffer, publicKey: Uint8Array | Buffer);
readonly public: Secp256k1PublicKey;
readonly bytes: Buffer;
sign(data: Buffer): Promise<Buffer>;
marshal(): Buffer;
equals(key: PrivateKey): boolean;
hash(): Promise<Buffer>;
id(): Promise<string>;
}

function unmarshalSecp256k1PrivateKey(
bytes: Buffer
): Promise<Secp256k1PrivateKey>;
function unmarshalSecp256k1PublicKey(bytes: Buffer): Secp256k1PublicKey;
function generateKeyPair(): Promise<Secp256k1PrivateKey>;
}
}

export const keysPBM: any;

/**
* Generates a keypair of the given type and bitsize.
* @param type One of the supported key types.
* @param bits Number of bits. Minimum of 1024.
*/
export function generateKeyPair(
type: KeyType | string,
bits: number
): Promise<PrivateKey>;
export function generateKeyPair(
type: "Ed25519",
bits: number
): Promise<keys.supportedKeys.ed25519.Ed25519PrivateKey>;
export function generateKeyPair(
type: "RSA",
bits: number
): Promise<keys.supportedKeys.rsa.RsaPrivateKey>;
export function generateKeyPair(
type: "secp256k1",
bits: number
): Promise<keys.supportedKeys.secp256k1.Secp256k1PrivateKey>;

/**
* Generates a keypair of the given type and bitsize.
* @param type One of the supported key types. Currently only 'Ed25519' is supported.
* @param seed A 32 byte uint8array.
* @param bits Number of bits. Minimum of 1024.
*/
export function generateKeyPairFromSeed(
type: KeyType | string,
seed: Uint8Array,
bits: number
): Promise<PrivateKey>;
export function generateKeyPairFromSeed(
type: "Ed25519",
seed: Uint8Array,
bits: number
): Promise<keys.supportedKeys.ed25519.Ed25519PrivateKey>;

/**
* Generates an ephemeral public key and returns a function that will compute the shared secret key.
* Focuses only on ECDH now, but can be made more general in the future.
* @param curve The curve to use. One of 'P-256', 'P-384', 'P-521' is currently supported.
*/
export function generateEphemeralKeyPair(
curve: CurveType | string
): Promise<{
key: Buffer;
genSharedKey: (theirPub: Buffer, forcePrivate?: any) => Promise<Buffer>;
}>;

/**
* Generates a set of keys for each party by stretching the shared key.
* @param cipherType The cipher type to use. One of 'AES-128', 'AES-256', or 'Blowfish'
* @param hashType The hash type to use. One of 'SHA1', 'SHA2256', or 'SHA2512'.
* @param secret The shared key secret.
*/
export function keyStretcher(
cipherType: CipherType | string,
hashType: HashType | string,
secret: Buffer | string
): Promise<StretchPair>;

/**
* Converts a protobuf serialized public key into its representative object.
* @param buf The protobuf serialized public key.
*/
export function unmarshalPublicKey(buf: Buffer): PublicKey;

/**
* Converts a public key object into a protobuf serialized public key.
* @param key An RSA, Ed25519, or Secp256k1 public key object.
* @param type One of the supported key types.
*/
export function marshalPublicKey(key: PublicKey, type?: KeyType | string): Buffer;

/**
* Converts a protobuf serialized private key into its representative object.
* @param buf The protobuf serialized private key.
*/
export function unmarshalPrivateKey(buf: Buffer): Promise<PrivateKey>;

/**
* Converts a private key object into a protobuf serialized private key.
* @param key An RSA, Ed25519, or Secp256k1 private key object.
* @param type One of the supported key types.
*/
export function marshalPrivateKey(key: PrivateKey, type?: KeyType | string): Buffer;

/**
* Converts a PEM password protected private key into its representative object.
* @param pem Password protected private key in PEM format.
* @param password The password used to protect the key.
*/
function _import(pem: string, password: string): Promise<supportedKeys.rsa.RsaPrivateKey>;
export { _import as import };
}

/**
* Generates a Buffer populated by random bytes.
* @param The size of the random bytes Buffer.
*/
export function randomBytes(number: number): Buffer;

/**
* Computes the Password-Based Key Derivation Function 2.
* @param password The password.
* @param salt The salt.
* @param iterations Number of iterations to use.
* @param keySize The size of the output key in bytes.
* @param hash The hash name ('sha1', 'sha2-512, ...)
*/
export function pbkdf2(
password: string | Buffer,
salt: string | Buffer,
iterations: number,
keySize: number,
hash: string
): Buffer;
1 change: 0 additions & 1 deletion src/keys/rsa-class.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ class RsaPrivateKey {
*
* @param {string} password - The password to read the encrypted PEM
* @param {string} [format] - Defaults to 'pkcs-8'.
* @returns {KeyInfo}
*/
async export (password, format = 'pkcs-8') { // eslint-disable-line require-await
let pem = null
Expand Down
5 changes: 4 additions & 1 deletion src/keys/rsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
const crypto = require('crypto')
const errcode = require('err-code')
const randomBytes = require('../random-bytes')

// @ts-check
/**
* @type {PrivateKey}
*/
let keypair
try {
if (process.env.LP2P_FORCE_CRYPTO_LIB === 'keypair') {
Expand Down
Loading

0 comments on commit e01977c

Please sign in to comment.