Compare commits

...

16 Commits

Author SHA1 Message Date
Jacob Heun
ad4bf3b357 chore: release version v0.17.2 2020-01-17 12:08:41 +01:00
Jacob Heun
730d762717 chore: update contributors 2020-01-17 12:08:40 +01:00
Carson Farmer
e01977c5a3 feat: add typescript types + linting/tests (#161)
* 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>
2020-01-17 12:04:52 +01:00
Jacob Heun
b5d94ecae7 chore: release version v0.17.1 2019-10-25 13:56:54 +02:00
Jacob Heun
1f9c2ddadb chore: update contributors 2019-10-25 13:56:53 +02:00
Jacob Heun
d6d06a8404 chore: remove commitlint
chore: update deps

chore: add bundlesize to ci
2019-10-25 13:51:00 +02:00
Maciej Krüger
8b8d0c1510 fix: jwk var naming 2019-10-25 13:51:00 +02:00
Maciej Krüger
b998f63aec feat: use forge to convert jwk2forge 2019-10-25 13:51:00 +02:00
Maciej Krüger
adc6eb478c test: add interop test 2019-10-25 13:51:00 +02:00
Maciej Krüger
2c1bac5ce9 fix: padding error 2019-10-25 13:51:00 +02:00
Maciej Krüger
027a5a9332 fix: use direct buffers instead of converting to hex 2019-10-25 13:51:00 +02:00
Maciej Krüger
2c294b56ab fix: lint 2019-10-25 13:51:00 +02:00
Maciej Krüger
487cd076fb refactor: cleanup 2019-10-25 13:51:00 +02:00
Maciej Krüger
b8e2414420 fix: browser rsa enc/dec 2019-10-25 13:51:00 +02:00
Maciej Krüger
9f747a173f feat: browser enc/dec 2019-10-25 13:51:00 +02:00
Maciej Krüger
34c5f5c8f0 feat: add (rsa)pubKey.encrypt and (rsa)privKey.decrypt
nodeJS only for now
2019-10-25 13:51:00 +02:00
23 changed files with 605 additions and 27 deletions

3
.aegir.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
bundlesize: { maxSize: '155kB' }
}

View File

@@ -23,7 +23,7 @@ jobs:
include:
- stage: check
script:
- npx aegir commitlint --travis
- npx aegir build --bundlesize
- npx aegir dep-check
- npm run lint

View File

