Skip to content

Commit

Permalink
fix: remove deprecated multibase and multihash (#674)
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <[email protected]>
  • Loading branch information
TimoGlastra authored Mar 15, 2022
1 parent 1e9715b commit 3411f1d
Show file tree
Hide file tree
Showing 15 changed files with 179 additions and 104 deletions.
5 changes: 2 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,13 @@
"lru_map": "^0.4.1",
"luxon": "^1.27.0",
"make-error": "^1.3.6",
"multibase": "^4.0.4",
"multiformats": "^9.4.14",
"multihashes": "^4.0.2",
"object-inspect": "^1.10.3",
"query-string": "^7.0.1",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.1.0",
"tsyringe": "^4.5.0",
"uuid": "^8.3.2",
"varint": "^6.0.0",
"web-did-resolver": "^2.0.8"
},
"devDependencies": {
Expand All @@ -56,6 +54,7 @@
"@types/luxon": "^1.27.0",
"@types/object-inspect": "^1.8.0",
"@types/uuid": "^8.3.0",
"@types/varint": "^6.0.0",
"rimraf": "~3.0.2",
"tslog": "^3.2.0",
"typescript": "~4.3.0"
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/modules/credentials/CredentialUtils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { LinkedAttachment } from '../../utils/LinkedAttachment'
import type { CredValues, Schema } from 'indy-sdk'

import { hash as sha256 } from '@stablelib/sha256'
import BigNumber from 'bn.js'

import { AriesFrameworkError } from '../../error/AriesFrameworkError'
import { Hasher } from '../../utils'
import { encodeAttachment } from '../../utils/attachment'
import { Buffer } from '../../utils/buffer'
import { isBoolean, isNumber, isString } from '../../utils/type'
Expand Down Expand Up @@ -165,7 +165,7 @@ export class CredentialUtils {
value = 'None'
}

return new BigNumber(sha256(Buffer.from(value as string))).toString()
return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString()
}

