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

fix: remove deprecated multibase and multihash #674

Merged
merged 3 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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: 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
25 changes: 25 additions & 0 deletions packages/core/src/utils/VarintEncoder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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 encodeTo(int: number, target: Buffer | Uint8Array, offset = 0) {
encode(int, target, offset)
return target
TimoGlastra marked this conversation as resolved.
Show resolved Hide resolved
}

public static encodingLength(int: number) {
return encodingLength(int)
}
}
Loading