Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: use .name property instead of .code for errors #2655

Merged
merged 8 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/connection-encrypter-plaintext/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"build": "aegir build",
"test": "aegir test",
"clean": "aegir clean",
"generate": "protons ./src/pb/index.proto",
"generate": "protons ./src/pb/proto.proto",
"lint": "aegir lint",
"test:chrome": "aegir test -t browser --cov",
"test:chrome-webworker": "aegir test -t webworker",
Expand All @@ -57,7 +57,8 @@
"it-protobuf-stream": "^1.1.3",
"it-stream-types": "^2.0.1",
"protons-runtime": "^5.4.0",
"uint8arraylist": "^2.4.8"
"uint8arraylist": "^2.4.8",
"uint8arrays": "^5.1.0"
},
"devDependencies": {
"@libp2p/interface-compliance-tests": "^5.4.11",
Expand Down
40 changes: 24 additions & 16 deletions packages/connection-encrypter-plaintext/src/pb/proto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
/* eslint-disable @typescript-eslint/no-empty-interface */

import { encodeMessage, decodeMessage, message, enumeration } from 'protons-runtime'
import type { Codec } from 'protons-runtime'
import { type Codec, decodeMessage, type DecodeOptions, encodeMessage, enumeration, message } from 'protons-runtime'
import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
import type { Uint8ArrayList } from 'uint8arraylist'

export interface Exchange {
Expand Down Expand Up @@ -36,7 +36,7 @@
if (opts.lengthDelimited !== false) {
w.ldelim()
}
}, (reader, length) => {
}, (reader, length, opts = {}) => {
const obj: any = {}

const end = length == null ? reader.len : reader.pos + length
Expand All @@ -45,15 +45,20 @@
const tag = reader.uint32()

switch (tag >>> 3) {
case 1:
case 1: {
obj.id = reader.bytes()
break
case 2:
obj.pubkey = PublicKey.codec().decode(reader, reader.uint32())
}
case 2: {
obj.pubkey = PublicKey.codec().decode(reader, reader.uint32(), {
limits: opts.limits?.pubkey
})
break
default:
}
default: {
reader.skipType(tag & 7)
break
}

Check warning on line 61 in packages/connection-encrypter-plaintext/src/pb/proto.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-plaintext/src/pb/proto.ts#L61

Added line #L61 was not covered by tests
}
}

Expand All @@ -68,8 +73,8 @@
return encodeMessage(obj, Exchange.codec())
}

export const decode = (buf: Uint8Array | Uint8ArrayList): Exchange => {
return decodeMessage(buf, Exchange.codec())
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Exchange>): Exchange => {
return decodeMessage(buf, Exchange.codec(), opts)
}
}

Expand Down Expand Up @@ -120,10 +125,10 @@
if (opts.lengthDelimited !== false) {
w.ldelim()
}
}, (reader, length) => {
}, (reader, length, opts = {}) => {
const obj: any = {
Type: KeyType.RSA,
Data: new Uint8Array(0)
Data: uint8ArrayAlloc(0)
}

const end = length == null ? reader.len : reader.pos + length
Expand All @@ -132,15 +137,18 @@
const tag = reader.uint32()

switch (tag >>> 3) {
case 1:
case 1: {
obj.Type = KeyType.codec().decode(reader)
break
case 2:
}
case 2: {
obj.Data = reader.bytes()
break
default:
}
default: {
reader.skipType(tag & 7)
break
}

Check warning on line 151 in packages/connection-encrypter-plaintext/src/pb/proto.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-plaintext/src/pb/proto.ts#L151

Added line #L151 was not covered by tests
}
}

Expand All @@ -155,7 +163,7 @@
return encodeMessage(obj, PublicKey.codec())
}

export const decode = (buf: Uint8Array | Uint8ArrayList): PublicKey => {
return decodeMessage(buf, PublicKey.codec())
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<PublicKey>): PublicKey => {
return decodeMessage(buf, PublicKey.codec(), opts)

Check warning on line 167 in packages/connection-encrypter-plaintext/src/pb/proto.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-plaintext/src/pb/proto.ts#L167

Added line #L167 was not covered by tests
}
}
8 changes: 2 additions & 6 deletions packages/connection-encrypter-plaintext/test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
/* eslint-env mocha */

