diff --git a/README.md b/README.md index e0d4b3c10..3a5d46bfe 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Decentralized Web Node (DWN) SDK Code Coverage -![Statements](https://img.shields.io/badge/statements-97.69%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-94.87%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-94.16%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-97.69%25-brightgreen.svg?style=flat) +![Statements](https://img.shields.io/badge/statements-97.74%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-95%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-94.2%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-97.74%25-brightgreen.svg?style=flat) - [Introduction](#introduction) - [Installation](#installation) @@ -92,7 +92,7 @@ DWN SDK includes a polyfilled distribution that can imported in a `module` scrip dataFormat: 'application/json', published: true, schema: 'yeeter/post', - authorizationSignatureInput: Jws.createSignatureInput(didKey) + authorizationSigner: Jws.createSigner(didKey) }); // get the DWN to process the RecordsWrite @@ -191,7 +191,7 @@ const recordsWrite = await RecordsWrite.create({ dataFormat: 'application/json', published: true, schema: 'yeeter/post', - authorizationSignatureInput: Jws.createSignatureInput(didKey) + authorizationSigner: Jws.createSigner(didKey) }); // get the DWN to process the RecordsWrite @@ -234,6 +234,30 @@ const tenantGate = new CustomTenantGate(); const dwn = await Dwn.create({ messageStore, dataStore, eventLog, tenantGate }); ``` +### Custom Signature Signer +If you have the private key readily available, it is recommended to use the built-in `PrivateKeySigner`. Otherwise, you can implement a customer signer to interface with external signing service, API, HSM, TPM etc and use it for signing your DWN messages: + +```ts +// create a custom signer +class CustomSigner implements Signer { + public keyId = 'did:example:alice#key1'; + public algorithm = 'EdDSA'; // use valid `alg` value published in https://www.iana.org/assignments/jose/jose.xhtml + public async sign (content: Uint8Array): Promise { + ... // custom signing logic + } +} + +const authorizationSigner = new CustomSigner(); + +const options: RecordsWriteOptions = { + ... + authorizationSigner +}; + +const recordsWrite = await RecordsWrite.create(options); +``` + + ## Release/Build Process The DWN JS SDK releases builds to [npmjs.com](https://www.npmjs.com/package/@tbd54566975/dwn-sdk-js). There are two build types: stable build and unstable build. diff --git a/src/core/dwn-error.ts b/src/core/dwn-error.ts index c3e3e3101..738e0645b 100644 --- a/src/core/dwn-error.ts +++ b/src/core/dwn-error.ts @@ -31,6 +31,9 @@ export enum DwnErrorCode { PermissionsGrantUnauthorizedGrant = 'PermissionsGrantUnauthorizedGrant', PermissionsRevokeMissingPermissionsGrant = 'PermissionsRevokeMissingPermissionsGrant', PermissionsRevokeUnauthorizedRevoke = 'PermissionsRevokeUnauthorizedRevoke', + PrivateKeySignerUnableToDeduceAlgorithm = 'PrivateKeySignerUnableToDeduceAlgorithm', + PrivateKeySignerUnableToDeduceKeyId = 'PrivateKeySignerUnableToDeduceKeyId', + PrivateKeySignerUnsupportedCurve = 'PrivateKeySignerUnsupportedCurve', ProtocolAuthorizationActionNotAllowed = 'ProtocolAuthorizationActionNotAllowed', ProtocolAuthorizationIncorrectDataFormat = 'ProtocolAuthorizationIncorrectDataFormat', ProtocolAuthorizationIncorrectProtocolPath = 'ProtocolAuthorizationIncorrectProtocolPath', @@ -56,7 +59,7 @@ export enum DwnErrorCode { RecordsWriteGetEntryIdUndefinedAuthor = 'RecordsWriteGetEntryIdUndefinedAuthor', RecordsWriteDataCidMismatch = 'RecordsWriteDataCidMismatch', RecordsWriteDataSizeMismatch = 'RecordsWriteDataSizeMismatch', - RecordsWriteMissingAuthorizationSignatureInput = 'RecordsWriteMissingAuthorizationSignatureInput', + RecordsWriteMissingAuthorizationSigner = 'RecordsWriteMissingAuthorizationSigner', RecordsWriteMissingDataInPrevious = 'RecordsWriteMissingDataInPrevious', RecordsWriteMissingDataAssociation = 'RecordsWriteMissingDataAssociation', RecordsWriteMissingDataStream = 'RecordsWriteMissingDataStream', diff --git a/src/core/message.ts b/src/core/message.ts index 936488e43..c070fa0ab 100644 --- a/src/core/message.ts +++ b/src/core/message.ts @@ -1,9 +1,9 @@ import type { GeneralJws } from '../types/jws-types.js'; -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { BaseAuthorizationPayload, Descriptor, GenericMessage } from '../types/message-types.js'; import { Cid } from '../utils/cid.js'; -import { GeneralJwsSigner } from '../jose/jws/general/signer.js'; +import { GeneralJwsBuilder } from '../jose/jws/general/builder.js'; import { Jws } from '../utils/jws.js'; import { lexicographicalCompare } from '../utils/string.js'; import { removeUndefinedProperties } from '../utils/object.js'; @@ -138,7 +138,7 @@ export abstract class Message { */ public static async signAsAuthorization( descriptor: Descriptor, - signatureInput: SignatureInput, + signatureInput: Signer, permissionsGrantId?: string, ): Promise { const descriptorCid = await Cid.computeCid(descriptor); @@ -148,9 +148,9 @@ export abstract class Message { const authPayloadStr = JSON.stringify(authPayload); const authPayloadBytes = new TextEncoder().encode(authPayloadStr); - const signer = await GeneralJwsSigner.create(authPayloadBytes, [signatureInput]); + const builder = await GeneralJwsBuilder.create(authPayloadBytes, [signatureInput]); - return signer.getJws(); + return builder.getJws(); } /** diff --git a/src/index.ts b/src/index.ts index 0f7d1ef28..615d5b4e9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,6 +38,7 @@ export { MessageStore, MessageStoreOptions } from './types/message-store.js'; export { PermissionsGrant, PermissionsGrantOptions } from './interfaces/permissions-grant.js'; export { PermissionsRequest, PermissionsRequestOptions } from './interfaces/permissions-request.js'; export { PermissionsRevoke, PermissionsRevokeOptions } from './interfaces/permissions-revoke.js'; +export { PrivateKeySigner } from './utils/private-key-signer.js'; export { Protocols } from './utils/protocols.js'; export { ProtocolsConfigure, ProtocolsConfigureOptions } from './interfaces/protocols-configure.js'; export { ProtocolsQuery, ProtocolsQueryOptions } from './interfaces/protocols-query.js'; @@ -46,7 +47,7 @@ export { RecordsDelete, RecordsDeleteOptions } from './interfaces/records-delete export { RecordsRead, RecordsReadOptions } from './interfaces/records-read.js'; export { SnapshotsCreate, SnapshotsCreateOptions } from './interfaces/snapshots-create.js'; export { Secp256k1 } from './utils/secp256k1.js'; -export { SignatureInput } from './types/jws-types.js'; +export { Signer } from './types/signer.js'; export { DataStoreLevel } from './store/data-store-level.js'; export { EventLogLevel } from './event-log/event-log-level.js'; export { MessageStoreLevel } from './store/message-store-level.js'; diff --git a/src/interfaces/events-get.ts b/src/interfaces/events-get.ts index b687df3a9..47f350e68 100644 --- a/src/interfaces/events-get.ts +++ b/src/interfaces/events-get.ts @@ -1,4 +1,4 @@ -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { EventsGetDescriptor, EventsGetMessage } from '../types/event-types.js'; import { getCurrentTimeInHighPrecision } from '../utils/time.js'; @@ -7,7 +7,7 @@ import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; export type EventsGetOptions = { watermark?: string; - authorizationSignatureInput: SignatureInput; + authorizationSigner: Signer; messageTimestamp?: string; }; @@ -31,7 +31,7 @@ export class EventsGet extends Message { descriptor.watermark = options.watermark; } - const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSigner); const message = { descriptor, authorization }; Message.validateJsonSchema(message); diff --git a/src/interfaces/hooks-write.ts b/src/interfaces/hooks-write.ts index c291a6b26..273fadb32 100644 --- a/src/interfaces/hooks-write.ts +++ b/src/interfaces/hooks-write.ts @@ -1,4 +1,4 @@ -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { HooksWriteDescriptor, HooksWriteMessage } from '../types/hooks-types.js'; import { getCurrentTimeInHighPrecision } from '../utils/time.js'; @@ -19,7 +19,7 @@ export type HooksWriteOptions = { filter: { method: string, }, - authorizationSignatureInput: SignatureInput; + authorizationSigner: Signer; }; /** @@ -43,7 +43,7 @@ export class HooksWrite extends Message { // Error: `undefined` is not supported by the IPLD Data Model and cannot be encoded removeUndefinedProperties(descriptor); - const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSigner); const message = { descriptor, authorization }; Message.validateJsonSchema(message); diff --git a/src/interfaces/messages-get.ts b/src/interfaces/messages-get.ts index 20072150c..b1d49c56a 100644 --- a/src/interfaces/messages-get.ts +++ b/src/interfaces/messages-get.ts @@ -1,4 +1,4 @@ -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { MessagesGetDescriptor, MessagesGetMessage } from '../types/messages-types.js'; import { Cid } from '../utils/cid.js'; @@ -8,7 +8,7 @@ import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; export type MessagesGetOptions = { messageCids: string[]; - authorizationSignatureInput: SignatureInput; + authorizationSigner: Signer; messageTimestamp?: string; }; @@ -30,7 +30,7 @@ export class MessagesGet extends Message { messageTimestamp : options?.messageTimestamp ?? getCurrentTimeInHighPrecision(), }; - const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSigner); const message = { descriptor, authorization }; Message.validateJsonSchema(message); diff --git a/src/interfaces/permissions-grant.ts b/src/interfaces/permissions-grant.ts index 3253423f0..16a1a59af 100644 --- a/src/interfaces/permissions-grant.ts +++ b/src/interfaces/permissions-grant.ts @@ -1,5 +1,5 @@ import type { PermissionsRequest } from './permissions-request.js'; -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { PermissionConditions, PermissionScope, RecordsPermissionScope } from '../types/permissions-types.js'; import type { PermissionsGrantDescriptor, PermissionsGrantMessage } from '../types/permissions-types.js'; @@ -19,7 +19,7 @@ export type PermissionsGrantOptions = { permissionsRequestId?: string; scope: PermissionScope; conditions?: PermissionConditions; - authorizationSignatureInput: SignatureInput; + authorizationSigner: Signer; }; export type CreateFromPermissionsRequestOverrides = { @@ -60,7 +60,7 @@ export class PermissionsGrant extends Message { // Error: `undefined` is not supported by the IPLD Data Model and cannot be encoded removeUndefinedProperties(descriptor); - const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSigner); const message: PermissionsGrantMessage = { descriptor, authorization }; Message.validateJsonSchema(message); @@ -72,12 +72,12 @@ export class PermissionsGrant extends Message { /** * generates a PermissionsGrant using the provided PermissionsRequest * @param permissionsRequest - * @param authorizationSignatureInput - the private key and additional signature material of the grantor + * @param authorizationSigner - the private key and additional signature material of the grantor * @param overrides - overrides that will be used instead of the properties in `permissionsRequest` */ public static async createFromPermissionsRequest( permissionsRequest: PermissionsRequest, - authorizationSignatureInput: SignatureInput, + authorizationSigner: Signer, overrides: CreateFromPermissionsRequestOverrides, ): Promise { const descriptor = permissionsRequest.message.descriptor; @@ -90,7 +90,7 @@ export class PermissionsGrant extends Message { permissionsRequestId : await Message.getCid(permissionsRequest.message), scope : overrides.scope ?? descriptor.scope, conditions : overrides.conditions ?? descriptor.conditions, - authorizationSignatureInput, + authorizationSigner, }); } diff --git a/src/interfaces/permissions-request.ts b/src/interfaces/permissions-request.ts index 34b7c4215..e154a9c02 100644 --- a/src/interfaces/permissions-request.ts +++ b/src/interfaces/permissions-request.ts @@ -1,4 +1,4 @@ -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { PermissionConditions, PermissionScope } from '../types/permissions-types.js'; import type { PermissionsRequestDescriptor, PermissionsRequestMessage } from '../types/permissions-types.js'; @@ -15,7 +15,7 @@ export type PermissionsRequestOptions = { grantedFor: string; scope: PermissionScope; conditions?: PermissionConditions; - authorizationSignatureInput: SignatureInput; + authorizationSigner: Signer; }; export class PermissionsRequest extends Message { @@ -43,7 +43,7 @@ export class PermissionsRequest extends Message { // Error: `undefined` is not supported by the IPLD Data Model and cannot be encoded removeUndefinedProperties(descriptor); - const auth = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput); + const auth = await Message.signAsAuthorization(descriptor, options.authorizationSigner); const message: PermissionsRequestMessage = { descriptor, authorization: auth }; Message.validateJsonSchema(message); diff --git a/src/interfaces/permissions-revoke.ts b/src/interfaces/permissions-revoke.ts index 43635e97e..4817150d8 100644 --- a/src/interfaces/permissions-revoke.ts +++ b/src/interfaces/permissions-revoke.ts @@ -1,4 +1,4 @@ -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { PermissionsGrantMessage, PermissionsRevokeDescriptor, PermissionsRevokeMessage } from '../types/permissions-types.js'; import { getCurrentTimeInHighPrecision } from '../utils/time.js'; @@ -9,7 +9,7 @@ import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; export type PermissionsRevokeOptions = { messageTimestamp?: string; permissionsGrantId: string; - authorizationSignatureInput: SignatureInput; + authorizationSigner: Signer; }; export class PermissionsRevoke extends Message { @@ -27,7 +27,7 @@ export class PermissionsRevoke extends Message { permissionsGrantId : options.permissionsGrantId, }; - const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSigner); const message: PermissionsRevokeMessage = { descriptor, authorization }; Message.validateJsonSchema(message); diff --git a/src/interfaces/protocols-configure.ts b/src/interfaces/protocols-configure.ts index cdda8b70a..8bdbfcd8d 100644 --- a/src/interfaces/protocols-configure.ts +++ b/src/interfaces/protocols-configure.ts @@ -1,4 +1,4 @@ -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { ProtocolDefinition, ProtocolsConfigureDescriptor, ProtocolsConfigureMessage } from '../types/protocols-types.js'; import { getCurrentTimeInHighPrecision } from '../utils/time.js'; @@ -9,7 +9,7 @@ import { normalizeProtocolUrl, normalizeSchemaUrl, validateProtocolUrlNormalized export type ProtocolsConfigureOptions = { messageTimestamp? : string; definition : ProtocolDefinition; - authorizationSignatureInput: SignatureInput; + authorizationSigner: Signer; permissionsGrantId?: string; }; @@ -32,7 +32,7 @@ export class ProtocolsConfigure extends Message { definition : ProtocolsConfigure.normalizeDefinition(options.definition) }; - const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput, options.permissionsGrantId); + const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSigner, options.permissionsGrantId); const message = { descriptor, authorization }; Message.validateJsonSchema(message); diff --git a/src/interfaces/protocols-query.ts b/src/interfaces/protocols-query.ts index 3fbb0dd13..3c8127139 100644 --- a/src/interfaces/protocols-query.ts +++ b/src/interfaces/protocols-query.ts @@ -1,5 +1,6 @@ +import type { GeneralJws } from '../types/jws-types.js'; import type { MessageStore } from '../types/message-store.js'; -import type { GeneralJws, SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { ProtocolsQueryDescriptor, ProtocolsQueryFilter, ProtocolsQueryMessage } from '../types/protocols-types.js'; import { getCurrentTimeInHighPrecision } from '../utils/time.js'; @@ -14,7 +15,7 @@ import { DwnError, DwnErrorCode } from '../core/dwn-error.js'; export type ProtocolsQueryOptions = { messageTimestamp?: string; filter?: ProtocolsQueryFilter, - authorizationSignatureInput?: SignatureInput; + authorizationSigner?: Signer; permissionsGrantId?: string; }; @@ -46,8 +47,8 @@ export class ProtocolsQuery extends Message { // only generate the `authorization` property if signature input is given let authorization: GeneralJws | undefined; - if (options.authorizationSignatureInput !== undefined) { - authorization = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput, options.permissionsGrantId); + if (options.authorizationSigner !== undefined) { + authorization = await Message.signAsAuthorization(descriptor, options.authorizationSigner, options.permissionsGrantId); } const message = { descriptor, authorization }; diff --git a/src/interfaces/records-delete.ts b/src/interfaces/records-delete.ts index 04f4e4f08..2a0366eec 100644 --- a/src/interfaces/records-delete.ts +++ b/src/interfaces/records-delete.ts @@ -2,7 +2,7 @@ import type { RecordsDeleteDescriptor, RecordsDeleteMessage } from '../types/rec import { getCurrentTimeInHighPrecision } from '../utils/time.js'; import { Message } from '../core/message.js'; -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import { authorize, validateAuthorizationIntegrity } from '../core/auth.js'; import { DwnInterfaceName, DwnMethodName } from '../core/message.js'; @@ -10,7 +10,7 @@ import { DwnInterfaceName, DwnMethodName } from '../core/message.js'; export type RecordsDeleteOptions = { recordId: string; messageTimestamp?: string; - authorizationSignatureInput: SignatureInput; + authorizationSigner: Signer; }; export class RecordsDelete extends Message { @@ -38,7 +38,7 @@ export class RecordsDelete extends Message { messageTimestamp : options.messageTimestamp ?? currentTime }; - const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSigner); const message: RecordsDeleteMessage = { descriptor, authorization }; Message.validateJsonSchema(message); diff --git a/src/interfaces/records-query.ts b/src/interfaces/records-query.ts index 105d302c8..1e67ecd6c 100644 --- a/src/interfaces/records-query.ts +++ b/src/interfaces/records-query.ts @@ -1,5 +1,5 @@ import type { Pagination } from '../types/message-types.js'; -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { RecordsFilter, RecordsQueryDescriptor, RecordsQueryMessage } from '../types/records-types.js'; import { getCurrentTimeInHighPrecision } from '../utils/time.js'; @@ -22,7 +22,7 @@ export type RecordsQueryOptions = { filter: RecordsFilter; dateSort?: DateSort; pagination?: Pagination; - authorizationSignatureInput?: SignatureInput; + authorizationSigner?: Signer; }; export class RecordsQuery extends Message { @@ -57,8 +57,8 @@ export class RecordsQuery extends Message { removeUndefinedProperties(descriptor); // only generate the `authorization` property if signature input is given - const authorizationSignatureInput = options.authorizationSignatureInput; - const authorization = authorizationSignatureInput ? await Message.signAsAuthorization(descriptor, authorizationSignatureInput) : undefined; + const authorizationSigner = options.authorizationSigner; + const authorization = authorizationSigner ? await Message.signAsAuthorization(descriptor, authorizationSigner) : undefined; const message = { descriptor, authorization }; Message.validateJsonSchema(message); diff --git a/src/interfaces/records-read.ts b/src/interfaces/records-read.ts index 1bdd793a4..2af5a6d78 100644 --- a/src/interfaces/records-read.ts +++ b/src/interfaces/records-read.ts @@ -1,7 +1,7 @@ import type { GenericMessage } from '../types/message-types.js'; import type { MessageStore } from '../types/message-store.js'; import type { RecordsWrite } from './records-write.js'; -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { RecordsFilter , RecordsReadDescriptor, RecordsReadMessage } from '../types/records-types.js'; import { getCurrentTimeInHighPrecision } from '../utils/time.js'; @@ -16,7 +16,7 @@ import { DwnInterfaceName, DwnMethodName } from '../core/message.js'; export type RecordsReadOptions = { filter: RecordsFilter; date?: string; - authorizationSignatureInput?: SignatureInput; + authorizationSigner?: Signer; permissionsGrantId?: string; }; @@ -39,7 +39,7 @@ export class RecordsRead extends Message { * @throws {DwnError} when a combination of required RecordsReadOptions are missing */ public static async create(options: RecordsReadOptions): Promise { - const { filter, authorizationSignatureInput, permissionsGrantId } = options; + const { filter, authorizationSigner, permissionsGrantId } = options; const currentTime = getCurrentTimeInHighPrecision(); const descriptor: RecordsReadDescriptor = { @@ -53,8 +53,8 @@ export class RecordsRead extends Message { // only generate the `authorization` property if signature input is given let authorization = undefined; - if (authorizationSignatureInput !== undefined) { - authorization = await Message.signAsAuthorization(descriptor, authorizationSignatureInput, permissionsGrantId); + if (authorizationSigner !== undefined) { + authorization = await Message.signAsAuthorization(descriptor, authorizationSigner, permissionsGrantId); } const message: RecordsReadMessage = { descriptor, authorization }; diff --git a/src/interfaces/records-write.ts b/src/interfaces/records-write.ts index 321b1a1f3..285a7bce0 100644 --- a/src/interfaces/records-write.ts +++ b/src/interfaces/records-write.ts @@ -1,6 +1,8 @@ +import type { GeneralJws } from '../types/jws-types.js'; import type { GenericMessage } from '../types/message-types.js'; import type { MessageStore } from '../types/message-store.js'; import type { PublicJwk } from '../types/jose-types.js'; +import type { Signer } from '../types/signer.js'; import type { EncryptedKey, EncryptionProperty, @@ -11,16 +13,15 @@ import type { RecordsWriteMessage, UnsignedRecordsWriteMessage } from '../types/records-types.js'; -import type { GeneralJws, SignatureInput } from '../types/jws-types.js'; import { Cid } from '../utils/cid.js'; import { Encoder } from '../utils/encoder.js'; import { Encryption } from '../utils/encryption.js'; import { EncryptionAlgorithm } from '../utils/encryption.js'; -import { GeneralJwsSigner } from '../jose/jws/general/signer.js'; +import { GeneralJwsBuilder } from '../jose/jws/general/builder.js'; import { getCurrentTimeInHighPrecision } from '../utils/time.js'; import { Jws } from '../utils/jws.js'; -import { KeyDerivationScheme } from '../index.js'; +import { KeyDerivationScheme } from '../utils/hd-key.js'; import { Message } from '../core/message.js'; import { ProtocolAuthorization } from '../core/protocol-authorization.js'; import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js'; @@ -47,8 +48,8 @@ export type RecordsWriteOptions = { published?: boolean; datePublished?: string; dataFormat: string; - authorizationSignatureInput?: SignatureInput; - attestationSignatureInputs?: SignatureInput[]; + authorizationSigner?: Signer; + attestationSigners?: Signer[]; encryptionInput?: EncryptionInput; permissionsGrantId?: string; }; @@ -111,8 +112,8 @@ export type CreateFromOptions = { published?: boolean; messageTimestamp?: string; datePublished?: string; - authorizationSignatureInput?: SignatureInput; - attestationSignatureInputs?: SignatureInput[]; + authorizationSigner?: Signer; + attestationSigners?: Signer[]; encryptionInput?: EncryptionInput; }; @@ -120,12 +121,12 @@ export class RecordsWrite { private _message: InternalRecordsWriteMessage; /** * Valid JSON message representing this RecordsWrite. - * @throws `DwnErrorCode.RecordsWriteMissingAuthorizationSignatureInput` if the message is not signed yet. + * @throws `DwnErrorCode.RecordsWriteMissingAuthorizationSigner` if the message is not signed yet. */ public get message(): RecordsWriteMessage { if (this._message.authorization === undefined) { throw new DwnError( - DwnErrorCode.RecordsWriteMissingAuthorizationSignatureInput, + DwnErrorCode.RecordsWriteMissingAuthorizationSigner, 'This RecordsWrite is not yet signed, JSON message cannot be generated from an incomplete state.' ); } @@ -246,7 +247,7 @@ export class RecordsWrite { // `attestation` generation const descriptorCid = await Cid.computeCid(descriptor); - const attestation = await RecordsWrite.createAttestation(descriptorCid, options.attestationSignatureInputs); + const attestation = await RecordsWrite.createAttestation(descriptorCid, options.attestationSigners); // `encryption` generation const encryption = await RecordsWrite.createEncryptionProperty(descriptor, options.encryptionInput); @@ -265,8 +266,8 @@ export class RecordsWrite { const recordsWrite = new RecordsWrite(message); - if (options.authorizationSignatureInput !== undefined) { - await recordsWrite.sign(options.authorizationSignatureInput, options.permissionsGrantId); + if (options.authorizationSigner !== undefined) { + await recordsWrite.sign(options.authorizationSigner, options.permissionsGrantId); } return recordsWrite; @@ -313,25 +314,25 @@ export class RecordsWrite { const createOptions: RecordsWriteOptions = { // immutable properties below, just inherit from the message given - recipient : unsignedMessage.descriptor.recipient, - recordId : unsignedMessage.recordId, - dateCreated : unsignedMessage.descriptor.dateCreated, - contextId : unsignedMessage.contextId, - protocol : unsignedMessage.descriptor.protocol, - protocolPath : unsignedMessage.descriptor.protocolPath, - parentId : unsignedMessage.descriptor.parentId, - schema : unsignedMessage.descriptor.schema, - dataFormat : unsignedMessage.descriptor.dataFormat, + recipient : unsignedMessage.descriptor.recipient, + recordId : unsignedMessage.recordId, + dateCreated : unsignedMessage.descriptor.dateCreated, + contextId : unsignedMessage.contextId, + protocol : unsignedMessage.descriptor.protocol, + protocolPath : unsignedMessage.descriptor.protocolPath, + parentId : unsignedMessage.descriptor.parentId, + schema : unsignedMessage.descriptor.schema, + dataFormat : unsignedMessage.descriptor.dataFormat, // mutable properties below - messageTimestamp : options.messageTimestamp ?? currentTime, + messageTimestamp : options.messageTimestamp ?? currentTime, published, datePublished, - data : options.data, - dataCid : options.data ? undefined : unsignedMessage.descriptor.dataCid, // if data not given, use base message dataCid - dataSize : options.data ? undefined : unsignedMessage.descriptor.dataSize, // if data not given, use base message dataSize - // finally still need input for signing - authorizationSignatureInput : options.authorizationSignatureInput, - attestationSignatureInputs : options.attestationSignatureInputs + data : options.data, + dataCid : options.data ? undefined : unsignedMessage.descriptor.dataCid, // if data not given, use base message dataCid + dataSize : options.data ? undefined : unsignedMessage.descriptor.dataSize, // if data not given, use base message dataSize + // finally still need signers + authorizationSigner : options.authorizationSigner, + attestationSigners : options.attestationSigners }; const recordsWrite = await RecordsWrite.create(createOptions); @@ -360,8 +361,8 @@ export class RecordsWrite { /** * Signs the RecordsWrite. */ - public async sign(signatureInput: SignatureInput, permissionsGrantId?: string): Promise { - const author = Jws.extractDid(signatureInput.protectedHeader.kid); + public async sign(signer: Signer, permissionsGrantId?: string): Promise { + const author = Jws.extractDid(signer.keyId); const descriptor = this._message.descriptor; const descriptorCid = await Cid.computeCid(descriptor); @@ -381,7 +382,7 @@ export class RecordsWrite { descriptorCid, this._message.attestation, this._message.encryption, - signatureInput, + signer, permissionsGrantId ); @@ -627,16 +628,16 @@ export class RecordsWrite { /** * Creates the `attestation` property of a RecordsWrite message if given signature inputs; returns `undefined` otherwise. */ - public static async createAttestation(descriptorCid: string, signatureInputs?: SignatureInput[]): Promise { - if (signatureInputs === undefined || signatureInputs.length === 0) { + public static async createAttestation(descriptorCid: string, signers?: Signer[]): Promise { + if (signers === undefined || signers.length === 0) { return undefined; } const attestationPayload: RecordsWriteAttestationPayload = { descriptorCid }; const attestationPayloadBytes = Encoder.objectToBytes(attestationPayload); - const signer = await GeneralJwsSigner.create(attestationPayloadBytes, signatureInputs); - return signer.getJws(); + const builder = await GeneralJwsBuilder.create(attestationPayloadBytes, signers); + return builder.getJws(); } /** @@ -648,7 +649,7 @@ export class RecordsWrite { descriptorCid: string, attestation: GeneralJws | undefined, encryption: EncryptionProperty | undefined, - signatureInput: SignatureInput, + signer: Signer, permissionsGrantId: string | undefined, ): Promise { const authorizationPayload: RecordsWriteAuthorizationPayload = { @@ -666,8 +667,8 @@ export class RecordsWrite { const authorizationPayloadBytes = Encoder.objectToBytes(authorizationPayload); - const signer = await GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput]); - return signer.getJws(); + const builder = await GeneralJwsBuilder.create(authorizationPayloadBytes, [signer]); + return builder.getJws(); } /** diff --git a/src/interfaces/snapshots-create.ts b/src/interfaces/snapshots-create.ts index 71d5c408b..eb738b1fd 100644 --- a/src/interfaces/snapshots-create.ts +++ b/src/interfaces/snapshots-create.ts @@ -1,4 +1,4 @@ -import type { SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { SnapshotDefinition, SnapshotsCreateDescriptor, SnapshotsCreateMessage } from '../types/snapshots-types.js'; import { Cid } from '../utils/cid.js'; @@ -10,7 +10,7 @@ import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; export type SnapshotsCreateOptions = { messageTimestamp? : string; definition : SnapshotDefinition; - authorizationSignatureInput: SignatureInput; + authorizationSigner: Signer; }; export class SnapshotsCreate extends Message { @@ -32,7 +32,7 @@ export class SnapshotsCreate extends Message { definitionCid }; - const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSigner); const message = { descriptor, authorization }; Message.validateJsonSchema(message); diff --git a/src/jose/algorithms/signing/ed25519.ts b/src/jose/algorithms/signing/ed25519.ts index 791ac1a3c..6211700a5 100644 --- a/src/jose/algorithms/signing/ed25519.ts +++ b/src/jose/algorithms/signing/ed25519.ts @@ -1,5 +1,5 @@ import * as Ed25519 from '@noble/ed25519'; -import type { PrivateJwk, PublicJwk, Signer } from '../../../types/jose-types.js'; +import type { PrivateJwk, PublicJwk, SignatureAlgorithm } from '../../../types/jose-types.js'; import { Encoder } from '../../../utils/encoder.js'; @@ -22,7 +22,7 @@ function publicKeyToJwk(publicKeyBytes: Uint8Array): PublicJwk { return publicJwk; } -export const ed25519: Signer = { +export const ed25519: SignatureAlgorithm = { sign: async (content: Uint8Array, privateJwk: PrivateJwk): Promise => { validateKey(privateJwk); diff --git a/src/jose/algorithms/signing/signers.ts b/src/jose/algorithms/signing/signature-algorithms.ts similarity index 71% rename from src/jose/algorithms/signing/signers.ts rename to src/jose/algorithms/signing/signature-algorithms.ts index 758e81cb3..0c62c0b5d 100644 --- a/src/jose/algorithms/signing/signers.ts +++ b/src/jose/algorithms/signing/signature-algorithms.ts @@ -1,10 +1,10 @@ -import type { Signer } from '../../../types/jose-types.js'; +import type { SignatureAlgorithm } from '../../../types/jose-types.js'; import { ed25519 } from './ed25519.js'; import { Secp256k1 } from '../../../utils/secp256k1.js'; // the key should be the appropriate `crv` value -export const signers: Record = { +export const signatureAlgorithms: Record = { 'Ed25519' : ed25519, 'secp256k1' : { sign : Secp256k1.sign, diff --git a/src/jose/jws/general/builder.ts b/src/jose/jws/general/builder.ts new file mode 100644 index 000000000..c3b9d32e4 --- /dev/null +++ b/src/jose/jws/general/builder.ts @@ -0,0 +1,48 @@ +import type { GeneralJws } from '../../../types/jws-types.js'; +import type { Signer } from '../../../types/signer.js'; + +import { Encoder } from '../../../utils/encoder.js'; + +export class GeneralJwsBuilder { + private jws: GeneralJws; + + private constructor(jws: GeneralJws) { + this.jws = jws; + } + + static async create(payload: Uint8Array, signers: Signer[] = []): Promise { + const jws: GeneralJws = { + payload : Encoder.bytesToBase64Url(payload), + signatures : [] + }; + + const builder = new GeneralJwsBuilder(jws); + + for (const signer of signers) { + await builder.addSignature(signer); + } + + return builder; + } + + async addSignature(signer: Signer): Promise { + const protectedHeader = { + kid : signer.keyId, + alg : signer.algorithm + }; + const protectedHeaderString = JSON.stringify(protectedHeader); + const protectedHeaderBase64UrlString = Encoder.stringToBase64Url(protectedHeaderString); + + const signingInputString = `${protectedHeaderBase64UrlString}.${this.jws.payload}`; + const signingInputBytes = Encoder.stringToBytes(signingInputString); + + const signatureBytes = await signer.sign(signingInputBytes); + const signature = Encoder.bytesToBase64Url(signatureBytes); + + this.jws.signatures.push({ protected: protectedHeaderBase64UrlString, signature }); + } + + getJws(): GeneralJws { + return this.jws; + } +} \ No newline at end of file diff --git a/src/jose/jws/general/signer.ts b/src/jose/jws/general/signer.ts deleted file mode 100644 index 5f56e588b..000000000 --- a/src/jose/jws/general/signer.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { GeneralJws, SignatureInput } from '../../../types/jws-types.js'; - -import { Encoder } from '../../../utils/encoder.js'; -import { signers } from '../../algorithms/signing/signers.js'; - -export class GeneralJwsSigner { - private jws: GeneralJws; - - constructor(jws: GeneralJws) { - this.jws = jws; - } - - static async create(payload: Uint8Array, signatureInputs: SignatureInput[] = []): Promise { - const jws: GeneralJws = { - payload : Encoder.bytesToBase64Url(payload), - signatures : [] - }; - - const signer = new GeneralJwsSigner(jws); - - for (const signatureInput of signatureInputs) { - await signer.addSignature(signatureInput); - } - - return signer; - } - - async addSignature(signatureInput: SignatureInput): Promise { - const { privateJwk, protectedHeader } = signatureInput; - const signer = signers[privateJwk.crv]; - - if (!signer) { - throw new Error(`unsupported crv. crv must be one of ${Object.keys(signers)}`); - } - - const protectedHeaderString = JSON.stringify(protectedHeader); - const protectedHeaderBase64UrlString = Encoder.stringToBase64Url(protectedHeaderString); - - const signingInputString = `${protectedHeaderBase64UrlString}.${this.jws.payload}`; - const signingInputBytes = Encoder.stringToBytes(signingInputString); - - const signatureBytes = await signer.sign(signingInputBytes, privateJwk); - const signature = Encoder.bytesToBase64Url(signatureBytes); - - this.jws.signatures.push({ protected: protectedHeaderBase64UrlString, signature }); - } - - getJws(): GeneralJws { - return this.jws; - } -} \ No newline at end of file diff --git a/src/types/jose-types.ts b/src/types/jose-types.ts index 1a07ba232..adbe45639 100644 --- a/src/types/jose-types.ts +++ b/src/types/jose-types.ts @@ -42,7 +42,7 @@ export type PrivateJwk = PublicJwk & { d: string; }; -export interface Signer { +export interface SignatureAlgorithm { /** * signs the provided payload using the provided JWK * @param content - the content to sign diff --git a/src/types/jws-types.ts b/src/types/jws-types.ts index 85d93d3ed..25e8b7327 100644 --- a/src/types/jws-types.ts +++ b/src/types/jws-types.ts @@ -1,4 +1,3 @@ -import type { PrivateJwk } from './jose-types.js'; /** * General JWS definition. Payload is returned as an empty * string when JWS Unencoded Payload Option @@ -27,18 +26,3 @@ export type SignatureEntry = { */ signature: string }; - -export type JwsHeaderParameters = { - /** JWS "alg" (Algorithm) Header Parameter. */ - alg: string - /** JWS "kid" (Key ID) Parameter. */ - kid: string -}; - -/** - * Input required to sign a DWN message. - */ -export type SignatureInput = { - protectedHeader: JwsHeaderParameters - privateJwk: PrivateJwk -}; \ No newline at end of file diff --git a/src/types/signer.ts b/src/types/signer.ts new file mode 100644 index 000000000..78281e3b4 --- /dev/null +++ b/src/types/signer.ts @@ -0,0 +1,27 @@ +/** + * A signer that is capable of generating a digital signature over any given bytes. + */ +export interface Signer { + /** + * The ID of the key used by this signer. + * This needs to be a fully-qualified ID (ie. prefixed with DID) so that author can be parsed out for processing such as `recordId` computation. + * Example: did:example:alice#key1 + * This value will be used as the "kid" parameter in JWS produced. + * While this property is not a required property per JWS specification, it is required for DWN authentication. + */ + keyId: string + + /** + * The name of the signature algorithm used by this signer. + * This value will be used as the "alg" parameter in JWS produced. + * This parameter is not used by the DWN but is unfortunately a required header property for a JWS as per: + * https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.1 + * Valid signature algorithm values can be found at https://www.iana.org/assignments/jose/jose.xhtml + */ + algorithm: string; + + /** + * Signs the given content and returns the signature as bytes. + */ + sign (content: Uint8Array): Promise; +} diff --git a/src/utils/jws.ts b/src/utils/jws.ts index b84799e7f..ae56a0eab 100644 --- a/src/utils/jws.ts +++ b/src/utils/jws.ts @@ -1,11 +1,13 @@ +import type { GeneralJws } from '../types/jws-types.js'; import type { SignatureEntry } from '../types/jws-types.js'; -import type { GeneralJws, SignatureInput } from '../types/jws-types.js'; +import type { Signer } from '../types/signer.js'; import type { KeyMaterial, PublicJwk } from '../types/jose-types.js'; import isPlainObject from 'lodash/isPlainObject.js'; import { Encoder } from './encoder.js'; -import { signers as verifiers } from '../jose/algorithms/signing/signers.js'; +import { PrivateKeySigner } from './private-key-signer.js'; +import { signatureAlgorithms } from '../jose/algorithms/signing/signature-algorithms.js'; /** @@ -34,16 +36,16 @@ export class Jws { * @returns `true` if signature is valid; `false` otherwise */ public static async verifySignature(base64UrlPayload: string, signatureEntry: SignatureEntry, jwkPublic: PublicJwk): Promise { - const verifier = verifiers[jwkPublic.crv]; + const signatureAlgorithm = signatureAlgorithms[jwkPublic.crv]; - if (!verifier) { - throw new Error(`unsupported crv. crv must be one of ${Object.keys(verifiers)}`); + if (!signatureAlgorithm) { + throw new Error(`unsupported crv. crv must be one of ${Object.keys(signatureAlgorithms)}`); } const payload = Encoder.stringToBytes(`${signatureEntry.protected}.${base64UrlPayload}`); const signatureBytes = Encoder.base64UrlToBytes(signatureEntry.signature); - return await verifier.verify(payload, signatureBytes, jwkPublic); + return await signatureAlgorithm.verify(payload, signatureBytes, jwkPublic); } /** @@ -73,25 +75,20 @@ export class Jws { } /** - * Creates a SignatureInput[] from the given Personas. + * Creates a Signer[] from the given Personas. */ - public static createSignatureInputs(keyMaterials: KeyMaterial[]): SignatureInput[] { - const signatureInputs = keyMaterials.map((keyMaterial) => Jws.createSignatureInput(keyMaterial)); + public static createSigners(keyMaterials: KeyMaterial[]): Signer[] { + const signatureInputs = keyMaterials.map((keyMaterial) => Jws.createSigner(keyMaterial)); return signatureInputs; } /** - * Creates a SignatureInput from the given Persona. + * Creates a Signer from the given Persona. */ - public static createSignatureInput(keyMaterial: KeyMaterial): SignatureInput { - const signatureInput = { - privateJwk : keyMaterial.keyPair.privateJwk, - protectedHeader : { - alg : keyMaterial.keyPair.privateJwk.alg as string, - kid : keyMaterial.keyId - } - }; - - return signatureInput; + public static createSigner(keyMaterial: KeyMaterial): Signer { + const privateJwk = keyMaterial.keyPair.privateJwk; + const keyId = keyMaterial.keyId; + const signer = new PrivateKeySigner({ privateJwk, keyId }); + return signer; } } diff --git a/src/utils/private-key-signer.ts b/src/utils/private-key-signer.ts new file mode 100644 index 000000000..fe6b3d967 --- /dev/null +++ b/src/utils/private-key-signer.ts @@ -0,0 +1,72 @@ +import type { PrivateJwk } from '../types/jose-types.js'; +import type { Signer } from '../types/signer.js'; + +import { signatureAlgorithms } from '../jose/algorithms/signing/signature-algorithms.js'; +import { DwnError, DwnErrorCode } from '../index.js'; + +/** + * Input to `PrivateKeySigner` constructor. + */ +export type PrivateKeySignerOptions = { + /** + * Private JWK to create the signer from. + */ + privateJwk: PrivateJwk; + + /** + * If not specified, the constructor will attempt to default/fall back to the `kid` value in the given `privateJwk`. + */ + keyId?: string; + + /** + * If not specified, the constructor will attempt to default/fall back to the `alg` value in the given `privateJwk`. + */ + algorithm?: string; +}; + +/** + * A signer that signs using a private key. + */ +export class PrivateKeySigner implements Signer { + public keyId; + public algorithm; + private privateJwk: PrivateJwk; + private signatureAlgorithm; + + public constructor(options: PrivateKeySignerOptions) { + if (options.keyId === undefined && options.privateJwk.kid === undefined) { + throw new DwnError( + DwnErrorCode.PrivateKeySignerUnableToDeduceKeyId, + `Unable to deduce the key ID` + ); + } + + // NOTE: `alg` is optional for a JWK as specified in https://datatracker.ietf.org/doc/html/rfc7517#section-4.4 + if (options.algorithm === undefined && options.privateJwk.alg === undefined) { + throw new DwnError( + DwnErrorCode.PrivateKeySignerUnableToDeduceAlgorithm, + `Unable to deduce the signature algorithm` + ); + } + + this.keyId = options.keyId ?? options.privateJwk.kid!; + this.algorithm = options.algorithm ?? options.privateJwk.alg!; + this.privateJwk = options.privateJwk; + this.signatureAlgorithm = signatureAlgorithms[options.privateJwk.crv]; + + if (!this.signatureAlgorithm) { + throw new DwnError( + DwnErrorCode.PrivateKeySignerUnsupportedCurve, + `Unsupported crv ${options.privateJwk.crv}, crv must be one of ${Object.keys(signatureAlgorithms)}` + ); + } + } + + /** + * Signs the given content and returns the signature as bytes. + */ + public async sign (content: Uint8Array): Promise { + const signatureBytes = await this.signatureAlgorithm.sign(content, this.privateJwk); + return signatureBytes; + } +} diff --git a/tests/dwn.spec.ts b/tests/dwn.spec.ts index 1cad50e08..ff3c8e712 100644 --- a/tests/dwn.spec.ts +++ b/tests/dwn.spec.ts @@ -154,7 +154,7 @@ export function testDwnClass(): void { filter: { recordId: 'recordId-doesnt-matter', }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); (recordsRead.message as any).descriptor.method = 'Write'; // Will cause interface and method check to fail const reply = await dwn.handleRecordsRead(alice.did, recordsRead.message); diff --git a/tests/handlers/permissions-grant.spec.ts b/tests/handlers/permissions-grant.spec.ts index 3f32ca396..fb8b989f9 100644 --- a/tests/handlers/permissions-grant.spec.ts +++ b/tests/handlers/permissions-grant.spec.ts @@ -171,7 +171,7 @@ export function testPermissionsGrantHandler(): void { }; schemaAndProtocolGrant.message.authorization = await Message.signAsAuthorization( schemaAndProtocolGrant.message.descriptor, - Jws.createSignatureInput(alice) + Jws.createSigner(alice) ); const schemaAndProtocolGrantReply = await dwn.processMessage(alice.did, schemaAndProtocolGrant.message); expect(schemaAndProtocolGrantReply.status.code).to.eq(400); @@ -189,7 +189,7 @@ export function testPermissionsGrantHandler(): void { }; schemaAndContextIdGrant.message.authorization = await Message.signAsAuthorization( schemaAndContextIdGrant.message.descriptor, - Jws.createSignatureInput(alice) + Jws.createSigner(alice) ); const schemaAndContextIdGrantReply = await dwn.processMessage(alice.did, schemaAndProtocolGrant.message); expect(schemaAndContextIdGrantReply.status.code).to.eq(400); @@ -207,7 +207,7 @@ export function testPermissionsGrantHandler(): void { }; schemaAndProtocolPathGrant.message.authorization = await Message.signAsAuthorization( schemaAndProtocolPathGrant.message.descriptor, - Jws.createSignatureInput(alice) + Jws.createSigner(alice) ); const schemaAndProtocolPathGrantReply = await dwn.processMessage(alice.did, schemaAndProtocolGrant.message); expect(schemaAndProtocolPathGrantReply.status.code).to.eq(400); @@ -239,7 +239,7 @@ export function testPermissionsGrantHandler(): void { }; contextIdAndProtocolPathGrant.message.authorization = await Message.signAsAuthorization( contextIdAndProtocolPathGrant.message.descriptor, - Jws.createSignatureInput(alice) + Jws.createSigner(alice) ); const contextIdAndProtocolPathGrantReply = await dwn.processMessage(alice.did, contextIdAndProtocolPathGrant.message); expect(contextIdAndProtocolPathGrantReply.status.code).to.eq(400); diff --git a/tests/handlers/protocols-configure.spec.ts b/tests/handlers/protocols-configure.spec.ts index 81f36b236..2fe0da175 100644 --- a/tests/handlers/protocols-configure.spec.ts +++ b/tests/handlers/protocols-configure.spec.ts @@ -13,7 +13,7 @@ import dexProtocolDefinition from '../vectors/protocol-definitions/dex.json' ass import minimalProtocolDefinition from '../vectors/protocol-definitions/minimal.json' assert { type: 'json' }; import { DidKeyResolver } from '../../src/did/did-key-resolver.js'; -import { GeneralJwsSigner } from '../../src/jose/jws/general/signer.js'; +import { GeneralJwsBuilder } from '../../src/jose/jws/general/builder.js'; import { lexicographicalCompare } from '../../src/utils/string.js'; import { Message } from '../../src/core/message.js'; import { minimalSleep } from '../../src/utils/time.js'; @@ -80,13 +80,13 @@ export function testProtocolsConfigureHandler(): void { // intentionally create more than one signature, which is not allowed const extraRandomPersona = await TestDataGenerator.generatePersona(); - const signatureInput1 = Jws.createSignatureInput(author); - const signatureInput2 = Jws.createSignatureInput(extraRandomPersona); + const signatureInput1 = Jws.createSigner(author); + const signatureInput2 = Jws.createSigner(extraRandomPersona); const authorizationPayloadBytes = Encoder.objectToBytes(protocolsConfigure.authorizationPayload!); - const signer = await GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput1, signatureInput2]); - message.authorization = signer.getJws(); + const jwsBuilder = await GeneralJwsBuilder.create(authorizationPayloadBytes, [signatureInput1, signatureInput2]); + message.authorization = jwsBuilder.getJws(); TestStubGenerator.stubDidResolver(didResolver, [author]); @@ -233,7 +233,7 @@ export function testProtocolsConfigureHandler(): void { // Re-create auth because we altered the descriptor after signing protocolsConfig.message.authorization = await Message.signAsAuthorization( protocolsConfig.message.descriptor, - Jws.createSignatureInput(alice) + Jws.createSigner(alice) ); // Send records write message @@ -257,7 +257,7 @@ export function testProtocolsConfigureHandler(): void { // Re-create auth because we altered the descriptor after signing protocolsConfig.message.authorization = await Message.signAsAuthorization( protocolsConfig.message.descriptor, - Jws.createSignatureInput(alice) + Jws.createSigner(alice) ); // Send records write message diff --git a/tests/handlers/protocols-query.spec.ts b/tests/handlers/protocols-query.spec.ts index 064d78d49..2840a2477 100644 --- a/tests/handlers/protocols-query.spec.ts +++ b/tests/handlers/protocols-query.spec.ts @@ -10,7 +10,7 @@ import sinon from 'sinon'; import chai, { expect } from 'chai'; import { DidKeyResolver } from '../../src/did/did-key-resolver.js'; -import { GeneralJwsSigner } from '../../src/jose/jws/general/signer.js'; +import { GeneralJwsBuilder } from '../../src/jose/jws/general/builder.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; import { TestStores } from '../test-stores.js'; import { TestStubGenerator } from '../utils/test-stub-generator.js'; @@ -151,7 +151,7 @@ export function testProtocolsQueryHandler(): void { // Re-create auth because we altered the descriptor after signing protocolsQuery.message.authorization = await Message.signAsAuthorization( protocolsQuery.message.descriptor, - Jws.createSignatureInput(alice) + Jws.createSigner(alice) ); // Send records write message @@ -169,9 +169,9 @@ export function testProtocolsQueryHandler(): void { const authorizationPayload = { ...protocolsQuery.authorizationPayload }; authorizationPayload.descriptorCid = incorrectDescriptorCid; const authorizationPayloadBytes = Encoder.objectToBytes(authorizationPayload); - const signatureInput = Jws.createSignatureInput(author); - const signer = await GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput]); - message.authorization = signer.getJws(); + const signatureInput = Jws.createSigner(author); + const jwsBuilder = await GeneralJwsBuilder.create(authorizationPayloadBytes, [signatureInput]); + message.authorization = jwsBuilder.getJws(); const reply = await dwn.processMessage(tenant, message); diff --git a/tests/handlers/records-delete.spec.ts b/tests/handlers/records-delete.spec.ts index badf9be22..fade57a4b 100644 --- a/tests/handlers/records-delete.spec.ts +++ b/tests/handlers/records-delete.spec.ts @@ -77,8 +77,8 @@ export function testRecordsDeleteHandler(): void { // testing delete const recordsDelete = await RecordsDelete.create({ - recordId : message.recordId, - authorizationSignatureInput : Jws.createSignatureInput(alice) + recordId : message.recordId, + authorizationSigner : Jws.createSigner(alice) }); const deleteReply = await dwn.processMessage(alice.did, recordsDelete.message); @@ -91,8 +91,8 @@ export function testRecordsDeleteHandler(): void { // testing deleting a deleted record const recordsDelete2 = await RecordsDelete.create({ - recordId : message.recordId, - authorizationSignatureInput : Jws.createSignatureInput(alice) + recordId : message.recordId, + authorizationSigner : Jws.createSigner(alice) }); const recordsDelete2Reply = await dwn.processMessage(alice.did, recordsDelete2.message); @@ -137,7 +137,7 @@ export function testRecordsDeleteHandler(): void { filter: { recordId: aliceAssociateData.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); const aliceRead1Reply = await dwn.handleRecordsRead(alice.did, aliceRead1.message); @@ -163,7 +163,7 @@ export function testRecordsDeleteHandler(): void { filter: { recordId: bobAssociateData.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob) + authorizationSigner: Jws.createSigner(bob) }); const bobRead1Reply = await dwn.handleRecordsRead(bob.did, bobRead1.message); @@ -178,8 +178,8 @@ export function testRecordsDeleteHandler(): void { // testing deleting a non-existent record const recordsDelete = await RecordsDelete.create({ - recordId : 'nonExistentRecordId', - authorizationSignatureInput : Jws.createSignatureInput(alice) + recordId : 'nonExistentRecordId', + authorizationSigner : Jws.createSigner(alice) }); const deleteReply = await dwn.processMessage(alice.did, recordsDelete.message); @@ -197,8 +197,8 @@ export function testRecordsDeleteHandler(): void { // generate subsequent write and delete with the delete having an earlier timestamp // NOTE: creating RecordsDelete first ensures it has an earlier `messageTimestamp` time const recordsDelete = await RecordsDelete.create({ - recordId : initialWriteData.message.recordId, - authorizationSignatureInput : Jws.createSignatureInput(alice) + recordId : initialWriteData.message.recordId, + authorizationSigner : Jws.createSigner(alice) }); await minimalSleep(); const subsequentWriteData = await TestDataGenerator.generateFromRecordsWrite({ @@ -291,8 +291,8 @@ export function testRecordsDeleteHandler(): void { expect(writeReply.status.code).to.equal(202); const recordsDelete = await RecordsDelete.create({ - recordId : message.recordId, - authorizationSignatureInput : Jws.createSignatureInput(alice) + recordId : message.recordId, + authorizationSigner : Jws.createSigner(alice) }); const deleteReply = await dwn.processMessage(alice.did, recordsDelete.message); @@ -322,15 +322,15 @@ export function testRecordsDeleteHandler(): void { const newWrite = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : recordsWrite.message, published : true, - authorizationSignatureInput : Jws.createSignatureInput(author) + authorizationSigner : Jws.createSigner(author) }); const newWriteReply = await dwn.processMessage(author.did, newWrite.message); expect(newWriteReply.status.code).to.equal(202); const recordsDelete = await RecordsDelete.create({ - recordId : message.recordId, - authorizationSignatureInput : Jws.createSignatureInput(author) + recordId : message.recordId, + authorizationSigner : Jws.createSigner(author) }); const deleteReply = await dwn.processMessage(author.did, recordsDelete.message); diff --git a/tests/handlers/records-query.spec.ts b/tests/handlers/records-query.spec.ts index dbee373e2..eca250ab5 100644 --- a/tests/handlers/records-query.spec.ts +++ b/tests/handlers/records-query.spec.ts @@ -918,7 +918,7 @@ export function testRecordsQueryHandler(): void { // Re-create auth because we altered the descriptor after signing recordsQuery.message.authorization = await Message.signAsAuthorization( recordsQuery.message.descriptor, - Jws.createSignatureInput(alice) + Jws.createSigner(alice) ); // Send records write message @@ -942,7 +942,7 @@ export function testRecordsQueryHandler(): void { // Re-create auth because we altered the descriptor after signing recordsQuery.message.authorization = await Message.signAsAuthorization( recordsQuery.message.descriptor, - Jws.createSignatureInput(alice) + Jws.createSigner(alice) ); // Send records write message diff --git a/tests/handlers/records-read.spec.ts b/tests/handlers/records-read.spec.ts index c04699bdc..36b7e0457 100644 --- a/tests/handlers/records-read.spec.ts +++ b/tests/handlers/records-read.spec.ts @@ -81,7 +81,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); const readReply = await dwn.handleRecordsRead(alice.did, recordsRead.message); @@ -108,7 +108,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob) + authorizationSigner: Jws.createSigner(bob) }); const readReply = await dwn.processMessage(alice.did, recordsRead.message); @@ -153,7 +153,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob) + authorizationSigner: Jws.createSigner(bob) }); const readReply = await dwn.handleRecordsRead(alice.did, recordsRead.message); @@ -180,7 +180,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob) + authorizationSigner: Jws.createSigner(bob) }); const readReply = await dwn.handleRecordsRead(alice.did, recordsRead.message); @@ -228,7 +228,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: imageRecordsWrite.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob) + authorizationSigner: Jws.createSigner(bob) }); const imageReadReply = await dwn.processMessage(alice.did, imageRecordsRead.message); expect(imageReadReply.status.code).to.equal(200); @@ -312,7 +312,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: emailRecordsWrite.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob) + authorizationSigner: Jws.createSigner(bob) }); const bobReadReply = await dwn.processMessage(alice.did, bobRecordsRead.message); expect(bobReadReply.status.code).to.equal(200); @@ -322,7 +322,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: emailRecordsWrite.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(imposterBob) + authorizationSigner: Jws.createSigner(imposterBob) }); const imposterReadReply = await dwn.processMessage(alice.did, imposterRecordsRead.message); expect(imposterReadReply.status.code).to.equal(401); @@ -367,7 +367,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: emailRecordsWrite.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob) + authorizationSigner: Jws.createSigner(bob) }); const bobReadReply = await dwn.processMessage(alice.did, bobRecordsRead.message); expect(bobReadReply.status.code).to.equal(200); @@ -377,7 +377,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: emailRecordsWrite.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(imposterBob) + authorizationSigner: Jws.createSigner(imposterBob) }); const imposterReadReply = await dwn.processMessage(alice.did, imposterRecordsRead.message); expect(imposterReadReply.status.code).to.equal(401); @@ -414,7 +414,7 @@ export function testRecordsReadHandler(): void { protocol : protocolDefinition.protocol, protocolPath : 'foo', }, - authorizationSignatureInput: Jws.createSignatureInput(alice), + authorizationSigner: Jws.createSigner(alice), }); const fooPathReply = await dwn.handleRecordsRead(alice.did, fooPathRead.message); @@ -463,7 +463,7 @@ export function testRecordsReadHandler(): void { protocol : protocolDefinition.protocol, protocolPath : 'foo', }, - authorizationSignatureInput: Jws.createSignatureInput(alice), + authorizationSigner: Jws.createSigner(alice), }); const fooPathReply = await dwn.handleRecordsRead(alice.did, fooPathRead.message); expect(fooPathReply.status.code).to.equal(400); @@ -505,8 +505,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const recordsReadReply = await dwn.processMessage(alice.did, recordsRead.message); expect(recordsReadReply.status.code).to.equal(401); @@ -547,8 +547,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const readReply = await dwn.processMessage(alice.did, recordsRead.message); expect(readReply.status.code).to.equal(200); @@ -601,7 +601,7 @@ export function testRecordsReadHandler(): void { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob), + authorizationSigner: Jws.createSigner(bob), }); const recordsReadWithoutGrantReply = await dwn.processMessage(alice.did, recordsReadWithoutGrant.message); expect(recordsReadWithoutGrantReply.status.code).to.equal(401); @@ -612,8 +612,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const recordsReadWithGrantReply = await dwn.processMessage(alice.did, recordsReadWithGrant.message); expect(recordsReadWithGrantReply.status.code).to.equal(200); @@ -665,7 +665,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob), + authorizationSigner: Jws.createSigner(bob), }); const recordsReadWithoutGrantReply = await dwn.processMessage(alice.did, recordsReadWithoutGrant.message); expect(recordsReadWithoutGrantReply.status.code).to.equal(401); @@ -676,8 +676,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const recordsReadWithGrantReply = await dwn.processMessage(alice.did, recordsReadWithGrant.message); expect(recordsReadWithGrantReply.status.code).to.equal(200); @@ -729,8 +729,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const recordsReadWithoutGrantReply = await dwn.processMessage(alice.did, recordsReadWithoutGrant.message); expect(recordsReadWithoutGrantReply.status.code).to.equal(401); @@ -783,8 +783,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const recordsReadWithoutGrantReply = await dwn.processMessage(alice.did, recordsReadWithoutGrant.message); expect(recordsReadWithoutGrantReply.status.code).to.equal(401); @@ -837,8 +837,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const recordsReadWithoutGrantReply = await dwn.processMessage(alice.did, recordsReadWithoutGrant.message); expect(recordsReadWithoutGrantReply.status.code).to.equal(200); @@ -890,8 +890,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const recordsReadWithoutGrantReply = await dwn.processMessage(alice.did, recordsReadWithoutGrant.message); expect(recordsReadWithoutGrantReply.status.code).to.equal(401); @@ -944,8 +944,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const recordsReadWithoutGrantReply = await dwn.processMessage(alice.did, recordsReadWithoutGrant.message); expect(recordsReadWithoutGrantReply.status.code).to.equal(200); @@ -997,8 +997,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const recordsReadWithoutGrantReply = await dwn.processMessage(alice.did, recordsReadWithoutGrant.message); expect(recordsReadWithoutGrantReply.status.code).to.equal(401); @@ -1042,8 +1042,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const readReply = await dwn.processMessage(alice.did, recordsRead.message); expect(readReply.status.code).to.equal(200); @@ -1086,8 +1086,8 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput : Jws.createSignatureInput(bob), - permissionsGrantId : await Message.getCid(permissionsGrant.message), + authorizationSigner : Jws.createSigner(bob), + permissionsGrantId : await Message.getCid(permissionsGrant.message), }); const readReply = await dwn.processMessage(alice.did, recordsRead.message); expect(readReply.status.code).to.equal(401); @@ -1103,7 +1103,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: `non-existent-record-id`, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); const readReply = await dwn.processMessage(alice.did, recordsRead.message); @@ -1130,8 +1130,8 @@ export function testRecordsReadHandler(): void { // RecordsDelete const recordsDelete = await RecordsDelete.create({ - recordId : message.recordId, - authorizationSignatureInput : Jws.createSignatureInput(alice) + recordId : message.recordId, + authorizationSigner : Jws.createSigner(alice) }); const deleteReply = await dwn.processMessage(alice.did, recordsDelete.message); @@ -1142,7 +1142,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); const readReply = await dwn.processMessage(alice.did, recordsRead.message); @@ -1167,7 +1167,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); const readReply = await dwn.processMessage(alice.did, recordsRead.message); @@ -1191,7 +1191,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); const dataStoreGet = sinon.spy(dataStore, 'get'); @@ -1222,7 +1222,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); const dataStoreGet = sinon.spy(dataStore, 'get'); @@ -1306,7 +1306,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); // test able to derive correct key using `schemas` scheme from root key to decrypt the message @@ -1380,9 +1380,9 @@ export function testRecordsReadHandler(): void { }; const recordsWrite = await RecordsWrite.create({ - authorizationSignatureInput : Jws.createSignatureInput(alice), + authorizationSigner : Jws.createSigner(alice), dataFormat, - data : encryptedDataBytes, + data : encryptedDataBytes, encryptionInput }); @@ -1394,7 +1394,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWrite.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); @@ -1499,7 +1499,7 @@ export function testRecordsReadHandler(): void { encryptionInputForBobsDwn.keyEncryptionInputs[indexOfKeyEncryptionInputToReplace] = protocolPathDerivedKeyEncryptionInputForBobsDwn; await bobToBobRecordsWrite.encryptSymmetricEncryptionKey(encryptionInputForBobsDwn); - await bobToBobRecordsWrite.sign(Jws.createSignatureInput(bob)); + await bobToBobRecordsWrite.sign(Jws.createSigner(bob)); const dataStreamForBobsDwn = DataStream.fromBytes(encryptedDataBytes); const bobToBobWriteReply = await dwn.processMessage(bob.did, bobToBobRecordsWrite.message, dataStreamForBobsDwn); @@ -1512,7 +1512,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); const readReply = await dwn.handleRecordsRead(alice.did, recordsRead.message); expect(readReply.status.code).to.equal(200); @@ -1554,7 +1554,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: recordsWriteToBob.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob) + authorizationSigner: Jws.createSigner(bob) }); const readByBobReply = await dwn.handleRecordsRead(bob.did, recordsReadByBob.message); expect(readByBobReply.status.code).to.equal(200); @@ -1645,7 +1645,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); const readReply = await dwn.handleRecordsRead(alice.did, recordsRead.message); expect(readReply.status.code).to.equal(200); @@ -1713,7 +1713,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: 'any-id', }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); // setting up a stub did resolver & message store @@ -1734,7 +1734,7 @@ export function testRecordsReadHandler(): void { filter: { recordId: 'any-id', }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); // setting up a stub method resolver & message store diff --git a/tests/handlers/records-write.spec.ts b/tests/handlers/records-write.spec.ts index 126e15c05..459f44232 100644 --- a/tests/handlers/records-write.spec.ts +++ b/tests/handlers/records-write.spec.ts @@ -27,7 +27,7 @@ import { DidResolver } from '../../src/did/did-resolver.js'; import { Dwn } from '../../src/dwn.js'; import { DwnErrorCode } from '../../src/core/dwn-error.js'; import { Encoder } from '../../src/utils/encoder.js'; -import { GeneralJwsSigner } from '../../src/jose/jws/general/signer.js'; +import { GeneralJwsBuilder } from '../../src/jose/jws/general/builder.js'; import { getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { Jws } from '../../src/utils/jws.js'; import { PermissionsConditionPublication } from '../../src/types/permissions-types.js'; @@ -291,7 +291,7 @@ export function testRecordsWriteHandler(): void { const write2 = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : message, published : true, - authorizationSignatureInput : Jws.createSignatureInput(author), + authorizationSigner : Jws.createSigner(author), }); const writeUpdateReply = await dwn.processMessage(tenant, write2.message); @@ -325,7 +325,7 @@ export function testRecordsWriteHandler(): void { const write2 = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : message, published : true, - authorizationSignatureInput : Jws.createSignatureInput(author), + authorizationSigner : Jws.createSigner(author), }); const writeUpdateReply = await dwn.processMessage(tenant, write2.message); @@ -358,7 +358,7 @@ export function testRecordsWriteHandler(): void { const write2 = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : message, published : true, - authorizationSignatureInput : Jws.createSignatureInput(author), + authorizationSigner : Jws.createSigner(author), }); const writeUpdateReply = await dwn.processMessage(tenant, write2.message); @@ -389,8 +389,8 @@ export function testRecordsWriteHandler(): void { message.descriptor.dataSize = DwnConstant.maxDataSizeAllowedToBeEncoded + 100; const descriptorCid = await Cid.computeCid(message.descriptor); const recordId = await RecordsWrite.getEntryId(alice.did, message.descriptor); - const authorizationSignatureInput = Jws.createSignatureInput(alice); - const authorization = await RecordsWrite['createAuthorization'](recordId, message.contextId, descriptorCid, message.attestation, message.encryption, authorizationSignatureInput, undefined); + const authorizationSigner = Jws.createSigner(alice); + const authorization = await RecordsWrite['createAuthorization'](recordId, message.contextId, descriptorCid, message.attestation, message.encryption, authorizationSigner, undefined); message.recordId = recordId; message.authorization = authorization; @@ -410,8 +410,8 @@ export function testRecordsWriteHandler(): void { message.descriptor.dataSize = DwnConstant.maxDataSizeAllowedToBeEncoded + 100; const descriptorCid = await Cid.computeCid(message.descriptor); const recordId = await RecordsWrite.getEntryId(alice.did, message.descriptor); - const authorizationSignatureInput = Jws.createSignatureInput(alice); - const authorization = await RecordsWrite['createAuthorization'](recordId, message.contextId, descriptorCid, message.attestation, message.encryption, authorizationSignatureInput, undefined); + const authorizationSigner = Jws.createSigner(alice); + const authorization = await RecordsWrite['createAuthorization'](recordId, message.contextId, descriptorCid, message.attestation, message.encryption, authorizationSigner, undefined); message.recordId = recordId; message.authorization = authorization; @@ -431,8 +431,8 @@ export function testRecordsWriteHandler(): void { message.descriptor.dataSize = 1; const descriptorCid = await Cid.computeCid(message.descriptor); const recordId = await RecordsWrite.getEntryId(alice.did, message.descriptor); - const authorizationSignatureInput = Jws.createSignatureInput(alice); - const authorization = await RecordsWrite['createAuthorization'](recordId, message.contextId, descriptorCid, message.attestation, message.encryption, authorizationSignatureInput, undefined); + const authorizationSigner = Jws.createSigner(alice); + const authorization = await RecordsWrite['createAuthorization'](recordId, message.contextId, descriptorCid, message.attestation, message.encryption, authorizationSigner, undefined); message.recordId = recordId; message.authorization = authorization; @@ -451,8 +451,8 @@ export function testRecordsWriteHandler(): void { message.descriptor.dataSize = 1; const descriptorCid = await Cid.computeCid(message.descriptor); const recordId = await RecordsWrite.getEntryId(alice.did, message.descriptor); - const authorizationSignatureInput = Jws.createSignatureInput(alice); - const authorization = await RecordsWrite['createAuthorization'](recordId, message.contextId, descriptorCid, message.attestation, message.encryption, authorizationSignatureInput, undefined); + const authorizationSigner = Jws.createSigner(alice); + const authorization = await RecordsWrite['createAuthorization'](recordId, message.contextId, descriptorCid, message.attestation, message.encryption, authorizationSigner, undefined); message.recordId = recordId; message.authorization = authorization; @@ -475,15 +475,15 @@ export function testRecordsWriteHandler(): void { expect(initialWriteReply.status.code).to.equal(202); const recordsDelete = await RecordsDelete.create({ - recordId : message.recordId, - authorizationSignatureInput : Jws.createSignatureInput(author), + recordId : message.recordId, + authorizationSigner : Jws.createSigner(author), }); const deleteReply = await dwn.processMessage(tenant, recordsDelete.message); expect(deleteReply.status.code).to.equal(202); const write = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : message, - authorizationSignatureInput : Jws.createSignatureInput(author), + authorizationSigner : Jws.createSigner(author), }); const withoutDataReply = await dwn.processMessage(tenant, write.message); @@ -507,15 +507,15 @@ export function testRecordsWriteHandler(): void { expect(initialWriteReply.status.code).to.equal(202); const recordsDelete = await RecordsDelete.create({ - recordId : message.recordId, - authorizationSignatureInput : Jws.createSignatureInput(author), + recordId : message.recordId, + authorizationSigner : Jws.createSigner(author), }); const deleteReply = await dwn.processMessage(tenant, recordsDelete.message); expect(deleteReply.status.code).to.equal(202); const write = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : message, - authorizationSignatureInput : Jws.createSignatureInput(author), + authorizationSigner : Jws.createSigner(author), }); const withoutDataReply = await dwn.processMessage(tenant, write.message); @@ -642,7 +642,7 @@ export function testRecordsWriteHandler(): void { filter: { recordId: write2.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(alice) + authorizationSigner: Jws.createSigner(alice) }); const readReply = await dwn.handleRecordsRead(alice.did, read.message); @@ -675,7 +675,7 @@ export function testRecordsWriteHandler(): void { const newWrite = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : recordsWrite.message, published : true, - authorizationSignatureInput : Jws.createSignatureInput(author) + authorizationSigner : Jws.createSigner(author) }); const newWriteReply = await dwn.processMessage(tenant, newWrite.message); @@ -712,7 +712,7 @@ export function testRecordsWriteHandler(): void { const newWrite = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : recordsWrite.message, data : newData, - authorizationSignatureInput : Jws.createSignatureInput(author) + authorizationSigner : Jws.createSigner(author) }); const newWriteReply = await dwn.processMessage(tenant, newWrite.message, DataStream.fromBytes(newData)); @@ -804,7 +804,7 @@ export function testRecordsWriteHandler(): void { const newWrite = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : recordsWrite.message, published : true, - authorizationSignatureInput : Jws.createSignatureInput(author) + authorizationSigner : Jws.createSigner(author) }); const newWriteReply = await dwn.processMessage(author.did, newWrite.message); @@ -813,7 +813,7 @@ export function testRecordsWriteHandler(): void { const newestWrite = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : recordsWrite.message, published : true, - authorizationSignatureInput : Jws.createSignatureInput(author) + authorizationSigner : Jws.createSigner(author) }); const newestWriteReply = await dwn.processMessage(author.did, newestWrite.message); @@ -1786,7 +1786,7 @@ export function testRecordsWriteHandler(): void { descriptorCid, attestation, recordsWrite.message.encryption, - Jws.createSignatureInput(alice), + Jws.createSigner(alice), undefined ); recordsWrite.message = { @@ -1851,7 +1851,7 @@ export function testRecordsWriteHandler(): void { filter: { recordId: imageRecordsWrite.message.recordId, }, - authorizationSignatureInput: Jws.createSignatureInput(bob) + authorizationSigner: Jws.createSigner(bob) }); const bobRecordsReadReply = await dwn.handleRecordsRead(alice.did, bobRecordsReadData.message); @@ -2620,7 +2620,7 @@ export function testRecordsWriteHandler(): void { const newWrite = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : message, published : true, - authorizationSignatureInput : Jws.createSignatureInput(alice), + authorizationSigner : Jws.createSigner(alice), data : updatedDataBytes, }); @@ -2646,9 +2646,9 @@ export function testRecordsWriteHandler(): void { const authorizationPayload = { ...recordsWrite.authorizationPayload }; authorizationPayload.recordId = await TestDataGenerator.randomCborSha256Cid(); // make recordId mismatch in authorization payload const authorizationPayloadBytes = Encoder.objectToBytes(authorizationPayload); - const signatureInput = Jws.createSignatureInput(author); - const signer = await GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput]); - message.authorization = signer.getJws(); + const signatureInput = Jws.createSigner(author); + const jwsBuilder = await GeneralJwsBuilder.create(authorizationPayloadBytes, [signatureInput]); + message.authorization = jwsBuilder.getJws(); const tenant = author.did; const didResolver = TestStubGenerator.createDidResolverStub(author); @@ -2670,9 +2670,9 @@ export function testRecordsWriteHandler(): void { const authorizationPayload = { ...recordsWrite.authorizationPayload }; authorizationPayload.contextId = await TestDataGenerator.randomCborSha256Cid(); // make contextId mismatch in authorization payload const authorizationPayloadBytes = Encoder.objectToBytes(authorizationPayload); - const signatureInput = Jws.createSignatureInput(author); - const signer = await GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput]); - message.authorization = signer.getJws(); + const signatureInput = Jws.createSigner(author); + const jwsBuilder = await GeneralJwsBuilder.create(authorizationPayloadBytes, [signatureInput]); + message.authorization = jwsBuilder.getJws(); const tenant = author.did; const didResolver = sinon.createStubInstance(DidResolver); @@ -2725,21 +2725,21 @@ export function testRecordsWriteHandler(): void { it('should fail with 400 if `attestation` payload contains properties other than `descriptorCid`', async () => { const { author, message, recordsWrite, dataStream } = await TestDataGenerator.generateRecordsWrite(); const tenant = author.did; - const signatureInput = Jws.createSignatureInput(author); + const signatureInput = Jws.createSigner(author); // replace `attestation` with one that has an additional property, but go the extra mile of making sure signature is valid const descriptorCid = recordsWrite.authorizationPayload!.descriptorCid; const attestationPayload = { descriptorCid, someAdditionalProperty: 'anyValue' }; // additional property is not allowed const attestationPayloadBytes = Encoder.objectToBytes(attestationPayload); - const attestationSigner = await GeneralJwsSigner.create(attestationPayloadBytes, [signatureInput]); - message.attestation = attestationSigner.getJws(); + const attestationBuilder = await GeneralJwsBuilder.create(attestationPayloadBytes, [signatureInput]); + message.attestation = attestationBuilder.getJws(); // recreate the `authorization` based on the new` attestationCid` const authorizationPayload = { ...recordsWrite.authorizationPayload }; authorizationPayload.attestationCid = await Cid.computeCid(attestationPayload); const authorizationPayloadBytes = Encoder.objectToBytes(authorizationPayload); - const authorizationSigner = await GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput]); - message.authorization = authorizationSigner.getJws(); + const authorizationBuilder = await GeneralJwsBuilder.create(authorizationPayloadBytes, [signatureInput]); + message.authorization = authorizationBuilder.getJws(); const didResolver = TestStubGenerator.createDidResolverStub(author); const messageStore = stubInterface(); @@ -2786,7 +2786,7 @@ export function testRecordsWriteHandler(): void { // replace valid attestation (the one signed by `authorization` with another attestation to the same message (descriptorCid) const bob = await DidKeyResolver.generate(); const descriptorCid = await Cid.computeCid(message.descriptor); - const attestationNotReferencedByAuthorization = await RecordsWrite['createAttestation'](descriptorCid, Jws.createSignatureInputs([bob])); + const attestationNotReferencedByAuthorization = await RecordsWrite['createAttestation'](descriptorCid, Jws.createSigners([bob])); message.attestation = attestationNotReferencedByAuthorization; const recordsWriteHandler = new RecordsWriteHandler(didResolver, messageStore, dataStore, eventLog); diff --git a/tests/interfaces/events-get.spec.ts b/tests/interfaces/events-get.spec.ts index 4b600813a..4925c6a75 100644 --- a/tests/interfaces/events-get.spec.ts +++ b/tests/interfaces/events-get.spec.ts @@ -10,8 +10,8 @@ describe('EventsGet Message', () => { it('creates an EventsGet message', async () => { const alice = await TestDataGenerator.generatePersona(); const eventsGet = await EventsGet.create({ - watermark : 'yolo', - authorizationSignatureInput : await Jws.createSignatureInput(alice) + watermark : 'yolo', + authorizationSigner : await Jws.createSigner(alice) }); const { message } = eventsGet; @@ -23,7 +23,7 @@ describe('EventsGet Message', () => { it('doesnt require a watermark', async () => { const alice = await TestDataGenerator.generatePersona(); const eventsGet = await EventsGet.create({ - authorizationSignatureInput: await Jws.createSignatureInput(alice) + authorizationSigner: await Jws.createSigner(alice) }); const message = eventsGet.message; @@ -37,8 +37,8 @@ describe('EventsGet Message', () => { it('parses a message into an EventsGet instance', async () => { const alice = await TestDataGenerator.generatePersona(); const eventsGet = await EventsGet.create({ - watermark : 'yolo', - authorizationSignatureInput : await Jws.createSignatureInput(alice) + watermark : 'yolo', + authorizationSigner : await Jws.createSigner(alice) }); const parsed = await EventsGet.parse(eventsGet.message); @@ -53,8 +53,8 @@ describe('EventsGet Message', () => { it('throws an exception if message is not a valid EventsGet message', async () => { const alice = await TestDataGenerator.generatePersona(); const eventsGet = await EventsGet.create({ - watermark : 'yolo', - authorizationSignatureInput : await Jws.createSignatureInput(alice) + watermark : 'yolo', + authorizationSigner : await Jws.createSigner(alice) }); const { message } = eventsGet; diff --git a/tests/interfaces/messages-get.spec.ts b/tests/interfaces/messages-get.spec.ts index 36a051b69..718894329 100644 --- a/tests/interfaces/messages-get.spec.ts +++ b/tests/interfaces/messages-get.spec.ts @@ -13,8 +13,8 @@ describe('MessagesGet Message', () => { const messageCid = await Message.getCid(message); const messagesGet = await MessagesGet.create({ - authorizationSignatureInput : await Jws.createSignatureInput(author), - messageCids : [messageCid] + authorizationSigner : await Jws.createSigner(author), + messageCids : [messageCid] }); expect(messagesGet.message.authorization).to.exist; @@ -29,8 +29,8 @@ describe('MessagesGet Message', () => { try { await MessagesGet.create({ - authorizationSignatureInput : await Jws.createSignatureInput(alice), - messageCids : [] + authorizationSigner : await Jws.createSigner(alice), + messageCids : [] }); expect.fail(); @@ -45,8 +45,8 @@ describe('MessagesGet Message', () => { try { await MessagesGet.create({ - authorizationSignatureInput : await Jws.createSignatureInput(alice), - messageCids : ['abcd'] + authorizationSigner : await Jws.createSigner(alice), + messageCids : ['abcd'] }); expect.fail(); @@ -62,8 +62,8 @@ describe('MessagesGet Message', () => { let messageCid = await Message.getCid(message); const messagesGet = await MessagesGet.create({ - authorizationSignatureInput : await Jws.createSignatureInput(author), - messageCids : [messageCid] + authorizationSigner : await Jws.createSigner(author), + messageCids : [messageCid] }); const parsed = await MessagesGet.parse(messagesGet.message); @@ -80,8 +80,8 @@ describe('MessagesGet Message', () => { const messageCid = await Message.getCid(recordsWriteMessage); const messagesGet = await MessagesGet.create({ - authorizationSignatureInput : await Jws.createSignatureInput(author), - messageCids : [messageCid] + authorizationSigner : await Jws.createSigner(author), + messageCids : [messageCid] }); const message = messagesGet.toJSON() as MessagesGetMessage; diff --git a/tests/interfaces/permissions-grant.spec.ts b/tests/interfaces/permissions-grant.spec.ts index 73de8429e..33f783d41 100644 --- a/tests/interfaces/permissions-grant.spec.ts +++ b/tests/interfaces/permissions-grant.spec.ts @@ -9,20 +9,14 @@ import { PermissionsGrant } from '../../src/interfaces/permissions-grant.js'; import { Secp256k1 } from '../../src/utils/secp256k1.js'; import { Temporal } from '@js-temporal/polyfill'; import { TestDataGenerator } from '../utils/test-data-generator.js'; -import { DidKeyResolver, DwnErrorCode } from '../../src/index.js'; +import { DidKeyResolver, DwnErrorCode, PrivateKeySigner } from '../../src/index.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../../src/core/message.js'; describe('PermissionsGrant', () => { describe('create()', async () => { it('creates a PermissionsGrant message', async () => { const { privateJwk } = await Secp256k1.generateKeyPair(); - const authorizationSignatureInput = { - privateJwk, - protectedHeader: { - alg : privateJwk.alg as string, - kid : 'did:jank:bob' - } - }; + const authorizationSigner = new PrivateKeySigner({ privateJwk, keyId: 'did:jank:bob' }); const { message } = await PermissionsGrant.create({ dateExpires : getCurrentTimeInHighPrecision(), @@ -31,7 +25,7 @@ describe('PermissionsGrant', () => { grantedTo : 'did:jank:alice', grantedFor : 'did:jank:bob', scope : { interface: DwnInterfaceName.Records, method: DwnMethodName.Write }, - authorizationSignatureInput + authorizationSigner }); expect(message.descriptor.grantedTo).to.equal('did:jank:alice'); @@ -44,20 +38,14 @@ describe('PermissionsGrant', () => { describe('scope validations', () => { it('ensures that `schema` and protocol related fields `protocol`, `contextId` or `protocolPath`', async () => { const { privateJwk } = await Secp256k1.generateKeyPair(); - const authorizationSignatureInput = { - privateJwk, - protectedHeader: { - alg : privateJwk.alg as string, - kid : 'did:jank:bob' - } - }; + const authorizationSigner = new PrivateKeySigner({ privateJwk, keyId: 'did:jank:bob' }); const permissionsGrantOptions = { dateExpires : getCurrentTimeInHighPrecision(), grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', grantedFor : 'did:jank:bob', - authorizationSignatureInput + authorizationSigner }; // Reject when `schema` and `protocol` are both present @@ -93,20 +81,14 @@ describe('PermissionsGrant', () => { it('ensures that `contextId` and `protocolPath` are not both present', async () => { const { privateJwk } = await Secp256k1.generateKeyPair(); - const authorizationSignatureInput = { - privateJwk, - protectedHeader: { - alg : privateJwk.alg as string, - kid : 'did:jank:bob' - } - }; + const authorizationSigner = new PrivateKeySigner({ privateJwk, keyId: 'did:jank:bob' }); const permissionsGrantOptions = { dateExpires : getCurrentTimeInHighPrecision(), grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', grantedFor : 'did:jank:bob', - authorizationSignatureInput + authorizationSigner }; // Allow when `context to be present ` and `protocol` are both present @@ -129,13 +111,7 @@ describe('PermissionsGrant', () => { const bob = await TestDataGenerator.generatePersona(); const { privateJwk } = await Secp256k1.generateKeyPair(); - const authorizationSignatureInput = { - privateJwk, - protectedHeader: { - alg : privateJwk.alg as string, - kid : alice.did - } - }; + const authorizationSigner = new PrivateKeySigner({ privateJwk, keyId: alice.did }); const { permissionsRequest } = await TestDataGenerator.generatePermissionsRequest({ author : bob, @@ -146,7 +122,7 @@ describe('PermissionsGrant', () => { }); const dateExpires = Temporal.Now.instant().add({ hours: 24 }).toString({ smallestUnit: 'microseconds' }); - const permissionsGrant = await PermissionsGrant.createFromPermissionsRequest(permissionsRequest, authorizationSignatureInput, { dateExpires }); + const permissionsGrant = await PermissionsGrant.createFromPermissionsRequest(permissionsRequest, authorizationSigner, { dateExpires }); expect(permissionsGrant.author).to.eq(alice.did); expect(permissionsGrant.message.descriptor.description).to.eq(permissionsRequest.message.descriptor.description); @@ -158,19 +134,13 @@ describe('PermissionsGrant', () => { expect(permissionsGrant.message.descriptor.permissionsRequestId).to.eq(await Message.getCid(permissionsRequest.message)); }); - it('should create a PermissionsGrant from a PerimssionsRequest and overrides', async () => { + it('should create a PermissionsGrant from a PermissionsRequest and overrides', async () => { const alice = await DidKeyResolver.generate(); const bob = await DidKeyResolver.generate(); const carol = await DidKeyResolver.generate(); const { privateJwk } = await Secp256k1.generateKeyPair(); - const authorizationSignatureInput = { - privateJwk, - protectedHeader: { - alg : privateJwk.alg as string, - kid : alice.did - } - }; + const authorizationSigner = new PrivateKeySigner({ privateJwk, keyId: `${alice.did}#key1` }); const { permissionsRequest } = await TestDataGenerator.generatePermissionsRequest(); @@ -191,7 +161,7 @@ describe('PermissionsGrant', () => { } }; - const permissionsGrant = await PermissionsGrant.createFromPermissionsRequest(permissionsRequest, authorizationSignatureInput, overrides); + const permissionsGrant = await PermissionsGrant.createFromPermissionsRequest(permissionsRequest, authorizationSigner, overrides); expect(permissionsGrant.author).to.eq(alice.did); expect(permissionsGrant.message.descriptor.description).to.eq(description); diff --git a/tests/interfaces/permissions-request.spec.ts b/tests/interfaces/permissions-request.spec.ts index dcd84ee98..56197ac9f 100644 --- a/tests/interfaces/permissions-request.spec.ts +++ b/tests/interfaces/permissions-request.spec.ts @@ -4,7 +4,7 @@ import chaiAsPromised from 'chai-as-promised'; import { expect } from 'chai'; import { PermissionsRequest } from '../../src/interfaces/permissions-request.js'; import { Secp256k1 } from '../../src/utils/secp256k1.js'; -import { DwnInterfaceName, DwnMethodName } from '../../src/index.js'; +import { DwnInterfaceName, DwnMethodName, PrivateKeySigner } from '../../src/index.js'; chai.use(chaiAsPromised); @@ -12,13 +12,7 @@ describe('PermissionsRequest', () => { describe('create', () => { it('creates a PermissionsRequest message', async () => { const { privateJwk } = await Secp256k1.generateKeyPair(); - const authorizationSignatureInput = { - privateJwk, - protectedHeader: { - alg : privateJwk.alg as string, - kid : 'did:jank:bob' - } - }; + const authorizationSigner = new PrivateKeySigner({ privateJwk, keyId: 'did:jank:bob' }); const { message } = await PermissionsRequest.create({ description : 'drugs', @@ -30,7 +24,7 @@ describe('PermissionsRequest', () => { method : DwnMethodName.Write, protocol : 'some-protocol', }, - authorizationSignatureInput + authorizationSigner }); expect(message.descriptor.grantedTo).to.equal('did:jank:alice'); diff --git a/tests/interfaces/protocols-configure.spec.ts b/tests/interfaces/protocols-configure.spec.ts index 2250ae6a2..f0ec1ee0d 100644 --- a/tests/interfaces/protocols-configure.spec.ts +++ b/tests/interfaces/protocols-configure.spec.ts @@ -19,9 +19,9 @@ describe('ProtocolsConfigure', () => { const currentTime = getCurrentTimeInHighPrecision(); const definition = { ...dexProtocolDefinition }; const protocolsConfigure = await ProtocolsConfigure.create({ - messageTimestamp : currentTime, + messageTimestamp : currentTime, definition, - authorizationSignatureInput : Jws.createSignatureInput(alice), + authorizationSigner : Jws.createSigner(alice), }); expect(protocolsConfigure.message.descriptor.messageTimestamp).to.equal(currentTime); @@ -32,10 +32,10 @@ describe('ProtocolsConfigure', () => { const definition = { ...dexProtocolDefinition, protocol: 'example.com/' }; const options = { - recipient : alice.did, - data : TestDataGenerator.randomBytes(10), - dataFormat : 'application/json', - authorizationSignatureInput : Jws.createSignatureInput(alice), + recipient : alice.did, + data : TestDataGenerator.randomBytes(10), + dataFormat : 'application/json', + authorizationSigner : Jws.createSigner(alice), definition, }; const protocolsConfig = await ProtocolsConfigure.create(options); @@ -52,12 +52,12 @@ describe('ProtocolsConfigure', () => { nonnormalizedDexProtocol.types.ask.schema = 'ask'; const options = { - recipient : alice.did, - data : TestDataGenerator.randomBytes(10), - dataFormat : 'application/json', - authorizationSignatureInput : Jws.createSignatureInput(alice), - protocol : 'example.com/', - definition : nonnormalizedDexProtocol + recipient : alice.did, + data : TestDataGenerator.randomBytes(10), + dataFormat : 'application/json', + authorizationSigner : Jws.createSigner(alice), + protocol : 'example.com/', + definition : nonnormalizedDexProtocol }; const protocolsConfig = await ProtocolsConfigure.create(options); diff --git a/tests/interfaces/protocols-query.spec.ts b/tests/interfaces/protocols-query.spec.ts index 3a068e9b2..efba55f6c 100644 --- a/tests/interfaces/protocols-query.spec.ts +++ b/tests/interfaces/protocols-query.spec.ts @@ -18,9 +18,9 @@ describe('ProtocolsQuery', () => { const currentTime = getCurrentTimeInHighPrecision(); const protocolsQuery = await ProtocolsQuery.create({ - filter : { protocol: 'anyValue' }, - messageTimestamp : currentTime, - authorizationSignatureInput : Jws.createSignatureInput(alice), + filter : { protocol: 'anyValue' }, + messageTimestamp : currentTime, + authorizationSigner : Jws.createSigner(alice), }); expect(protocolsQuery.message.descriptor.messageTimestamp).to.equal(currentTime); @@ -31,12 +31,12 @@ describe('ProtocolsQuery', () => { const alice = await TestDataGenerator.generatePersona(); const options = { - recipient : alice.did, - data : TestDataGenerator.randomBytes(10), - dataFormat : 'application/json', - authorizationSignatureInput : Jws.createSignatureInput(alice), - filter : { protocol: 'example.com/' }, - definition : dexProtocolDefinition + recipient : alice.did, + data : TestDataGenerator.randomBytes(10), + dataFormat : 'application/json', + authorizationSigner : Jws.createSigner(alice), + filter : { protocol: 'example.com/' }, + definition : dexProtocolDefinition }; const protocolsConfig = await ProtocolsQuery.create(options); diff --git a/tests/interfaces/records-delete.spec.ts b/tests/interfaces/records-delete.spec.ts index f137a8015..8ae854ad4 100644 --- a/tests/interfaces/records-delete.spec.ts +++ b/tests/interfaces/records-delete.spec.ts @@ -15,9 +15,9 @@ describe('RecordsDelete', () => { const currentTime = getCurrentTimeInHighPrecision(); const recordsDelete = await RecordsDelete.create({ - recordId : 'anything', - authorizationSignatureInput : Jws.createSignatureInput(alice), - messageTimestamp : currentTime + recordId : 'anything', + authorizationSigner : Jws.createSigner(alice), + messageTimestamp : currentTime }); expect(recordsDelete.message.descriptor.messageTimestamp).to.equal(currentTime); @@ -27,8 +27,8 @@ describe('RecordsDelete', () => { const alice = await TestDataGenerator.generatePersona(); const recordsDelete = await RecordsDelete.create({ - recordId : 'anything', - authorizationSignatureInput : Jws.createSignatureInput(alice) + recordId : 'anything', + authorizationSigner : Jws.createSigner(alice) }); expect(recordsDelete.message.descriptor.messageTimestamp).to.exist; diff --git a/tests/interfaces/records-query.spec.ts b/tests/interfaces/records-query.spec.ts index 8e02a6bdf..9c8ee3561 100644 --- a/tests/interfaces/records-query.spec.ts +++ b/tests/interfaces/records-query.spec.ts @@ -18,9 +18,9 @@ describe('RecordsQuery', () => { const currentTime = getCurrentTimeInHighPrecision(); const recordsQuery = await RecordsQuery.create({ - filter : { schema: 'anything' }, - messageTimestamp : currentTime, - authorizationSignatureInput : Jws.createSignatureInput(alice), + filter : { schema: 'anything' }, + messageTimestamp : currentTime, + authorizationSigner : Jws.createSigner(alice), }); expect(recordsQuery.message.descriptor.messageTimestamp).to.equal(currentTime); @@ -30,12 +30,12 @@ describe('RecordsQuery', () => { const alice = await TestDataGenerator.generatePersona(); const options = { - recipient : alice.did, - data : TestDataGenerator.randomBytes(10), - dataFormat : 'application/json', - authorizationSignatureInput : Jws.createSignatureInput(alice), - filter : { protocol: 'example.com/' }, - definition : dexProtocolDefinition + recipient : alice.did, + data : TestDataGenerator.randomBytes(10), + dataFormat : 'application/json', + authorizationSigner : Jws.createSigner(alice), + filter : { protocol: 'example.com/' }, + definition : dexProtocolDefinition }; const recordsQuery = await RecordsQuery.create(options); @@ -48,12 +48,12 @@ describe('RecordsQuery', () => { const alice = await TestDataGenerator.generatePersona(); const options = { - recipient : alice.did, - data : TestDataGenerator.randomBytes(10), - dataFormat : 'application/json', - authorizationSignatureInput : Jws.createSignatureInput(alice), - filter : { schema: 'example.com/' }, - definition : dexProtocolDefinition + recipient : alice.did, + data : TestDataGenerator.randomBytes(10), + dataFormat : 'application/json', + authorizationSigner : Jws.createSigner(alice), + filter : { schema: 'example.com/' }, + definition : dexProtocolDefinition }; const recordsQuery = await RecordsQuery.create(options); diff --git a/tests/interfaces/records-read.spec.ts b/tests/interfaces/records-read.spec.ts index 1f40fc977..100b4c8e7 100644 --- a/tests/interfaces/records-read.spec.ts +++ b/tests/interfaces/records-read.spec.ts @@ -21,8 +21,8 @@ describe('RecordsRead', () => { filter: { recordId: 'anything', }, - authorizationSignatureInput : Jws.createSignatureInput(alice), - date : currentTime + authorizationSigner : Jws.createSigner(alice), + date : currentTime }); expect(recordsRead.message.descriptor.messageTimestamp).to.equal(currentTime); @@ -32,12 +32,12 @@ describe('RecordsRead', () => { const alice = await TestDataGenerator.generatePersona(); const options = { - recipient : alice.did, - data : TestDataGenerator.randomBytes(10), - dataFormat : 'application/json', - authorizationSignatureInput : Jws.createSignatureInput(alice), - filter : { protocol: 'example.com/' }, - definition : dexProtocolDefinition + recipient : alice.did, + data : TestDataGenerator.randomBytes(10), + dataFormat : 'application/json', + authorizationSigner : Jws.createSigner(alice), + filter : { protocol: 'example.com/' }, + definition : dexProtocolDefinition }; const recordsQuery = await RecordsRead.create(options); @@ -50,12 +50,12 @@ describe('RecordsRead', () => { const alice = await TestDataGenerator.generatePersona(); const options = { - recipient : alice.did, - data : TestDataGenerator.randomBytes(10), - dataFormat : 'application/json', - authorizationSignatureInput : Jws.createSignatureInput(alice), - filter : { schema: 'example.com/' }, - definition : dexProtocolDefinition + recipient : alice.did, + data : TestDataGenerator.randomBytes(10), + dataFormat : 'application/json', + authorizationSigner : Jws.createSigner(alice), + filter : { schema: 'example.com/' }, + definition : dexProtocolDefinition }; const recordsQuery = await RecordsRead.create(options); diff --git a/tests/interfaces/records-write.spec.ts b/tests/interfaces/records-write.spec.ts index d8ffbad9c..c60f7c1e2 100644 --- a/tests/interfaces/records-write.spec.ts +++ b/tests/interfaces/records-write.spec.ts @@ -1,5 +1,6 @@ import type { MessageStore } from '../../src/types/message-store.js'; import type { RecordsWriteMessage } from '../../src/types/records-types.js'; +import type { Signer } from '../../src/index.js'; import type { EncryptionInput, RecordsWriteOptions } from '../../src/interfaces/records-write.js'; import chaiAsPromised from 'chai-as-promised'; @@ -10,7 +11,7 @@ import { getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { RecordsWrite } from '../../src/interfaces/records-write.js'; import { stubInterface } from 'ts-sinon'; import { TestDataGenerator } from '../utils/test-data-generator.js'; -import { Jws, KeyDerivationScheme } from '../../src/index.js'; +import { Encoder, Jws, KeyDerivationScheme } from '../../src/index.js'; chai.use(chaiAsPromised); @@ -22,11 +23,11 @@ describe('RecordsWrite', () => { const alice = await TestDataGenerator.generatePersona(); const options = { - data : TestDataGenerator.randomBytes(10), - dataFormat : 'application/json', - dateCreated : '2022-10-14T10:20:30.405060Z', - recordId : await TestDataGenerator.randomCborSha256Cid(), - authorizationSignatureInput : Jws.createSignatureInput(alice) + data : TestDataGenerator.randomBytes(10), + dataFormat : 'application/json', + dateCreated : '2022-10-14T10:20:30.405060Z', + recordId : await TestDataGenerator.randomCborSha256Cid(), + authorizationSigner : Jws.createSigner(alice) }; const recordsWrite = await RecordsWrite.create(options); @@ -46,11 +47,11 @@ describe('RecordsWrite', () => { const alice = await TestDataGenerator.generatePersona(); const options = { - data : TestDataGenerator.randomBytes(10), - dataFormat : 'application/json', - recordId : await TestDataGenerator.randomCborSha256Cid(), - published : true, - authorizationSignatureInput : Jws.createSignatureInput(alice) + data : TestDataGenerator.randomBytes(10), + dataFormat : 'application/json', + recordId : await TestDataGenerator.randomCborSha256Cid(), + published : true, + authorizationSigner : Jws.createSigner(alice) }; const recordsWrite = await RecordsWrite.create(options); @@ -64,13 +65,13 @@ describe('RecordsWrite', () => { // testing `data` and `dataCid` both defined const options1 = { - recipient : alice.did, - data : TestDataGenerator.randomBytes(10), - dataCid : await TestDataGenerator.randomCborSha256Cid(), - dataFormat : 'application/json', - recordId : await TestDataGenerator.randomCborSha256Cid(), - published : true, - authorizationSignatureInput : Jws.createSignatureInput(alice) + recipient : alice.did, + data : TestDataGenerator.randomBytes(10), + dataCid : await TestDataGenerator.randomCborSha256Cid(), + dataFormat : 'application/json', + recordId : await TestDataGenerator.randomCborSha256Cid(), + published : true, + authorizationSigner : Jws.createSigner(alice) }; const createPromise1 = RecordsWrite.create(options1); @@ -78,14 +79,14 @@ describe('RecordsWrite', () => { // testing `data` and `dataCid` both undefined const options2 = { - recipient : alice.did, + recipient : alice.did, // intentionally showing both `data` and `dataCid` are undefined // data : TestDataGenerator.randomBytes(10), // dataCid : await TestDataGenerator.randomCborSha256Cid(), - dataFormat : 'application/json', - recordId : await TestDataGenerator.randomCborSha256Cid(), - published : true, - authorizationSignatureInput : Jws.createSignatureInput(alice) + dataFormat : 'application/json', + recordId : await TestDataGenerator.randomCborSha256Cid(), + published : true, + authorizationSigner : Jws.createSigner(alice) }; const createPromise2 = RecordsWrite.create(options2); @@ -96,27 +97,27 @@ describe('RecordsWrite', () => { const alice = await TestDataGenerator.generatePersona(); const options1 = { - recipient : alice.did, - dataCid : await TestDataGenerator.randomCborSha256Cid(), + recipient : alice.did, + dataCid : await TestDataGenerator.randomCborSha256Cid(), // dataSize : 123, // intentionally missing - dataFormat : 'application/json', - recordId : await TestDataGenerator.randomCborSha256Cid(), - published : true, - authorizationSignatureInput : Jws.createSignatureInput(alice) + dataFormat : 'application/json', + recordId : await TestDataGenerator.randomCborSha256Cid(), + published : true, + authorizationSigner : Jws.createSigner(alice) }; const createPromise1 = RecordsWrite.create(options1); await expect(createPromise1).to.be.rejectedWith('`dataCid` and `dataSize` must both be defined or undefined at the same time'); const options2 = { - recipient : alice.did, - data : TestDataGenerator.randomBytes(10), + recipient : alice.did, + data : TestDataGenerator.randomBytes(10), // dataCid : await TestDataGenerator.randomCborSha256Cid(), // intentionally missing - dataSize : 123, - dataFormat : 'application/json', - recordId : await TestDataGenerator.randomCborSha256Cid(), - published : true, - authorizationSignatureInput : Jws.createSignatureInput(alice) + dataSize : 123, + dataFormat : 'application/json', + recordId : await TestDataGenerator.randomCborSha256Cid(), + published : true, + authorizationSigner : Jws.createSigner(alice) }; const createPromise2 = RecordsWrite.create(options2); @@ -127,13 +128,13 @@ describe('RecordsWrite', () => { const alice = await TestDataGenerator.generatePersona(); const options = { - recipient : alice.did, - data : TestDataGenerator.randomBytes(10), - dataFormat : 'application/json', - authorizationSignatureInput : Jws.createSignatureInput(alice), - protocol : 'example.com/', - protocolPath : 'example', - schema : 'http://foo.bar/schema' + recipient : alice.did, + data : TestDataGenerator.randomBytes(10), + dataFormat : 'application/json', + authorizationSigner : Jws.createSigner(alice), + protocol : 'example.com/', + protocolPath : 'example', + schema : 'http://foo.bar/schema' }; const recordsWrite = await RecordsWrite.create(options); @@ -146,29 +147,29 @@ describe('RecordsWrite', () => { const alice = await TestDataGenerator.generatePersona(); const options1 = { - recipient : alice.did, - protocol : 'http://example.com', + recipient : alice.did, + protocol : 'http://example.com', // protocolPath : 'foo/bar', // intentionally missing - dataCid : await TestDataGenerator.randomCborSha256Cid(), - dataSize : 123, - dataFormat : 'application/json', - recordId : await TestDataGenerator.randomCborSha256Cid(), - authorizationSignatureInput : Jws.createSignatureInput(alice) + dataCid : await TestDataGenerator.randomCborSha256Cid(), + dataSize : 123, + dataFormat : 'application/json', + recordId : await TestDataGenerator.randomCborSha256Cid(), + authorizationSigner : Jws.createSigner(alice) }; const createPromise1 = RecordsWrite.create(options1); await expect(createPromise1).to.be.rejectedWith('`protocol` and `protocolPath` must both be defined or undefined at the same time'); const options2 = { - recipient : alice.did, + recipient : alice.did, // protocol : 'http://example.com', // intentionally missing - protocolPath : 'foo/bar', - data : TestDataGenerator.randomBytes(10), - dataCid : await TestDataGenerator.randomCborSha256Cid(), - dataSize : 123, - dataFormat : 'application/json', - recordId : await TestDataGenerator.randomCborSha256Cid(), - authorizationSignatureInput : Jws.createSignatureInput(alice) + protocolPath : 'foo/bar', + data : TestDataGenerator.randomBytes(10), + dataCid : await TestDataGenerator.randomCborSha256Cid(), + dataSize : 123, + dataFormat : 'application/json', + recordId : await TestDataGenerator.randomCborSha256Cid(), + authorizationSigner : Jws.createSigner(alice) }; const createPromise2 = RecordsWrite.create(options2); @@ -179,15 +180,15 @@ describe('RecordsWrite', () => { const alice = await TestDataGenerator.generatePersona(); const options: RecordsWriteOptions = { - schema : 'http://any-schema.com', - protocol : 'http://example.com', - protocolPath : 'foo/bar', - parentId : await TestDataGenerator.randomCborSha256Cid(), - dataCid : await TestDataGenerator.randomCborSha256Cid(), - dataSize : 123, - dataFormat : 'application/json', - recordId : await TestDataGenerator.randomCborSha256Cid(), - authorizationSignatureInput : Jws.createSignatureInput(alice) + schema : 'http://any-schema.com', + protocol : 'http://example.com', + protocolPath : 'foo/bar', + parentId : await TestDataGenerator.randomCborSha256Cid(), + dataCid : await TestDataGenerator.randomCborSha256Cid(), + dataSize : 123, + dataFormat : 'application/json', + recordId : await TestDataGenerator.randomCborSha256Cid(), + authorizationSigner : Jws.createSigner(alice) }; const createPromise = RecordsWrite.create(options); @@ -195,6 +196,35 @@ describe('RecordsWrite', () => { await expect(createPromise).to.be.rejectedWith('`contextId` must also be given when `parentId` is specified'); }); + it('should be able to create a RecordsWrite successfully using a custom signer', async () => { + // create a custom signer + const hardCodedSignature = Encoder.stringToBytes('some_hard_coded_signature'); + class CustomSigner implements Signer { + public keyId = 'did:example:alice#key1'; + public algorithm = 'unused'; + public async sign (_content: Uint8Array): Promise { + return hardCodedSignature; + } + } + + const authorizationSigner = new CustomSigner(); + + const options: RecordsWriteOptions = { + schema : 'http://any-schema.com', + protocol : 'http://example.com', + protocolPath : 'foo/bar', + dataCid : await TestDataGenerator.randomCborSha256Cid(), + dataSize : 123, + dataFormat : 'application/json', + recordId : await TestDataGenerator.randomCborSha256Cid(), + authorizationSigner + }; + + const recordsWrite = await RecordsWrite.create(options); + + expect(recordsWrite.message.authorization!.signatures[0].signature).to.equal(Encoder.bytesToBase64Url(hardCodedSignature)); + }); + it('should throw if attempting to use `protocols` key derivation encryption scheme on non-protocol-based record', async () => { const alice = await TestDataGenerator.generatePersona(); @@ -212,9 +242,9 @@ describe('RecordsWrite', () => { // intentionally generating a record that is not protocol-based const createPromise = RecordsWrite.create({ - authorizationSignatureInput : Jws.createSignatureInput(alice), - dataFormat : 'application/json', - data : TestDataGenerator.randomBytes(10), + authorizationSigner : Jws.createSigner(alice), + dataFormat : 'application/json', + data : TestDataGenerator.randomBytes(10), encryptionInput }); @@ -238,9 +268,9 @@ describe('RecordsWrite', () => { // intentionally generating a record that is without `schema` const createPromise = RecordsWrite.create({ - authorizationSignatureInput : Jws.createSignatureInput(alice), - dataFormat : 'application/octet-stream', - data : TestDataGenerator.randomBytes(10), + authorizationSigner : Jws.createSigner(alice), + dataFormat : 'application/octet-stream', + data : TestDataGenerator.randomBytes(10), encryptionInput }); @@ -257,7 +287,7 @@ describe('RecordsWrite', () => { const write = await RecordsWrite.createFrom({ unsignedRecordsWriteMessage : recordsWrite.message, datePublished : getCurrentTimeInHighPrecision(), - authorizationSignatureInput : Jws.createSignatureInput(author) + authorizationSigner : Jws.createSigner(author) }); expect(write.message.descriptor.published).to.be.true; @@ -293,7 +323,7 @@ describe('RecordsWrite', () => { expect(recordsWrite.author).to.not.exist; expect(recordsWrite.authorizationPayload).to.not.exist; - expect(() => recordsWrite.message).to.throw(DwnErrorCode.RecordsWriteMissingAuthorizationSignatureInput); + expect(() => recordsWrite.message).to.throw(DwnErrorCode.RecordsWriteMissingAuthorizationSigner); }); }); }); diff --git a/tests/interfaces/snapshots-create.spec.ts b/tests/interfaces/snapshots-create.spec.ts index 6b5dc57e5..5f19b5a0c 100644 --- a/tests/interfaces/snapshots-create.spec.ts +++ b/tests/interfaces/snapshots-create.spec.ts @@ -30,9 +30,9 @@ describe('SnapshotsCreate', () => { }; const snapshotsCreate = await SnapshotsCreate.create({ - messageTimestamp : currentTime, + messageTimestamp : currentTime, definition, - authorizationSignatureInput : Jws.createSignatureInput(alice), + authorizationSigner : Jws.createSigner(alice), }); expect(snapshotsCreate.message.descriptor.messageTimestamp).to.equal(currentTime); diff --git a/tests/jose/jws/general.spec.ts b/tests/jose/jws/general.spec.ts index 49d0f5b71..b642e7c24 100644 --- a/tests/jose/jws/general.spec.ts +++ b/tests/jose/jws/general.spec.ts @@ -2,13 +2,14 @@ import chaiAsPromised from 'chai-as-promised'; import chai, { expect } from 'chai'; import { DidResolver } from '../../../src/did/did-resolver.js'; -import { GeneralJwsSigner } from '../../../src/jose/jws/general/signer.js'; +import { GeneralJwsBuilder } from '../../../src/jose/jws/general/builder.js'; import { GeneralJwsVerifier } from '../../../src/jose/jws/general/verifier.js'; import { Jws } from '../../../src/utils/jws.js'; -import { signers } from '../../../src/jose/algorithms/signing/signers.js'; +import { PrivateKeySigner } from '../../../src/index.js'; +import { signatureAlgorithms } from '../../../src/jose/algorithms/signing/signature-algorithms.js'; import sinon from 'sinon'; -const { Ed25519, secp256k1 } = signers; +const { Ed25519, secp256k1 } = signatureAlgorithms; chai.use(chaiAsPromised); @@ -22,16 +23,16 @@ describe('General JWS Sign/Verify', () => { it('should sign and verify secp256k1 signature using a key vector correctly', async () => { const { privateJwk, publicJwk } = await secp256k1.generateKeyPair(); const payloadBytes = new TextEncoder().encode('anyPayloadValue'); - const protectedHeader = { alg: 'ES256K', kid: 'did:jank:alice#key1' }; + const keyId = 'did:jank:alice#key1'; - const signer = await GeneralJwsSigner.create(payloadBytes, [{ privateJwk, protectedHeader }]); - const jws = signer.getJws(); + const jwsBuilder = await GeneralJwsBuilder.create(payloadBytes, [new PrivateKeySigner({ privateJwk, keyId })]); + const jws = jwsBuilder.getJws(); const mockResolutionResult = { didResolutionMetadata : {}, didDocument : { verificationMethod: [{ - id : 'did:jank:alice#key1', + id : keyId, type : 'JsonWebKey2020', controller : 'did:jank:alice', publicKeyJwk : publicJwk @@ -56,10 +57,10 @@ describe('General JWS Sign/Verify', () => { it('should sign and verify ed25519 signature using a key vector correctly', async () => { const { privateJwk, publicJwk } = await Ed25519.generateKeyPair(); const payloadBytes = new TextEncoder().encode('anyPayloadValue'); - const protectedHeader = { alg: 'EdDSA', kid: 'did:jank:alice#key1' }; + const keyId = 'did:jank:alice#key1'; - const signer = await GeneralJwsSigner.create(payloadBytes, [{ privateJwk, protectedHeader }]); - const jws = signer.getJws(); + const jwsBuilder = await GeneralJwsBuilder.create(payloadBytes, [new PrivateKeySigner({ privateJwk, keyId })]); + const jws = jwsBuilder.getJws(); const mockResolutionResult = { didResolutionMetadata : {}, @@ -95,7 +96,7 @@ describe('General JWS Sign/Verify', () => { did : 'did:jank:alice', privateJwk : secp256k1Keys.privateJwk, jwkPublic : secp256k1Keys.publicJwk, - protectedHeader : { alg: 'ES256K', kid: 'did:jank:alice#key1' }, + keyId : 'did:jank:alice#key1', mockResolutionResult : { didResolutionMetadata : {}, didDocument : { @@ -114,7 +115,7 @@ describe('General JWS Sign/Verify', () => { did : 'did:jank:bob', privateJwk : ed25519Keys.privateJwk, jwkPublic : ed25519Keys.publicJwk, - protectedHeader : { alg: 'EdDSA', kid: 'did:jank:bob#key1' }, + keyId : 'did:jank:bob#key1', mockResolutionResult : { didResolutionMetadata : {}, didDocument : { @@ -129,14 +130,14 @@ describe('General JWS Sign/Verify', () => { } }; - const signatureInputs = [ - { privateJwk: alice.privateJwk, protectedHeader: alice.protectedHeader }, - { privateJwk: bob.privateJwk, protectedHeader: bob.protectedHeader }, + const signers = [ + new PrivateKeySigner({ privateJwk: alice.privateJwk, keyId: alice.keyId }), + new PrivateKeySigner({ privateJwk: bob.privateJwk, keyId: bob.keyId }) ]; const payloadBytes = new TextEncoder().encode('anyPayloadValue'); - const signer = await GeneralJwsSigner.create(payloadBytes, signatureInputs); - const jws = signer.getJws(); + const jwsBuilder = await GeneralJwsBuilder.create(payloadBytes, signers); + const jws = jwsBuilder.getJws(); const resolveStub = sinon.stub(); resolveStub.withArgs('did:jank:alice').resolves(alice.mockResolutionResult); @@ -148,28 +149,28 @@ describe('General JWS Sign/Verify', () => { }); const verifier = new GeneralJwsVerifier(jws); - const verificatonResult = await verifier.verify(resolverStub); + const verificationResult = await verifier.verify(resolverStub); - expect(verificatonResult.signers.length).to.equal(2); - expect(verificatonResult.signers).to.include(alice.did); - expect(verificatonResult.signers).to.include(bob.did); + expect(verificationResult.signers.length).to.equal(2); + expect(verificationResult.signers).to.include(alice.did); + expect(verificationResult.signers).to.include(bob.did); }); it('should not verify the same signature more than once', async () => { const { privateJwk: privateJwkEd25519, publicJwk: publicJwkEd25519 } = await Ed25519.generateKeyPair(); const { privateJwk: privateJwkSecp256k1, publicJwk: publicJwkSecp256k1 } = await secp256k1.generateKeyPair(); const payloadBytes = new TextEncoder().encode('anyPayloadValue'); - const protectedHeaderEd25519 = { alg: 'EdDSA', kid: 'did:jank:alice#key1' }; - const protectedHeaderSecp256k1 = { alg: 'ES256K', kid: 'did:jank:alice#key2' }; + const keyId1 = 'did:jank:alice#key1'; + const keyId2 = 'did:jank:alice#key2'; - const signer = await GeneralJwsSigner.create( + const jwsBuilder = await GeneralJwsBuilder.create( payloadBytes, [ - { privateJwk: privateJwkEd25519, protectedHeader: protectedHeaderEd25519 }, - { privateJwk: privateJwkSecp256k1, protectedHeader: protectedHeaderSecp256k1 } + new PrivateKeySigner({ privateJwk: privateJwkEd25519, keyId: keyId1 }), + new PrivateKeySigner({ privateJwk: privateJwkSecp256k1, keyId: keyId2 }) ] ); - const jws = signer.getJws(); + const jws = jwsBuilder.getJws(); const mockResolutionResult = { didResolutionMetadata : {}, diff --git a/tests/utils/private-key-signer.spec.ts b/tests/utils/private-key-signer.spec.ts new file mode 100644 index 000000000..2681b996d --- /dev/null +++ b/tests/utils/private-key-signer.spec.ts @@ -0,0 +1,44 @@ +import { DwnErrorCode } from '../../src/core/dwn-error.js'; +import { expect } from 'chai'; +import { PrivateKeySigner } from '../../src/index.js'; +import { Secp256k1 } from '../../src/utils/secp256k1.js'; + +describe('PrivateKeySigner', () => { + describe('constructor', () => { + it('should use key ID found in the private JWK if no key ID is explicitly given', async () => { + const { privateJwk } = await Secp256k1.generateKeyPair(); + privateJwk.kid = 'awesome-key-id'; + + const signer = new PrivateKeySigner({ privateJwk }); + expect(signer.keyId).to.equal(privateJwk.kid); + }); + + it('should override signature algorithm found in the private JWK if a value is explicitly given', async () => { + const { privateJwk } = await Secp256k1.generateKeyPair(); + + const explicitlySpecifiedAlgorithm = 'awesome-algorithm'; + const signer = new PrivateKeySigner({ privateJwk, keyId: 'anyValue', algorithm: explicitlySpecifiedAlgorithm }); + expect(signer.algorithm).to.equal(explicitlySpecifiedAlgorithm); + }); + + it('should throw if key ID is not explicitly specified and not given in private JWK', async () => { + const { privateJwk } = await Secp256k1.generateKeyPair(); + + expect(() => new PrivateKeySigner({ privateJwk })).to.throw(DwnErrorCode.PrivateKeySignerUnableToDeduceKeyId); + }); + + it('should throw if signature algorithm is not explicitly specified and not given in private JWK', async () => { + const { privateJwk } = await Secp256k1.generateKeyPair(); + delete privateJwk.alg; // remove `alg` for this test + + expect(() => new PrivateKeySigner({ privateJwk, keyId: 'anyValue' })).to.throw(DwnErrorCode.PrivateKeySignerUnableToDeduceAlgorithm); + }); + + it('should throw if crypto curve of the given private JWK is not supported', async () => { + const { privateJwk } = await Secp256k1.generateKeyPair(); + (privateJwk as any).crv = 'unknown'; // change `crv` to an unsupported value for this test + + expect(() => new PrivateKeySigner({ privateJwk, keyId: 'anyValue' })).to.throw(DwnErrorCode.PrivateKeySignerUnsupportedCurve); + }); + }); +}); diff --git a/tests/utils/test-data-generator.ts b/tests/utils/test-data-generator.ts index dbd980d8a..fb01e5261 100644 --- a/tests/utils/test-data-generator.ts +++ b/tests/utils/test-data-generator.ts @@ -326,12 +326,12 @@ export class TestDataGenerator { // TODO: #451 - Remove reference and use of dataStream everywhere in tests - https://github.com/TBD54566975/dwn-sdk-js/issues/451 const dataStream = undefined; - const authorizationSignatureInput = Jws.createSignatureInput(author); + const authorizationSigner = Jws.createSigner(author); const options: ProtocolsConfigureOptions = { messageTimestamp : input?.messageTimestamp, definition, - authorizationSignatureInput, + authorizationSigner, permissionsGrantId : input?.permissionsGrantId }; @@ -352,12 +352,12 @@ export class TestDataGenerator { // generate author persona if not given const author = input?.author ?? await TestDataGenerator.generatePersona(); - const authorizationSignatureInput = Jws.createSignatureInput(author); + const authorizationSigner = Jws.createSigner(author); const options: ProtocolsQueryOptions = { messageTimestamp : input?.messageTimestamp, filter : input?.filter, - authorizationSignatureInput, + authorizationSigner, permissionsGrantId : input?.permissionsGrantId, }; removeUndefinedProperties(options); @@ -383,8 +383,8 @@ export class TestDataGenerator { public static async generateRecordsWrite(input?: GenerateRecordsWriteInput): Promise { const author = input?.author ?? await TestDataGenerator.generatePersona(); - const authorizationSignatureInput = Jws.createSignatureInput(author); - const attestationSignatureInputs = Jws.createSignatureInputs(input?.attesters ?? []); + const authorizationSigner = Jws.createSigner(author); + const attestationSigners = Jws.createSigners(input?.attesters ?? []); const dataCid = input?.dataCid; const dataSize = input?.dataSize; @@ -411,8 +411,8 @@ export class TestDataGenerator { data : dataBytes, dataCid, dataSize, - authorizationSignatureInput, - attestationSignatureInputs, + authorizationSigner, + attestationSigners, encryptionInput : input?.encryptionInput, permissionsGrantId : input?.permissionsGrantId, }; @@ -540,7 +540,7 @@ export class TestDataGenerator { }; await recordsWrite.encryptSymmetricEncryptionKey(encryptionInput); - await recordsWrite.sign(Jws.createSignatureInput(author)); + await recordsWrite.sign(Jws.createSigner(author)); return { message, dataStream: dataStream!, recordsWrite, encryptedDataBytes, encryptionInput }; } @@ -566,7 +566,7 @@ export class TestDataGenerator { published, datePublished, messageTimestamp : input.messageTimestamp, - authorizationSignatureInput : Jws.createSignatureInput(input.author) + authorizationSigner : Jws.createSigner(input.author) }; const recordsWrite = await RecordsWrite.createFrom(options); @@ -594,14 +594,14 @@ export class TestDataGenerator { author = await TestDataGenerator.generatePersona(); } - let authorizationSignatureInput = undefined; + let authorizationSigner = undefined; if (author !== undefined) { - authorizationSignatureInput = Jws.createSignatureInput(author); + authorizationSigner = Jws.createSigner(author); } const options: RecordsQueryOptions = { messageTimestamp : input?.messageTimestamp, - authorizationSignatureInput, + authorizationSigner, filter : input?.filter ?? { schema: TestDataGenerator.randomString(10) }, // must have one filter property if no filter is given dateSort : input?.dateSort, pagination : input?.pagination @@ -624,8 +624,8 @@ export class TestDataGenerator { const author = input?.author ?? await DidKeyResolver.generate(); const recordsDelete = await RecordsDelete.create({ - recordId : input?.recordId ?? await TestDataGenerator.randomCborSha256Cid(), - authorizationSignatureInput : Jws.createSignatureInput(author) + recordId : input?.recordId ?? await TestDataGenerator.randomCborSha256Cid(), + authorizationSigner : Jws.createSigner(author) }); return { @@ -641,11 +641,11 @@ export class TestDataGenerator { public static async generateHooksWrite(input?: GenerateHooksWriteInput): Promise { const author = input?.author ?? await TestDataGenerator.generatePersona(); - const authorizationSignatureInput = Jws.createSignatureInput(author); + const authorizationSigner = Jws.createSigner(author); const options: HooksWriteOptions = { messageTimestamp : input?.messageTimestamp, - authorizationSignatureInput, + authorizationSigner, filter : input?.filter ?? { method: 'RecordsWrite' }, // hardcode to filter on `RecordsWrite` if no filter is given }; removeUndefinedProperties(options); @@ -673,8 +673,8 @@ export class TestDataGenerator { interface : DwnInterfaceName.Records, method : DwnMethodName.Write }, - conditions : input?.conditions, - authorizationSignatureInput : Jws.createSignatureInput(author) + conditions : input?.conditions, + authorizationSigner : Jws.createSigner(author) }); return { @@ -702,8 +702,8 @@ export class TestDataGenerator { interface : DwnInterfaceName.Records, method : DwnMethodName.Write }, - conditions : input?.conditions, - authorizationSignatureInput : Jws.createSignatureInput(author) + conditions : input?.conditions, + authorizationSigner : Jws.createSigner(author) }); return { @@ -718,10 +718,10 @@ export class TestDataGenerator { */ public static async generatePermissionsRevoke(input?: GeneratePermissionsRevokeInput): Promise { const author = input?.author ?? await TestDataGenerator.generatePersona(); - const authorizationSignatureInput = Jws.createSignatureInput(author); + const authorizationSigner = Jws.createSigner(author); const permissionsRevoke = await PermissionsRevoke.create({ - authorizationSignatureInput, + authorizationSigner, permissionsGrantId : input?.permissionsGrantId ?? await TestDataGenerator.randomCborSha256Cid(), messageTimestamp : input?.dateCreated }); @@ -735,9 +735,9 @@ export class TestDataGenerator { public static async generateEventsGet(input?: GenerateEventsGetInput): Promise { const author = input?.author ?? await TestDataGenerator.generatePersona(); - const authorizationSignatureInput = Jws.createSignatureInput(author); + const authorizationSigner = Jws.createSigner(author); - const options: EventsGetOptions = { authorizationSignatureInput }; + const options: EventsGetOptions = { authorizationSigner }; if (input?.watermark) { options.watermark = input.watermark; } @@ -753,10 +753,10 @@ export class TestDataGenerator { public static async generateMessagesGet(input: GenerateMessagesGetInput): Promise { const author = input?.author ?? await TestDataGenerator.generatePersona(); - const authorizationSignatureInput = Jws.createSignatureInput(author); + const authorizationSigner = Jws.createSigner(author); const options: MessagesGetOptions = { - authorizationSignatureInput, + authorizationSigner, messageCids: input.messageCids }; diff --git a/tests/validation/json-schemas/jwk-verification-method.spec.ts b/tests/validation/json-schemas/jwk-verification-method.spec.ts index 80d96c653..cfa074386 100644 --- a/tests/validation/json-schemas/jwk-verification-method.spec.ts +++ b/tests/validation/json-schemas/jwk-verification-method.spec.ts @@ -1,8 +1,8 @@ import { expect } from 'chai'; -import { signers } from '../../../src/jose/algorithms/signing/signers.js'; +import { signatureAlgorithms } from '../../../src/jose/algorithms/signing/signature-algorithms.js'; import { validateJsonSchema } from '../../../src/schema-validator.js'; -const { secp256k1 } = signers; +const { secp256k1 } = signatureAlgorithms; describe('JwkVerificationMethod', async () => { // NOTE: @noble/secp256k1 requires globalThis.crypto polyfill for diff --git a/tests/validation/json-schemas/jwk/general-jwk.spec.ts b/tests/validation/json-schemas/jwk/general-jwk.spec.ts index 3493a27cf..ca4a466cf 100644 --- a/tests/validation/json-schemas/jwk/general-jwk.spec.ts +++ b/tests/validation/json-schemas/jwk/general-jwk.spec.ts @@ -1,8 +1,8 @@ import { expect } from 'chai'; -import { signers } from '../../../../src/jose/algorithms/signing/signers.js'; +import { signatureAlgorithms } from '../../../../src/jose/algorithms/signing/signature-algorithms.js'; import { validateJsonSchema } from '../../../../src/schema-validator.js'; -const { Ed25519, secp256k1 } = signers; +const { Ed25519, secp256k1 } = signatureAlgorithms; describe('GeneralJwk Schema', async () => { const jwkSecp256k1 = await secp256k1.generateKeyPair(); diff --git a/tests/validation/json-schemas/jwk/public-jwk.spec.ts b/tests/validation/json-schemas/jwk/public-jwk.spec.ts index 345d9f3d5..ed06e0925 100644 --- a/tests/validation/json-schemas/jwk/public-jwk.spec.ts +++ b/tests/validation/json-schemas/jwk/public-jwk.spec.ts @@ -1,8 +1,8 @@ import { expect } from 'chai'; -import { signers } from '../../../../src/jose/algorithms/signing/signers.js'; +import { signatureAlgorithms } from '../../../../src/jose/algorithms/signing/signature-algorithms.js'; import { validateJsonSchema } from '../../../../src/schema-validator.js'; -const { Ed25519, secp256k1 } = signers; +const { Ed25519, secp256k1 } = signatureAlgorithms; describe('PublicJwk Schema', async () => { const { publicJwk: publicJwkSecp256k1 } = await secp256k1.generateKeyPair();