private static isInt32(number: number) {
Expand Down
10 changes: 4 additions & 6 deletions packages/core/src/modules/dids/domain/Key.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { KeyType } from '../../../crypto'

import { varint } from 'multiformats'

import { Buffer, BufferEncoder, MultiBaseEncoder } from '../../../utils'
import { Buffer, BufferEncoder, MultiBaseEncoder, VarintEncoder } from '../../../utils'

import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './key-type/multiCodecKey'

Expand All @@ -27,7 +25,7 @@ export class Key {

public static fromFingerprint(fingerprint: string) {
const { data } = MultiBaseEncoder.decode(fingerprint)
const [code, byteLength] = varint.decode(data)
const [code, byteLength] = VarintEncoder.decode(data)

const publicKey = Buffer.from(data.slice(byteLength))
const keyType = getKeyTypeByMultiCodecPrefix(code)
Expand All @@ -38,8 +36,8 @@ export class Key {
public get prefixedPublicKey() {
const multiCodecPrefix = getMultiCodecPrefixByKeytype(this.keyType)

// Create Uint8Array with length of the prefix bytes, then use varint to fill the prefix bytes
const prefixBytes = varint.encodeTo(multiCodecPrefix, new Uint8Array(varint.encodingLength(multiCodecPrefix)))
// Create Buffer with length of the prefix bytes, then use varint to fill the prefix bytes
const prefixBytes = VarintEncoder.encode(multiCodecPrefix)

// Combine prefix with public key
return Buffer.concat([prefixBytes, this.publicKey])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('DidResolver', () => {
didDocumentMetadata: {},
didResolutionMetadata: {
error: 'notFound',
message: `resolver_error: Unable to resolve did 'did:key:asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th': Error: Invalid multibase: asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th`,
message: `resolver_error: Unable to resolve did 'did:key:asdfkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th': Error: No decoder found for multibase prefix 'a'`,
},
})
})
Expand Down
11 changes: 2 additions & 9 deletions packages/core/src/modules/dids/methods/peer/DidPeer.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type { DidDocument } from '../../domain'
import type { ParsedDid } from '../../types'

import { hash as sha256 } from '@stablelib/sha256'
import { instanceToInstance } from 'class-transformer'

import { BufferEncoder, JsonEncoder, MultiBaseEncoder, MultiHashEncoder, Buffer } from '../../../../utils'
import { JsonEncoder, MultiBaseEncoder, MultiHashEncoder } from '../../../../utils'
import { Key } from '../../domain/Key'
import { getKeyDidMappingByKeyType } from '../../domain/key-type'
import { parseDid } from '../../domain/parse'
Expand Down Expand Up @@ -73,13 +72,7 @@ export class DidPeer {
// Remove id from did document as the id should be generated without an id.
const didDocumentBuffer = JsonEncoder.toBuffer({ ...didDocument.toJSON(), id: undefined })

// TODO: we should improve the buffer/multibase/multihash API.
const didIdentifier = BufferEncoder.toUtf8String(
MultiBaseEncoder.encode(
Buffer.from(MultiHashEncoder.encode(sha256(didDocumentBuffer), 'sha2-256')),
'base58btc'
)
)
const didIdentifier = MultiBaseEncoder.encode(MultiHashEncoder.encode(didDocumentBuffer, 'sha2-256'), 'base58btc')

const did = `did:peer:1${didIdentifier}`

Expand Down
23 changes: 23 additions & 0 deletions packages/core/src/utils/Hasher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { hash as sha256 } from '@stablelib/sha256'

export type HashName = 'sha2-256'

type HashingMap = {
[key in HashName]: (data: Uint8Array) => Uint8Array
}

const hashingMap: HashingMap = {
'sha2-256': (data) => sha256(data),
}

export class Hasher {
public static hash(data: Uint8Array, hashName: HashName): Uint8Array {
const hashFn = hashingMap[hashName]

if (!hashFn) {
throw new Error(`Unsupported hash name '${hashName}'`)
}

return hashFn(data)
}
}
17 changes: 7 additions & 10 deletions packages/core/src/utils/HashlinkEncoder.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import type { HashName } from './Hasher'
import type { BaseName } from './MultiBaseEncoder'
import type { Buffer } from './buffer'

import { hash as sha256 } from '@stablelib/sha256'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore ts is giving me headaches because this package has no types
import cbor from 'borc'

import { BufferEncoder } from './BufferEncoder'
import { MultiBaseEncoder } from './MultiBaseEncoder'
import { MultiHashEncoder } from './MultiHashEncoder'

Expand Down Expand Up @@ -38,7 +37,7 @@ export class HashlinkEncoder {
*/
public static encode(
buffer: Buffer | Uint8Array,
hashAlgorithm: 'sha2-256',
hashAlgorithm: HashName,
baseEncoding: BaseName = 'base58btc',
metadata?: Metadata
) {
Expand Down Expand Up @@ -84,15 +83,13 @@ export class HashlinkEncoder {
}

private static encodeMultiHash(
buffer: Buffer | Uint8Array,
hashName: 'sha2-256',
data: Buffer | Uint8Array,
hashName: HashName,
baseEncoding: BaseName = 'base58btc'
): string {
// TODO: Support more hashing algorithms
const hash = sha256(buffer)
const mh = MultiHashEncoder.encode(hash, hashName)
const mh = MultiHashEncoder.encode(data, hashName)
const mb = MultiBaseEncoder.encode(mh, baseEncoding)
return BufferEncoder.toUtf8String(mb)
return mb
}

private static encodeMetadata(metadata: Metadata, baseEncoding: BaseName): string {
Expand All @@ -110,7 +107,7 @@ export class HashlinkEncoder {

const multibaseMetadata = MultiBaseEncoder.encode(cborData, baseEncoding)

return BufferEncoder.toUtf8String(multibaseMetadata)
return multibaseMetadata
}

private static decodeMetadata(mb: string): Metadata {
Expand Down
69 changes: 45 additions & 24 deletions packages/core/src/utils/MultiBaseEncoder.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,66 @@
import multibase from 'multibase'
import { decodeFromBase58, encodeToBase58 } from './base58'

export type BaseName = multibase.BaseName
export type BaseName = 'base58btc'

type EncodingMap = {
[key in BaseName]: (data: Uint8Array) => string
}

type DecodingMap = {
[key: string]: (data: string) => { data: Uint8Array; baseName: BaseName }
}

const multibaseEncodingMap: EncodingMap = {
base58btc: (data) => `z${encodeToBase58(data)}`,
}

const multibaseDecodingMap: DecodingMap = {
z: (data) => ({ data: decodeFromBase58(data.substring(1)), baseName: 'base58btc' }),
}

export class MultiBaseEncoder {
/**
*
* Encodes a buffer into a multibase
*
* @param {Uint8Array} buffer the buffer that has to be encoded
* @param {multibase.BaseName} baseName the encoding algorithm
* @param buffer the buffer that has to be encoded
* @param baseName the encoding algorithm
*/
public static encode(buffer: Uint8Array, baseName: multibase.BaseName = 'base58btc') {
return multibase.encode(baseName, buffer)
public static encode(buffer: Uint8Array, baseName: BaseName) {
const encode = multibaseEncodingMap[baseName]

if (!encode) {
throw new Error(`Unsupported encoding '${baseName}'`)
}

return encode(buffer)
}

/**
*
* Decodes a multibase into a Uint8Array
*
* @param {string} data the multibase that has to be decoded
* @param data the multibase that has to be decoded
*
* @returns {Uint8array} data the decoded multibase
* @returns {string} encodingAlgorithm name of the encoding algorithm
* @returns decoded data and the multi base name
*/
public static decode(data: string | Uint8Array): { data: Uint8Array; baseName: string } {
if (this.isValid(data)) {
const baseName = multibase.encodingFromData(data).name
return { data: multibase.decode(data), baseName }
public static decode(data: string): { data: Uint8Array; baseName: string } {
const prefix = data[0]
const decode = multibaseDecodingMap[prefix]

if (!decode) {
throw new Error(`No decoder found for multibase prefix '${prefix}'`)
}
throw new Error(`Invalid multibase: ${data}`)

return decode(data)
}

/**
*
* Validates if it is a valid multibase encoded value
*
* @param {Uint8Array} data the multibase that needs to be validated
*
* @returns {boolean} bool whether the multibase value is encoded
*/
public static isValid(data: string | Uint8Array): boolean {
return multibase.isEncoded(data) ? true : false
public static isValid(data: string): boolean {
try {
MultiBaseEncoder.decode(data)
return true
} catch (error) {
return false
}
}
}
56 changes: 47 additions & 9 deletions packages/core/src/utils/MultiHashEncoder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
import * as multihash from 'multihashes'
import type { HashName } from './Hasher'

import { Hasher } from './Hasher'
import { VarintEncoder } from './VarintEncoder'
import { Buffer } from './buffer'

type MultiHashNameMap = {
[key in HashName]: number
}

type MultiHashCodeMap = {
[key: number]: HashName
}

const multiHashNameMap: MultiHashNameMap = {
'sha2-256': 0x12,
}

const multiHashCodeMap: MultiHashCodeMap = Object.entries(multiHashNameMap).reduce(
(map, [hashName, hashCode]) => ({ ...map, [hashCode]: hashName }),
{}
)

export class MultiHashEncoder {
/**
Expand All @@ -10,8 +31,14 @@ export class MultiHashEncoder {
*
* @returns a multihash
*/
public static encode(buffer: Uint8Array, hashName: 'sha2-256'): Uint8Array {
return multihash.encode(buffer, hashName)
public static encode(data: Uint8Array, hashName: 'sha2-256'): Buffer {
const hash = Hasher.hash(data, hashName)
const hashCode = multiHashNameMap[hashName]

const hashPrefix = VarintEncoder.encode(hashCode)
const hashLengthPrefix = VarintEncoder.encode(hash.length)

return Buffer.concat([hashPrefix, hashLengthPrefix, hash])
}

/**
Expand All @@ -22,12 +49,23 @@ export class MultiHashEncoder {
*
* @returns object with the data and the hashing algorithm
*/
public static decode(data: Uint8Array): { data: Uint8Array; hashName: string } {
if (this.isValid(data)) {
const decodedHash = multihash.decode(data)
return { data: decodedHash.digest, hashName: decodedHash.name }
public static decode(data: Uint8Array): { data: Buffer; hashName: string } {
const [hashPrefix, hashPrefixByteLength] = VarintEncoder.decode(data)
const withoutHashPrefix = data.slice(hashPrefixByteLength)

const [, lengthPrefixByteLength] = VarintEncoder.decode(withoutHashPrefix)
const withoutLengthPrefix = withoutHashPrefix.slice(lengthPrefixByteLength)

const hashName = multiHashCodeMap[hashPrefix]

if (!hashName) {
throw new Error(`Unsupported hash code 0x${hashPrefix.toString(16)}`)
}

return {
data: Buffer.from(withoutLengthPrefix),
hashName: multiHashCodeMap[hashPrefix],
}
throw new Error(`Invalid multihash: ${data}`)
}

/**
Expand All @@ -40,7 +78,7 @@ export class MultiHashEncoder {
*/
public static isValid(data: Uint8Array): boolean {
try {
multihash.validate(data)
MultiHashEncoder.decode(data)
return true
} catch (e) {
return false
Expand Down
20 changes: 20 additions & 0 deletions packages/core/src/utils/VarintEncoder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { decode, encode, encodingLength } from 'varint'

import { Buffer } from './buffer'

export class VarintEncoder {
public static decode(data: Uint8Array | number[] | Buffer) {
const code = decode(data)
return [code, decode.bytes] as const
}

public static encode(int: number) {
const target = new Buffer(VarintEncoder.encodingLength(int))
encode(int, target)
return target
}

public static encodingLength(int: number) {
return encodingLength(int)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const invalidMultiBase = 'gKWfinQuRQ3ekD1danFHqvKRg9koFp8vpokUeREEgjSyHwweeKDFax
describe('MultiBaseEncoder', () => {
describe('encode()', () => {
it('Encodes valid multibase', () => {
const multibase = BufferEncoder.toUtf8String(MultiBaseEncoder.encode(validData, 'base58btc'))
const multibase = MultiBaseEncoder.encode(validData, 'base58btc')
expect(multibase).toEqual('z2NEpo7TZRRrLZSi2U')
})
})
Expand All @@ -24,7 +24,7 @@ describe('MultiBaseEncoder', () => {
it('Decodes invalid multibase', () => {
expect(() => {
MultiBaseEncoder.decode(invalidMultiBase)
}).toThrow(/^Invalid multibase: /)
}).toThrow(/^No decoder found for multibase prefix/)
})
})

Expand Down
Loading

0 comments on commit 3411f1d

Please sign in to comment.