import {
InvalidCryptoExchangeError,
UnexpectedPeerError
} from '@libp2p/interface'
import { mockMultiaddrConnPair } from '@libp2p/interface-compliance-tests/mocks'
import { defaultLogger } from '@libp2p/logger'
import { peerIdFromBytes } from '@libp2p/peer-id'
Expand Down Expand Up @@ -56,7 +52,7 @@ describe('plaintext', () => {
encrypter.secureOutbound(outbound, wrongPeer)
]).then(() => expect.fail('should have failed'), (err) => {
expect(err).to.exist()
expect(err).to.have.property('code', UnexpectedPeerError.code)
expect(err).to.have.property('name', 'UnexpectedPeerError')
})
})

Expand All @@ -81,6 +77,6 @@ describe('plaintext', () => {
encrypter.secureInbound(inbound),
encrypterRemote.secureOutbound(outbound, localPeer)
]))
.to.eventually.be.rejected.with.property('code', InvalidCryptoExchangeError.code)
.to.eventually.be.rejected.with.property('name', 'InvalidCryptoExchangeError')
})
})
19 changes: 19 additions & 0 deletions packages/connection-encrypter-tls/src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* The handshake timed out
*/
export class HandshakeTimeoutError extends Error {
constructor (message = 'Handshake timeout') {
super(message)
this.name = 'HandshakeTimeoutError'
}

Check warning on line 8 in packages/connection-encrypter-tls/src/errors.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-tls/src/errors.ts#L6-L8

Added lines #L6 - L8 were not covered by tests
}

/**
* The certificate was invalid
*/
export class InvalidCertificateError extends Error {
constructor (message = 'Invalid certificate') {
super(message)
this.name = 'InvalidCertificateError'
}

Check warning on line 18 in packages/connection-encrypter-tls/src/errors.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-tls/src/errors.ts#L16-L18

Added lines #L16 - L18 were not covered by tests
}
8 changes: 4 additions & 4 deletions packages/connection-encrypter-tls/src/pb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
/* eslint-disable @typescript-eslint/no-empty-interface */

import { type Codec, decodeMessage, encodeMessage, enumeration, message } from 'protons-runtime'
import { type Codec, decodeMessage, type DecodeOptions, encodeMessage, enumeration, message } from 'protons-runtime'
import type { Uint8ArrayList } from 'uint8arraylist'

