-
Notifications
You must be signed in to change notification settings - Fork 200
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add dynamic suite and signing provider (#949)
Signed-off-by: Timo Glastra <timo@animo.id>
- Loading branch information
1 parent
1e708e9
commit ab8b8ef
Showing
26 changed files
with
381 additions
and
230 deletions.
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, 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
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 CreateKeyPairOptions { | ||
seed?: string | ||
} | ||
|
||
export interface SigningProvider { | ||
readonly keyType: KeyType | ||
|
||
createKeyPair(options: CreateKeyPairOptions): 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 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' | ||
) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.