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: add dynamic suite and signing provider #949

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
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, CreateKeyOptions, 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 createKey({ seed }: CreateKeyOptions): 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 CreateKeyOptions {
seed?: string
}

export interface SigningProvider {
readonly keyType: KeyType

createKey(options: CreateKeyOptions): Promise<KeyPair>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
createKey(options: CreateKeyOptions): Promise<KeyPair>
createKeyPair(options: CreateKeyPairOptions): Promise<KeyPair>

For createKey I would expect a symmetric key.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense 👍

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 createKey(): 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