export enum KeyType {
Expand Down Expand Up @@ -54,7 +54,7 @@ export namespace PublicKey {
if (opts.lengthDelimited !== false) {
w.ldelim()
}
}, (reader, length) => {
}, (reader, length, opts = {}) => {
const obj: any = {}

const end = length == null ? reader.len : reader.pos + length
Expand Down Expand Up @@ -89,7 +89,7 @@ export namespace PublicKey {
return encodeMessage(obj, PublicKey.codec())
}

export const decode = (buf: Uint8Array | Uint8ArrayList): PublicKey => {
return decodeMessage(buf, PublicKey.codec())
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<PublicKey>): PublicKey => {
return decodeMessage(buf, PublicKey.codec(), opts)
}
}
5 changes: 3 additions & 2 deletions packages/connection-encrypter-tls/src/tls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
*/

import { TLSSocket, type TLSSocketOptions, connect } from 'node:tls'
import { CodeError, serviceCapabilities } from '@libp2p/interface'
import { serviceCapabilities } from '@libp2p/interface'
import { HandshakeTimeoutError } from './errors.js'
import { generateCertificate, verifyPeerCertificate, itToStream, streamToIt } from './utils.js'
import { PROTOCOL } from './index.js'
import type { TLSComponents, TLSInit } from './index.js'
Expand Down Expand Up @@ -84,7 +85,7 @@

return new Promise((resolve, reject) => {
const abortTimeout = setTimeout(() => {
socket.destroy(new CodeError('Handshake timeout', 'ERR_HANDSHAKE_TIMEOUT'))
socket.destroy(new HandshakeTimeoutError())

Check warning on line 88 in packages/connection-encrypter-tls/src/tls.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-tls/src/tls.ts#L88

Added line #L88 was not covered by tests
}, this.timeout)

const verifyRemote = (): void => {
Expand Down
51 changes: 24 additions & 27 deletions packages/connection-encrypter-tls/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Duplex as DuplexStream } from 'node:stream'
import { Ed25519PublicKey, Secp256k1PublicKey, marshalPublicKey, supportedKeys, unmarshalPrivateKey, unmarshalPublicKey } from '@libp2p/crypto/keys'
import { CodeError, InvalidCryptoExchangeError, UnexpectedPeerError } from '@libp2p/interface'
import { InvalidCryptoExchangeError, InvalidParametersError, UnexpectedPeerError } from '@libp2p/interface'
import { peerIdFromKeys } from '@libp2p/peer-id'
import { AsnConvert } from '@peculiar/asn1-schema'
import * as asn1X509 from '@peculiar/asn1-x509'
Expand All @@ -11,7 +11,8 @@
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import { KeyType, PublicKey } from '../src/pb/index.js'
import { InvalidCertificateError } from './errors.js'
import { KeyType, PublicKey } from './pb/index.js'
import type { PeerId, PublicKey as Libp2pPublicKey, Logger } from '@libp2p/interface'
import type { Duplex } from 'it-stream-types'
import type { Uint8ArrayList } from 'uint8arraylist'
Expand All @@ -33,12 +34,12 @@

if (x509Cert.notBefore.getTime() > now) {
log?.error('the certificate was not valid yet')
throw new CodeError('The certificate is not valid yet', 'ERR_INVALID_CERTIFICATE')
throw new InvalidCertificateError('The certificate is not valid yet')

Check warning on line 37 in packages/connection-encrypter-tls/src/utils.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-tls/src/utils.ts#L37

Added line #L37 was not covered by tests
}

if (x509Cert.notAfter.getTime() < now) {
log?.error('the certificate has expired')
throw new CodeError('The certificate has expired', 'ERR_INVALID_CERTIFICATE')
throw new InvalidCertificateError('The certificate has expired')

Check warning on line 42 in packages/connection-encrypter-tls/src/utils.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-tls/src/utils.ts#L42

Added line #L42 was not covered by tests
}

const certSignatureValid = await x509Cert.verify()
Expand All @@ -59,7 +60,7 @@

if (libp2pPublicKeyExtension == null || libp2pPublicKeyExtension.type !== LIBP2P_PUBLIC_KEY_EXTENSION) {
log?.error('the certificate did not include the libp2p public key extension')
throw new CodeError('The certificate did not include the libp2p public key extension', 'ERR_INVALID_CERTIFICATE')
throw new InvalidCertificateError('The certificate did not include the libp2p public key extension')

Check warning on line 63 in packages/connection-encrypter-tls/src/utils.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-tls/src/utils.ts#L63

Added line #L63 was not covered by tests
}

const { result: libp2pKeySequence } = asn1js.fromBER(libp2pPublicKeyExtension.value)
Expand Down Expand Up @@ -104,34 +105,17 @@
}

export async function generateCertificate (peerId: PeerId): Promise<{ cert: string, key: string }> {
const now = Date.now()

const alg = {
name: 'ECDSA',
namedCurve: 'P-256',
hash: 'SHA-256'
}

const keys = await crypto.subtle.generateKey(alg, true, ['sign'])

const certPublicKeySpki = await crypto.subtle.exportKey('spki', keys.publicKey)
const dataToSign = encodeSignatureData(certPublicKeySpki)

if (peerId.privateKey == null) {
throw new InvalidCryptoExchangeError('Private key was missing from PeerId')
throw new InvalidParametersError('Private key was missing from PeerId')
}

const privateKey = await unmarshalPrivateKey(peerId.privateKey)
const sig = await privateKey.sign(dataToSign)

let keyType: KeyType
let keyData: Uint8Array

if (peerId.publicKey == null) {
throw new CodeError('Public key missing from PeerId', 'ERR_INVALID_PEER_ID')
throw new InvalidParametersError('Public key missing from PeerId')

Check warning on line 113 in packages/connection-encrypter-tls/src/utils.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-tls/src/utils.ts#L113

Added line #L113 was not covered by tests
}

const publicKey = unmarshalPublicKey(peerId.publicKey)
let keyType: KeyType
let keyData: Uint8Array

if (peerId.type === 'Ed25519') {
// Ed25519: Only the 32 bytes of the public key
Expand All @@ -146,9 +130,22 @@
keyType = KeyType.RSA
keyData = publicKey.marshal()
} else {
throw new CodeError('Unknown PeerId type', 'ERR_UNKNOWN_PEER_ID_TYPE')
throw new InvalidParametersError('PeerId had unknown or unsupported type')

Check warning on line 133 in packages/connection-encrypter-tls/src/utils.ts

View check run for this annotation

Codecov / codecov/patch

packages/connection-encrypter-tls/src/utils.ts#L133

Added line #L133 was not covered by tests
}

const now = Date.now()

const alg = {
name: 'ECDSA',
namedCurve: 'P-256',
hash: 'SHA-256'
}

const keys = await crypto.subtle.generateKey(alg, true, ['sign'])
const certPublicKeySpki = await crypto.subtle.exportKey('spki', keys.publicKey)
const dataToSign = encodeSignatureData(certPublicKeySpki)
const privateKey = await unmarshalPrivateKey(peerId.privateKey)
const sig = await privateKey.sign(dataToSign)
const notAfter = new Date(now + CERT_VALIDITY_PERIOD_TO)
// workaround for https://github.com/PeculiarVentures/x509/issues/73
notAfter.setMilliseconds(0)
Expand Down
8 changes: 2 additions & 6 deletions packages/connection-encrypter-tls/test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
/* eslint-env mocha */

import {
InvalidCryptoExchangeError,
UnexpectedPeerError
} from '@libp2p/interface'
import { mockMultiaddrConnPair } from '@libp2p/interface-compliance-tests/mocks'
import { defaultLogger } from '@libp2p/logger'
import { peerIdFromBytes } from '@libp2p/peer-id'
Expand Down Expand Up @@ -51,7 +47,7 @@ describe('tls', () => {
encrypter.secureOutbound(outbound, wrongPeer)
]).then(() => expect.fail('should have failed'), (err) => {
expect(err).to.exist()
expect(err).to.have.property('code', UnexpectedPeerError.code)
expect(err).to.have.property('name', 'UnexpectedPeerError')
})
})

Expand All @@ -76,6 +72,6 @@ describe('tls', () => {
encrypter.secureInbound(inbound),
encrypter.secureOutbound(outbound, localPeer)
]))
.to.eventually.be.rejected.with.property('code', InvalidCryptoExchangeError.code)
.to.eventually.be.rejected.with.property('name', 'InvalidParametersError')
})
})
6 changes: 3 additions & 3 deletions packages/connection-encrypter-tls/test/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ describe('utils', () => {

it('should reject certificate with a the wrong peer id in the extension', async () => {
await expect(verifyPeerCertificate(testVectors.wrongPeerIdInExtension.cert, undefined, logger('libp2p'))).to.eventually.be.rejected
.with.property('code', 'ERR_INVALID_CRYPTO_EXCHANGE')
.with.property('name', 'InvalidCryptoExchangeError')
})

it('should reject certificate with invalid self signature', async () => {
await expect(verifyPeerCertificate(testVectors.invalidCertificateSignature.cert, undefined, logger('libp2p'))).to.eventually.be.rejected
.with.property('code', 'ERR_INVALID_CRYPTO_EXCHANGE')
.with.property('name', 'InvalidCryptoExchangeError')
})

it('should reject certificate with a chain', async () => {
Expand Down Expand Up @@ -72,6 +72,6 @@ describe('utils', () => {
})

await expect(verifyPeerCertificate(new Uint8Array(cert.rawData), undefined, logger('libp2p'))).to.eventually.be.rejected
.with.property('code', 'ERR_INVALID_CRYPTO_EXCHANGE')
.with.property('name', 'InvalidCryptoExchangeError')
})
})
29 changes: 29 additions & 0 deletions packages/crypto/src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Signing a message failed
*/
export class SigningError extends Error {
constructor (message = 'An error occurred while signing a message') {
super(message)
this.name = 'SigningError'
}
}

/**
* Verifying a message signature failed
*/
export class VerificationError extends Error {
constructor (message = 'An error occurred while verifying a message') {
super(message)
this.name = 'VerificationError'
}

Check warning on line 18 in packages/crypto/src/errors.ts

View check run for this annotation

Codecov / codecov/patch

packages/crypto/src/errors.ts#L16-L18

Added lines #L16 - L18 were not covered by tests
}

/**
* WebCrypto was not available in the current context
*/
export class WebCryptoMissingError extends Error {
constructor (message = 'Missing Web Crypto API') {
super(message)
this.name = 'WebCryptoMissingError'
}

Check warning on line 28 in packages/crypto/src/errors.ts

View check run for this annotation

Codecov / codecov/patch

packages/crypto/src/errors.ts#L26-L28

Added lines #L26 - L28 were not covered by tests
}
Loading
Loading