Skip to content

Commit

Permalink
feat: add dynamic suite and signing provider (#949)
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <timo@animo.id>
  • Loading branch information
TimoGlastra committed Aug 26, 2022
1 parent 16d3472 commit ce90744
Show file tree
Hide file tree
Showing 26 changed files with 381 additions and 230 deletions.
151 changes: 0 additions & 151 deletions packages/core/src/crypto/BbsService.ts

This file was deleted.

3 changes: 2 additions & 1 deletion packages/core/src/crypto/__tests__/JwsService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { IndyWallet } from '../../wallet/IndyWallet'
import { JwsService } from '../JwsService'
import { Key } from '../Key'
import { KeyType } from '../KeyType'
import { SigningProviderRegistry } from '../signing-provider'

import * as didJwsz6Mkf from './__fixtures__/didJwsz6Mkf'
import * as didJwsz6Mkv from './__fixtures__/didJwsz6Mkv'
Expand All @@ -19,7 +20,7 @@ describe('JwsService', () => {

beforeAll(async () => {
const config = getAgentConfig('JwsService')
wallet = new IndyWallet(config.agentDependencies, config.logger)
wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([]))
agentContext = getAgentContext({
wallet,
})
Expand Down
111 changes: 111 additions & 0 deletions packages/core/src/crypto/signing-provider/Bls12381g2SigningProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type { SigningProvider, CreateKeyPairOptions, SignOptions, VerifyOptions, KeyPair } from './SigningProvider'

import { bls12381toBbs, verify, sign, generateBls12381G2KeyPair } from '@mattrglobal/bbs-signatures'

import { injectable } from '../../plugins'
import { TypedArrayEncoder } from '../../utils'
import { Buffer } from '../../utils/buffer'
import { KeyType } from '../KeyType'

import { SigningProviderError } from './SigningProviderError'

/**
* This will be extracted to the bbs package.
*/
@injectable()
export class Bls12381g2SigningProvider implements SigningProvider {
public readonly keyType = KeyType.Bls12381g2

/**
* Create a KeyPair with type Bls12381g2
*
* @throws {SigningProviderError} When a key could not be created
*/
public async createKeyPair({ seed }: CreateKeyPairOptions): Promise<KeyPair> {
// Generate bytes from the seed as required by the bbs-signatures libraries
const seedBytes = seed ? TypedArrayEncoder.fromString(seed) : undefined

const blsKeyPair = await generateBls12381G2KeyPair(seedBytes)

return {
keyType: KeyType.Bls12381g2,
publicKeyBase58: TypedArrayEncoder.toBase58(blsKeyPair.publicKey),
privateKeyBase58: TypedArrayEncoder.toBase58(blsKeyPair.secretKey),
}
}

/**
* Sign an arbitrary amount of messages, in byte form, with a keypair
*
* @param messages Buffer[] List of messages in Buffer form
* @param publicKey Buffer Publickey required for the signing process
* @param privateKey Buffer PrivateKey required for the signing process
*
* @returns A Buffer containing the signature of the messages
*
* @throws {SigningProviderError} When there are no supplied messages
*/
public async sign({ data, publicKeyBase58, privateKeyBase58 }: SignOptions): Promise<Buffer> {
if (data.length === 0) throw new SigningProviderError('Unable to create a signature without any messages')
// Check if it is a single message or list and if it is a single message convert it to a list
const normalizedMessages = (TypedArrayEncoder.isTypedArray(data) ? [data as Buffer] : data) as Buffer[]

// Get the Uint8Array variant of all the messages
const messageBuffers = normalizedMessages.map((m) => Uint8Array.from(m))

const publicKey = TypedArrayEncoder.fromBase58(publicKeyBase58)
const privateKey = TypedArrayEncoder.fromBase58(privateKeyBase58)

const bbsKeyPair = await bls12381toBbs({
keyPair: { publicKey: Uint8Array.from(publicKey), secretKey: Uint8Array.from(privateKey) },
messageCount: normalizedMessages.length,
})

// Sign the messages via the keyPair
const signature = await sign({
keyPair: bbsKeyPair,
messages: messageBuffers,
})

// Convert the Uint8Array signature to a Buffer type
return Buffer.from(signature)
}

/**
* Verify an arbitrary amount of messages with their signature created with their key pair
*
* @param publicKey Buffer The public key used to sign the messages
* @param messages Buffer[] The messages that have to be verified if they are signed
* @param signature Buffer The signature that has to be verified if it was created with the messages and public key
*
* @returns A boolean whether the signature is create with the public key over the messages
*
* @throws {SigningProviderError} When the message list is empty
* @throws {SigningProviderError} When the verification process failed
*/
public async verify({ data, publicKeyBase58, signature }: VerifyOptions): Promise<boolean> {
if (data.length === 0) throw new SigningProviderError('Unable to create a signature without any messages')
// Check if it is a single message or list and if it is a single message convert it to a list
const normalizedMessages = (TypedArrayEncoder.isTypedArray(data) ? [data as Buffer] : data) as Buffer[]

const publicKey = TypedArrayEncoder.fromBase58(publicKeyBase58)

// Get the Uint8Array variant of all the messages
const messageBuffers = normalizedMessages.map((m) => Uint8Array.from(m))

const bbsKeyPair = await bls12381toBbs({
keyPair: { publicKey: Uint8Array.from(publicKey) },
messageCount: normalizedMessages.length,
})

// Verify the signature against the messages with their public key
const { verified, error } = await verify({ signature, messages: messageBuffers, publicKey: bbsKeyPair.publicKey })

// If the messages could not be verified and an error occurred
if (!verified && error) {
throw new SigningProviderError(`Could not verify the signature against the messages: ${error}`)
}

return verified
}
}
32 changes: 32 additions & 0 deletions packages/core/src/crypto/signing-provider/SigningProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Buffer } from '../../utils/buffer'
import type { KeyType } from '../KeyType'

export interface KeyPair {
publicKeyBase58: string
privateKeyBase58: string
keyType: KeyType
}

export interface SignOptions {
data: Buffer | Buffer[]
publicKeyBase58: string
privateKeyBase58: string
}

export interface VerifyOptions {
data: Buffer | Buffer[]
publicKeyBase58: string
signature: Buffer
}

export interface CreateKeyPairOptions {
seed?: string
}

export interface SigningProvider {
readonly keyType: KeyType

createKeyPair(options: CreateKeyPairOptions): Promise<KeyPair>
sign(options: SignOptions): Promise<Buffer>
verify(options: VerifyOptions): Promise<boolean>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { AriesFrameworkError } from '../../error'

export class SigningProviderError extends AriesFrameworkError {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { KeyType } from '..'
import type { SigningProvider } from './SigningProvider'

import { AriesFrameworkError } from '../../error'
import { injectable, injectAll } from '../../plugins'

export const SigningProviderToken = Symbol('SigningProviderToken')

@injectable()
export class SigningProviderRegistry {
private signingKeyProviders: SigningProvider[]

public constructor(@injectAll(SigningProviderToken) signingKeyProviders: SigningProvider[]) {
this.signingKeyProviders = signingKeyProviders
}

public hasProviderForKeyType(keyType: KeyType): boolean {
const signingKeyProvider = this.signingKeyProviders.find((x) => x.keyType === keyType)

return signingKeyProvider !== undefined
}

public getProviderForKeyType(keyType: KeyType): SigningProvider {
const signingKeyProvider = this.signingKeyProviders.find((x) => x.keyType === keyType)

if (!signingKeyProvider) {
throw new AriesFrameworkError(`No signing key provider for key type: ${keyType}`)
}

return signingKeyProvider
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { Buffer } from '../../../utils/buffer'
import type { SigningProvider, KeyPair } from '../SigningProvider'

import { KeyType } from '../../KeyType'
import { SigningProviderRegistry } from '../SigningProviderRegistry'

class SigningProviderMock implements SigningProvider {
public readonly keyType = KeyType.Bls12381g2

public async createKeyPair(): Promise<KeyPair> {
throw new Error('Method not implemented.')
}
public async sign(): Promise<Buffer> {
throw new Error('Method not implemented.')
}
public async verify(): Promise<boolean> {
throw new Error('Method not implemented.')
}
}

const signingProvider = new SigningProviderMock()
const signingProviderRegistry = new SigningProviderRegistry([signingProvider])

describe('SigningProviderRegistry', () => {
describe('hasProviderForKeyType', () => {
test('returns true if the key type is registered', () => {
expect(signingProviderRegistry.hasProviderForKeyType(KeyType.Bls12381g2)).toBe(true)
})

test('returns false if the key type is not registered', () => {
expect(signingProviderRegistry.hasProviderForKeyType(KeyType.Ed25519)).toBe(false)
})
})

describe('getProviderForKeyType', () => {
test('returns the correct provider true if the key type is registered', () => {
expect(signingProviderRegistry.getProviderForKeyType(KeyType.Bls12381g2)).toBe(signingProvider)
})

test('throws error if the key type is not registered', () => {
expect(() => signingProviderRegistry.getProviderForKeyType(KeyType.Ed25519)).toThrowError(
'No signing key provider for key type: ed25519'
)
})
})
})
Loading

0 comments on commit ce90744

Please sign in to comment.