-
Notifications
You must be signed in to change notification settings - Fork 204
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
TimoGlastra
merged 4 commits into
openwallet-foundation:0.3.0-pre
from
TimoGlastra:feat/dynamic-signing-suite-provider
Jul 19, 2022
Merged
Changes from 2 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
111 changes: 111 additions & 0 deletions
111
packages/core/src/crypto/signing-provider/Bls12381g2SigningProvider.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
32
packages/core/src/crypto/signing-provider/SigningProvider.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
sign(options: SignOptions): Promise<Buffer> | ||
verify(options: VerifyOptions): Promise<boolean> | ||
} |
3 changes: 3 additions & 0 deletions
3
packages/core/src/crypto/signing-provider/SigningProviderError.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { AriesFrameworkError } from '../../error' | ||
|
||
export class SigningProviderError extends AriesFrameworkError {} |
32 changes: 32 additions & 0 deletions
32
packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
packages/core/src/crypto/signing-provider/__tests__/SigningProviderRegistry.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' | ||
) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For
createKey
I would expect a symmetric key.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense 👍