Skip to content

Commit

Permalink
fix(build): fix strict compiler errors for most of the code
Browse files Browse the repository at this point in the history
fixes #174
  • Loading branch information
mirceanis committed Jun 1, 2021
1 parent cd4edd2 commit b84fad7
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 37 deletions.
11 changes: 7 additions & 4 deletions src/JWE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export interface Decrypter {
iv: Uint8Array,
aad?: Uint8Array,
recipient?: Record<string, any>
) => Promise<Uint8Array>
) => Promise<Uint8Array | null>
}

function validateJWE(jwe: JWE) {
Expand All @@ -66,7 +66,7 @@ function validateJWE(jwe: JWE) {

function encodeJWE({ ciphertext, tag, iv, protectedHeader, recipient }: EncryptionResult, aad?: Uint8Array): JWE {
const jwe: JWE = {
protected: protectedHeader,
protected: <string>protectedHeader,
iv: bytesToBase64url(iv),
ciphertext: bytesToBase64url(ciphertext),
tag: bytesToBase64url(tag)
Expand Down Expand Up @@ -99,10 +99,13 @@ export async function createJWE(
cek = encryptionResult.cek
jwe = encodeJWE(encryptionResult, aad)
} else {
jwe.recipients.push(await encrypter.encryptCek(cek))
const recipient = await encrypter.encryptCek?.(cek)
if (recipient) {
jwe?.recipients?.push(recipient)
}
}
}
return jwe
return <JWE>jwe
}
}

Expand Down
41 changes: 26 additions & 15 deletions src/JWT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import VerifierAlgorithm from './VerifierAlgorithm'
export type Signer = (data: string | Uint8Array) => Promise<EcdsaSignature | string>
export type SignerAlgorithm = (payload: string, signer: Signer) => Promise<string>

export type ProofPurposeTypes =
| 'assertionMethod'
| 'authentication'
| 'keyAgreement'
| 'capabilityDelegation'
| 'capabilityInvocation'

export interface JWTOptions {
issuer: string
signer: Signer
Expand All @@ -26,7 +33,7 @@ export interface JWTVerifyOptions {
resolver?: Resolvable
skewTime?: number
/** See https://www.w3.org/TR/did-spec-registries/#verification-relationships */
proofPurpose?: 'authentication' | 'assertionMethod' | 'capabilityDelegation' | 'capabilityInvocation' | string
proofPurpose?: ProofPurposeTypes
}

export interface JWSCreationOptions {
Expand Down Expand Up @@ -139,7 +146,7 @@ function encodeSection(data: any, shouldCanonicalize: boolean = false): string {
export const NBF_SKEW: number = 300

function decodeJWS(jws: string): JWSDecoded {
const parts: RegExpMatchArray = jws.match(/^([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/)
const parts = jws.match(/^([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/)
if (parts) {
return {
header: JSON.parse(decodeBase64url(parts[1])),
Expand Down Expand Up @@ -292,30 +299,30 @@ export function verifyJWS(jws: string, pubkeys: VerificationMethod | Verificatio
export async function verifyJWT(
jwt: string,
options: JWTVerifyOptions = {
resolver: null,
auth: null,
audience: null,
callbackUrl: null,
skewTime: null,
proofPurpose: null
resolver: undefined,
auth: undefined,
audience: undefined,
callbackUrl: undefined,
skewTime: undefined,
proofPurpose: undefined
}
): Promise<JWTVerified> {
if (!options.resolver) throw new Error('No DID resolver has been configured')
const { payload, header, signature, data }: JWTDecoded = decodeJWT(jwt)
const proofPurpose: string | undefined = options.hasOwnProperty('auth')
const proofPurpose: ProofPurposeTypes | undefined = options.hasOwnProperty('auth')
? options.auth
? 'authentication'
: undefined
: options.proofPurpose
const { didResolutionResult, authenticators, issuer }: DIDAuthenticator = await resolveAuthenticator(
options.resolver,
header.alg,
payload.iss,
payload.iss || '',
proofPurpose
)
const signer: VerificationMethod = await verifyJWSDecoded({ header, data, signature } as JWSDecoded, authenticators)
const now: number = Math.floor(Date.now() / 1000)
const skewTime = options.skewTime >= 0 ? options.skewTime : NBF_SKEW
const skewTime = typeof options.skewTime !== 'undefined' && options.skewTime >= 0 ? options.skewTime : NBF_SKEW
if (signer) {
const nowSkewed = now + skewTime
if (payload.nbf) {
Expand All @@ -341,6 +348,9 @@ export async function verifyJWT(
}
return { payload, didResolutionResult, issuer, signer, jwt }
}
throw new Error(
`JWT not valid. issuer DID document does not contain a verificationMethod that matches the signature.`
)
}

/**
Expand All @@ -363,7 +373,7 @@ export async function resolveAuthenticator(
resolver: Resolvable,
alg: string,
issuer: string,
proofPurpose?: string
proofPurpose?: ProofPurposeTypes
): Promise<DIDAuthenticator> {
const types: string[] = SUPPORTED_PUBLIC_KEY_TYPES[alg]
if (!types || types.length === 0) {
Expand All @@ -383,7 +393,7 @@ export async function resolveAuthenticator(
didResult = result as DIDResolutionResult
}

if (didResult.didResolutionMetadata?.error) {
if (didResult.didResolutionMetadata?.error || didResult.didDocument == null) {
const { error, message } = didResult.didResolutionMetadata
throw new Error(`Unable to resolve DID document for ${issuer}: ${error}, ${message || ''}`)
}
Expand All @@ -399,7 +409,8 @@ export async function resolveAuthenticator(
]
if (typeof proofPurpose === 'string') {
// support legacy DID Documents that do not list assertionMethod
if (proofPurpose.startsWith('assertion') && !didResult.didDocument.hasOwnProperty('assertionMethod')) {
if (proofPurpose.startsWith('assertion') && !didResult?.didDocument?.hasOwnProperty('assertionMethod')) {
didResult.didDocument = { ...(<DIDDocument>didResult.didDocument) }
didResult.didDocument.assertionMethod = [...publicKeysToCheck.map((pk) => pk.id)]
}

Expand All @@ -414,7 +425,7 @@ export async function resolveAuthenticator(
return <VerificationMethod>verificationMethod
}
})
.filter((key) => key != null)
.filter((key) => key != null) as VerificationMethod[]
}

const authenticators: VerificationMethod[] = publicKeysToCheck.filter(({ type }) =>
Expand Down
30 changes: 16 additions & 14 deletions src/VerifierAlgorithm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ec as EC } from 'elliptic'
import { ec as EC, SignatureInput } from 'elliptic'
import { sha256, toEthereumAddress } from './Digest'
import { verify } from '@stablelib/ed25519'
import type { VerificationMethod } from 'did-resolver'
Expand All @@ -8,15 +8,15 @@ const secp256k1 = new EC('secp256k1')

// converts a JOSE signature to it's components
export function toSignatureObject(signature: string, recoverable = false): EcdsaSignature {
const rawsig: Uint8Array = base64ToBytes(signature)
if (rawsig.length !== (recoverable ? 65 : 64)) {
const rawSig: Uint8Array = base64ToBytes(signature)
if (rawSig.length !== (recoverable ? 65 : 64)) {
throw new Error('wrong signature length')
}
const r: string = bytesToHex(rawsig.slice(0, 32))
const s: string = bytesToHex(rawsig.slice(32, 64))
const r: string = bytesToHex(rawSig.slice(0, 32))
const s: string = bytesToHex(rawSig.slice(32, 64))
const sigObj: EcdsaSignature = { r, s }
if (recoverable) {
sigObj.recoveryParam = rawsig[64]
sigObj.recoveryParam = rawSig[64]
}
return sigObj
}
Expand All @@ -32,7 +32,7 @@ function extractPublicKeyBytes(pk: VerificationMethod): Uint8Array {
return base64ToBytes((<LegacyVerificationMethod>pk).publicKeyBase64)
} else if (pk.publicKeyHex) {
return hexToBytes(pk.publicKeyHex)
} else if (pk.publicKeyJwk && pk.publicKeyJwk.crv === 'secp256k1') {
} else if (pk.publicKeyJwk && pk.publicKeyJwk.crv === 'secp256k1' && pk.publicKeyJwk.x && pk.publicKeyJwk.y) {
return hexToBytes(
secp256k1
.keyFromPublic({
Expand All @@ -59,10 +59,10 @@ export function verifyES256K(
return typeof ethereumAddress !== 'undefined' || typeof blockchainAccountId !== undefined
})

let signer: VerificationMethod = fullPublicKeys.find((pk: VerificationMethod) => {
let signer: VerificationMethod | undefined = fullPublicKeys.find((pk: VerificationMethod) => {
try {
const pubBytes = extractPublicKeyBytes(pk)
return secp256k1.keyFromPublic(pubBytes).verify(hash, sigObj)
return secp256k1.keyFromPublic(pubBytes).verify(hash, <SignatureInput>sigObj)
} catch (err) {
return false
}
Expand Down Expand Up @@ -92,14 +92,14 @@ export function verifyRecoverableES256K(
]
}

const checkSignatureAgainstSigner = (sigObj: EcdsaSignature): VerificationMethod => {
const checkSignatureAgainstSigner = (sigObj: EcdsaSignature): VerificationMethod | undefined => {
const hash: Uint8Array = sha256(data)
const recoveredKey: any = secp256k1.recoverPubKey(hash, sigObj, sigObj.recoveryParam)
const recoveredKey: any = secp256k1.recoverPubKey(hash, <SignatureInput>sigObj, <number>sigObj.recoveryParam)
const recoveredPublicKeyHex: string = recoveredKey.encode('hex')
const recoveredCompressedPublicKeyHex: string = recoveredKey.encode('hex', true)
const recoveredAddress: string = toEthereumAddress(recoveredPublicKeyHex)

const signer: VerificationMethod = authenticators.find((pk: VerificationMethod) => {
const signer: VerificationMethod | undefined = authenticators.find((pk: VerificationMethod) => {
const keyHex = bytesToHex(extractPublicKeyBytes(pk))
return (
keyHex === recoveredPublicKeyHex ||
Expand All @@ -112,7 +112,9 @@ export function verifyRecoverableES256K(
return signer
}

const signer: VerificationMethod[] = signatures.map(checkSignatureAgainstSigner).filter((key) => key != null)
const signer: VerificationMethod[] = signatures
.map(checkSignatureAgainstSigner)
.filter((key) => typeof key !== 'undefined') as VerificationMethod[]

if (signer.length === 0) throw new Error('Signature invalid for JWT')
return signer[0]
Expand All @@ -125,7 +127,7 @@ export function verifyEd25519(
): VerificationMethod {
const clear: Uint8Array = stringToBytes(data)
const sig: Uint8Array = base64ToBytes(signature)
const signer: VerificationMethod = authenticators.find((pk: VerificationMethod) => {
const signer = authenticators.find((pk: VerificationMethod) => {
return verify(extractPublicKeyBytes(pk), clear, sig)
})
if (!signer) throw new Error('Signature invalid for JWT')
Expand Down
8 changes: 4 additions & 4 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as u8a from 'uint8arrays'
export interface EcdsaSignature {
r: string
s: string
recoveryParam?: number
recoveryParam?: number | null
}

export function bytesToBase64url(b: Uint8Array): string {
Expand Down Expand Up @@ -56,15 +56,15 @@ export function toJose({ r, s, recoveryParam }: EcdsaSignature, recoverable?: bo
jose.set(u8a.fromString(r, 'base16'), 0)
jose.set(u8a.fromString(s, 'base16'), 32)
if (recoverable) {
if (recoveryParam === undefined) {
if (typeof recoveryParam === 'undefined') {
throw new Error('Signer did not return a recoveryParam')
}
jose[64] = recoveryParam
jose[64] = <number>recoveryParam
}
return bytesToBase64url(jose)
}

export function fromJose(signature: string): { r: string; s: string; recoveryParam: number } {
export function fromJose(signature: string): { r: string; s: string; recoveryParam?: number } {
const signatureBytes: Uint8Array = base64ToBytes(signature)
if (signatureBytes.length < 64 || signatureBytes.length > 65) {
throw new TypeError(`Wrong size for signature. Expected 64 or 65 bytes, but got ${signatureBytes.length}`)
Expand Down

0 comments on commit b84fad7

Please sign in to comment.