@@ -1,3 +1,35 @@
<a name="0.17.2"></a>
## [0.17.2](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.1...v0.17.2) (2020-01-17)
### Features
* add typescript types + linting/tests ([#161](https://github.com/libp2p/js-libp2p-crypto/issues/161)) ([e01977c](https://github.com/libp2p/js-libp2p-crypto/commit/e01977c))
<a name="0.17.1"></a>
## [0.17.1](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.0...v0.17.1) (2019-10-25)
### Bug Fixes
* better error for missing web crypto ([a5e0560](https://github.com/libp2p/js-libp2p-crypto/commit/a5e0560))
* browser rsa enc/dec ([b8e2414](https://github.com/libp2p/js-libp2p-crypto/commit/b8e2414))
* jwk var naming ([8b8d0c1](https://github.com/libp2p/js-libp2p-crypto/commit/8b8d0c1))
* lint ([2c294b5](https://github.com/libp2p/js-libp2p-crypto/commit/2c294b5))
* padding error ([2c1bac5](https://github.com/libp2p/js-libp2p-crypto/commit/2c1bac5))
* use direct buffers instead of converting to hex ([027a5a9](https://github.com/libp2p/js-libp2p-crypto/commit/027a5a9))
### Features
* add (rsa)pubKey.encrypt and (rsa)privKey.decrypt ([34c5f5c](https://github.com/libp2p/js-libp2p-crypto/commit/34c5f5c))
* browser enc/dec ([9f747a1](https://github.com/libp2p/js-libp2p-crypto/commit/9f747a1))
* use forge to convert jwk2forge ([b998f63](https://github.com/libp2p/js-libp2p-crypto/commit/b998f63))
<a name="0.17.0"></a>
# [0.17.0](https://github.com/libp2p/js-libp2p-crypto/compare/v0.16.1...v0.17.0) (2019-07-11)

View File

@@ -1,8 +1,9 @@
{
"name": "libp2p-crypto",
"version": "0.17.0",
"version": "0.17.2",
"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",
@@ -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": "bundlesize -f dist/index.min.js -s 139kB"
"size": "aegir build --bundlesize",
"test:types": "npx tsc"
},
"keywords": [
"IPFS",
@@ -36,7 +38,7 @@
"license": "MIT",
"dependencies": {
"asmcrypto.js": "^2.3.2",
"asn1.js": "^5.0.1",
"asn1.js": "^5.2.0",
"bn.js": "^5.0.0",
"browserify-aes": "^1.2.0",
"bs58": "^4.0.1",
@@ -44,22 +46,27 @@
"iso-random-stream": "^1.1.0",
"keypair": "^1.0.1",
"libp2p-crypto-secp256k1": "~0.4.0",
"multihashing-async": "~0.7.0",
"node-forge": "~0.8.5",
"multihashing-async": "~0.8.0",
"node-forge": "~0.9.1",
"pem-jwk": "^2.0.0",
"protons": "^1.0.1",
"rsa-pem-to-jwk": "^1.1.3",
"tweetnacl": "^1.0.1",
"ursa-optional": "~0.10.0"
"ursa-optional": "~0.10.1"
},
"devDependencies": {
"aegir": "^19.0.5",
"@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",
"bundlesize": "~0.18.0",
"chai": "^4.2.0",
"chai-string": "^1.5.0",
"dirty-chai": "^2.0.1",
"sinon": "^7.3.2"
"sinon": "^7.5.0"
},
"engines": {
"node": ">=10.0.0",
@@ -75,8 +82,10 @@
"homepage": "https://github.com/libp2p/js-libp2p-crypto",
"contributors": [
"Alan Shaw <alan.shaw@protocol.ai>",
"Alan Shaw <alan@tableflip.io>",
"Alberto Elias <hi@albertoelias.me>",
"Arve Knudsen <arve.knudsen@gmail.com>",
"Carson Farmer <carson.farmer@gmail.com>",
"David Dias <daviddias.p@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",

345
src/index.d.ts vendored Normal file
View File

@@ -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;

View File

@@ -25,7 +25,7 @@ const ErrMissingSecp256K1 = {
}
function typeToKey (type) {
let key = supportedKeys[type.toLowerCase()]
const key = supportedKeys[type.toLowerCase()]
if (!key) {
const supported = Object.keys(supportedKeys).join(' / ')
throw errcode(new Error(`invalid or unsupported key type ${type}. Must be ${supported}`), 'ERR_UNSUPPORTED_KEY_TYPE')

42
src/keys/jwk2pem.js Normal file
View File

@@ -0,0 +1,42 @@
'use strict'
const forge = {
util: require('node-forge/lib/util'),
pki: require('node-forge/lib/pki'),
jsbn: require('node-forge/lib/jsbn')
}
function base64urlToBigInteger (str) {
var bytes = forge.util.decode64(
(str + '==='.slice((str.length + 3) % 4))
.replace(/-/g, '+')
.replace(/_/g, '/'))
return new forge.jsbn.BigInteger(forge.util.bytesToHex(bytes), 16)
}
function convert (key, types) {
return types.map(t => base64urlToBigInteger(key[t]))
}
function jwk2priv (key) {
return forge.pki.setRsaPrivateKey(...convert(key, ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']))
}
function jwk2privPem (key) {
return forge.pki.privateKeyToPem(jwk2priv(key))
}
function jwk2pub (key) {
return forge.pki.setRsaPublicKey(...convert(key, ['n', 'e']))
}
function jwk2pubPem (key) {
return forge.pki.publicKeyToPem(jwk2pub(key))
}
module.exports = {
jwk2pub,
jwk2pubPem,
jwk2priv,
jwk2privPem
}

View File

@@ -29,7 +29,7 @@ module.exports = async (cipherType, hash, secret) => {
}
if (!hash) {
throw errcode(new Error(`missing hash type`), 'ERR_MISSING_HASH_TYPE')
throw errcode(new Error('missing hash type'), 'ERR_MISSING_HASH_TYPE')
}
const cipherKeySize = cipher.keySize
@@ -41,7 +41,7 @@ module.exports = async (cipherType, hash, secret) => {
const m = await hmac.create(hash, secret)
let a = await m.digest(seed)
let result = []
const result = []
let j = 0
while (j < resultLength) {

View File

@@ -120,3 +120,32 @@ function derivePublicFromPrivate (jwKey) {
['verify']
)
}
/*
RSA encryption/decryption for the browser with webcrypto workarround
"bloody dark magic. webcrypto's why."
Explanation:
- Convert JWK to nodeForge
- Convert msg buffer to nodeForge buffer: ByteBuffer is a "binary-string backed buffer", so let's make our buffer a binary string
- Convert resulting nodeForge buffer to buffer: it returns a binary string, turn that into a uint8array(buffer)
*/
const { jwk2pub, jwk2priv } = require('./jwk2pem')
function convertKey (key, pub, msg, handle) {
const fkey = pub ? jwk2pub(key) : jwk2priv(key)
const fmsg = Buffer.from(msg).toString('binary')
const fomsg = handle(fmsg, fkey)
return Buffer.from(fomsg, 'binary')
}
exports.encrypt = function (key, msg) {
return convertKey(key, true, msg, (msg, key) => key.encrypt(msg))
}
exports.decrypt = function (key, msg) {
return convertKey(key, false, msg, (msg, key) => key.decrypt(msg))
}

View File

@@ -32,7 +32,7 @@ class RsaPublicKey {
}
encrypt (bytes) {
return this._key.encrypt(bytes, 'RSAES-PKCS1-V1_5')
return crypto.encrypt(this._key, bytes)
}
equals (key) {
@@ -68,6 +68,10 @@ class RsaPrivateKey {
return new RsaPublicKey(this._publicKey)
}
decrypt (bytes) {
return crypto.decrypt(this._key, bytes)
}
marshal () {
return crypto.utils.jwkToPkcs1(this._key)
}
@@ -106,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

View File

@@ -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') {
@@ -68,3 +71,13 @@ exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line
const pem = jwkToPem(key)
return verify.verify(pem, sig)
}
const padding = crypto.constants.RSA_PKCS1_PADDING
exports.encrypt = function (key, bytes) {
return crypto.publicEncrypt({ key: jwkToPem(key), padding }, bytes)
}
exports.decrypt = function (key, bytes) {
return crypto.privateDecrypt({ key: jwkToPem(key), padding }, bytes)
}

View File

@@ -6,7 +6,7 @@ const BN = require('asn1.js').bignum
// Adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#appendix-C
exports.toBase64 = function toBase64 (bn, len) {
// if len is defined then the bytes are leading-0 padded to the length
let s = bn.toArrayLike(Buffer, 'be', len).toString('base64')
const s = bn.toArrayLike(Buffer, 'be', len).toString('base64')
return s
.replace(/(=*)$/, '') // Remove any trailing '='s

View File

@@ -1,4 +1,5 @@
/* eslint max-nested-callbacks: ["error", 8] */
/* eslint-disable valid-jsdoc */
/* eslint-env mocha */
'use strict'
@@ -17,6 +18,8 @@ const bytes = {
32: 'AES-256'
}
/** @typedef {import("libp2p-crypto").aes.Cipher} Cipher */
describe('AES-CTR', () => {
Object.keys(bytes).forEach((byte) => {
it(`${bytes[byte]} - encrypt and decrypt`, async () => {
@@ -93,6 +96,10 @@ describe('AES-CTR', () => {
})
})
// @ts-check
/**
* @type {function(Cipher): void}
*/
async function encryptAndDecrypt (cipher) {
const data = Buffer.alloc(100)
data.fill(Math.ceil(Math.random() * 100))

View File

@@ -10,8 +10,14 @@ const crypto = require('../src')
const fixtures = require('./fixtures/go-key-rsa')
const { expectErrCode } = require('./util')
/** @typedef {import("libp2p-crypto").PrivateKey} PrivateKey */
describe('libp2p-crypto', function () {
this.timeout(20 * 1000)
// @ts-check
/**
* @type {PrivateKey}
*/
let key
before(async () => {
key = await crypto.keys.generateKeyPair('RSA', 512)
@@ -109,9 +115,9 @@ describe('libp2p-crypto', function () {
})
describe('randomBytes', () => {
it('throws with no number passed', () => {
it('throws with invalid number passed', () => {
expect(() => {
crypto.randomBytes()
crypto.randomBytes(-1)
}).to.throw()
})

View File

@@ -14,7 +14,7 @@ function doTests (fncName, fnc, num, skipBuffersAndStrings) {
// skip this garbage because it's a buffer or a string and we were told do do that
return
}
let args = []
const args = []
for (let i = 0; i < num; i++) {
args.push(garbage)
}

View File

@@ -12,8 +12,14 @@ const fixtures = require('../fixtures/go-key-ed25519')
const testGarbage = require('../helpers/test-garbage-error-handling')
/** @typedef {import("libp2p-crypto").PrivateKey} PrivateKey */
describe('ed25519', function () {
this.timeout(20 * 1000)
// @ts-check
/**
* @type {PrivateKey}
*/
let key
before(async () => {
key = await crypto.keys.generateKeyPair('Ed25519', 512)
@@ -118,11 +124,15 @@ describe('ed25519', function () {
describe('throws error instead of crashing', () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
testGarbage.doTests('key.verify', key.verify.bind(key), 2)
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys))
testGarbage.doTests('key.verify', key.verify.bind(key), 2, null)
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys), null, null)
})
describe('go interop', () => {
// @ts-check
/**
* @type {PrivateKey}
*/
let privateKey
before(async () => {

View File

@@ -11,6 +11,10 @@ const fixtures = require('../fixtures/go-elliptic-key')
const crypto = require('../../src')
const curves = ['P-256', 'P-384'] // 'P-521' fails in tests :( no clue why
// @ts-check
/**
* @type {Record<string, number>}
*/
const lengths = {
'P-256': 65,
'P-384': 97,
@@ -61,7 +65,7 @@ describe('generateEphemeralKeyPair', () => {
})
})
it(`handles bad curve name`, async () => {
it('handles bad curve name', async () => {
try {
await crypto.keys.generateEphemeralKeyPair('bad name')
} catch (err) {

View File

@@ -15,6 +15,10 @@ describe('keyStretcher', () => {
const ciphers = ['AES-128', 'AES-256', 'Blowfish']
const hashes = ['SHA1', 'SHA256', 'SHA512']
let res
// @ts-check
/**
* @type {Buffer}
*/
let secret
before(async () => {

View File

@@ -15,12 +15,18 @@ const fixtures = require('../fixtures/go-key-rsa')
const testGarbage = require('../helpers/test-garbage-error-handling')
/** @typedef {import('libp2p-crypto').keys.supportedKeys.rsa.RsaPrivateKey} RsaPrivateKey */
describe('RSA', function () {
this.timeout(20 * 1000)
// @ts-check
/**
* @type {RsaPrivateKey}
*/
let key
before(async () => {
key = await crypto.keys.generateKeyPair('RSA', 512)
key = await rsa.generateKeyPair(512)
})
it('generates a valid key', async () => {
@@ -80,6 +86,30 @@ describe('RSA', function () {
expect(valid).to.be.eql(true)
})
it('encrypt and decrypt', async () => {
const data = Buffer.from('hello world')
const enc = await key.public.encrypt(data)
const dec = await key.decrypt(enc)
expect(dec).to.be.eql(data)
})
it('encrypt decrypt browser/node interop', async () => {
// @ts-check
/**
* @type {any}
*/
const id = await crypto.keys.unmarshalPrivateKey(Buffer.from('CAASqAkwggSkAgEAAoIBAQCk0O+6oNRxhcdZe2GxEDrFBkDV4TZFZnp2ly/dL1cGMBql/8oXPZgei6h7+P5zzfDq2YCfwbjbf0IVY1AshRl6B5VGE1WS+9p1y1OZxJf5os6V1ENnTi6FTcyuBl4BN8dmIKOif0hqgqflaT5OhfYZDXfbJyVQj4vb2+Stu2Xpph3nwqAnTw/7GC/7jrt2Cq6Tu1PoZi36wSwEPYW3eQ1HAYxZjTYYDXl2iyHygnTcbkGRwAQ7vjk+mW7u60zyoolCm9f6Y7c/orJ33DDUocbaGJLlHcfd8bioBwaZy/2m7q43X8pQs0Q1/iwUt0HHZj1YARmHKbh0zR31ciFiV37dAgMBAAECggEADtJBNKnA4QKURj47r0YT2uLwkqtBi6UnDyISalQXAdXyl4n0nPlrhBewC5H9I+HZr+zmTbeIjaiYgz7el1pSy7AB4v7bG7AtWZlyx6mvtwHGjR+8/f3AXjl8Vgv5iSeAdXUq8fJ7SyS7v3wi38HZOzCEXj9bci6ud5ODMYJgLE4gZD0+i1+/V9cpuYfGpS/gLTLEMQLiw/9o8NSZ7sAnxg0UlYhotqaQY23hvXPBOe+0oa95zl2n6XTxCafa3dQl/B6CD1tUq9dhbQew4bxqMq/mhRO9pREEqZ083Uh+u4PTc1BeHgIQaS864pHPb+AY1F7KDvPtHhdojnghp8d70QKBgQDeRYFxo6sd04ohY86Z/i9icVYIyCvfXAKnaMKeGUjK7ou6sDJwFX8W97+CzXpZ/vffsk/l5GGhC50KqrITxHAy/h5IjyDODfps7NMIp0Dm9sO4PWibbw3OOVBRc8w3b3i7I8MrUUA1nLHE1T1HA1rKOTz5jYhE0fi9XKiT1ciKOQKBgQC903w+n9y7M7eaMW7Z5/13kZ7PS3HlM681eaPrk8J4J+c6miFF40/8HOsmarS38v0fgTeKkriPz5A7aLzRHhSiOnp350JNM6c3sLwPEs2qx/CRuWWx1rMERatfDdUH6mvlK6QHu0QgSfQR27EO6a6XvVSJXbvFmimjmtIaz/IpxQKBgQDWJ9HYVAGC81abZTaiWK3/A4QJYhQjWNuVwPICsgnYvI4Uib+PDqcs0ffLZ38DRw48kek5bxpBuJbOuDhro1EXUJCNCJpq7jzixituovd9kTRyR3iKii2bDM2+LPwOTXDdnk9lZRugjCEbrPkleq33Ob7uEtfAty4aBTTHe6uEwQKBgQCB+2q8RyMSXNuADhFlzOFXGrOwJm0bEUUMTPrduRQUyt4e1qOqA3klnXe3mqGcxBpnlEe/76/JacvNom6Ikxx16a0qpYRU8OWz0KU1fR6vrrEgV98241k5t6sdL4+MGA1Bo5xyXtzLb1hdUh3vpDwVU2OrnC+To3iXus/b5EBiMQKBgEI1OaBcFiyjgLGEyFKoZbtzH1mdatTExfrAQqCjOVjQByoMpGhHTXwEaosvyYu63Pa8AJPT7juSGaiKYEJFcXO9BiNyVfmQiqSHJcYeuh+fmO9IlHRHgy5xaIIC00AHS2vC/gXwmXAdPis6BZqDJeiCuOLWJ94QXn8JBT8IgGAI', 'base64'))
const msg = Buffer.from('hello')
// browser
const dec1 = id.decrypt(Buffer.from('YRFUDx8UjbWSfDS84cDA4WowaaOmd1qFNAv5QutodCKYb9uPtU/tDiAvJzOGu5DCJRo2J0l/35P2weiB4/C2Cb1aZgXKMx/QQC+2jSJiymhqcZaYerjTvkCFwkjCaqthoVo/YXxsaFZ1q7bdTZUDH1TaJR7hWfSyzyPcA8c0w43MIsw16pY8ZaPSclvnCwhoTg1JGjMk6te3we7+wR8QU7VrPhs54mZWxrpu3NQ8xZ6xQqIedsEiNhBUccrCSzYghgsP0Ae/8iKyGyl3U6IegsJNn8jcocvzOJrmU03rgIFPjvuBdaqB38xDSTjbA123KadB28jNoSZh18q/yH3ZIg==', 'base64'))
expect(dec1).to.be.eql(msg)
// node
const dec2 = id.decrypt(Buffer.from('e6yxssqXsWc27ozDy0PGKtMkCS28KwFyES2Ijz89yiz+w6bSFkNOhHPKplpPzgQEuNoUGdbseKlJFyRYHjIT8FQFBHZM8UgSkgoimbY5on4xSxXs7E5/+twjqKdB7oNveTaTf7JCwaeUYnKSjbiYFEawtMiQE91F8sTT7TmSzOZ48tUhnddAAZ3Ac/O3Z9MSAKOCDipi+JdZtXRT8KimGt36/7hjjosYmPuHR1Xy/yMTL6SMbXtBM3yAuEgbQgP+q/7kHMHji3/JvTpYdIUU+LVtkMusXNasRA+UWG2zAht18vqjFMsm9JTiihZw9jRHD4vxAhf75M992tnC+0ZuQg==', 'base64'))
expect(dec2).to.be.eql(msg)
})
it('fails to verify for different data', async () => {
const data = Buffer.from('hello world')
const sig = await key.sign(data)
@@ -122,7 +152,12 @@ describe('RSA', function () {
describe('throws error instead of crashing', () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
testGarbage.doTests('key.verify', key.verify.bind(key), 2, true)
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys))
testGarbage.doTests(
'crypto.keys.unmarshalPrivateKey',
crypto.keys.unmarshalPrivateKey.bind(crypto.keys),
null,
null
)
})
describe('go interop', () => {

View File

@@ -12,7 +12,8 @@ const crypto = require('../../src')
describe('without libp2p-crypto-secp256k1 module present', () => {
before(() => {
sinon.replace(crypto.keys.supportedKeys, 'secp256k1', null)
const empty = null
sinon.replace(crypto.keys.supportedKeys, 'secp256k1', empty)
})
after(() => {

View File

@@ -24,7 +24,7 @@ describe('Util', () => {
})
it('toBase64 zero padding', (done) => {
let bnpad = new BN('ff', 16)
const bnpad = new BN('ff', 16)
expect(util.toBase64(bnpad, 2)).to.eql('AP8')
done()
})

View File

@@ -1,8 +1,13 @@
/* eslint-disable valid-jsdoc */
'use strict'
const chai = require('chai')
const expect = chai.expect
// @ts-check
/**
* @type {function(any, string): void}
*/
const expectErrCode = async (p, code) => {
try {
await p

30
tsconfig.json Normal file
View File

@@ -0,0 +1,30 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"target": "ES5",
"noImplicitAny": false,
"noImplicitThis": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"allowJs": true,
"checkJs": true,
"baseUrl": ".",
"paths": {
"libp2p-crypto": [
"./src",
"../../src",
"../src"
]
},
"types": ["node", "mocha", "chai"],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": ["./src/index.d.ts",],
"include": ["./test/**/*.spec.js"]
}