Skip to content

Commit

Permalink
Added exmaple and README documentation on custom signer
Browse files Browse the repository at this point in the history
  • Loading branch information
thehenrytsai committed Sep 15, 2023
1 parent 37ad389 commit 5a1f9b3
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 3 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,31 @@ const tenantGate = new CustomTenantGate();
const dwn = await Dwn.create({ messageStore, dataStore, eventLog, tenantGate });
```
### Custom Signature Signer
Instead of using `PrivateKeySigner` to perform messger signing when you have the private key readily available, 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 async sign (content: Uint8Array): Promise<Uint8Array> {
... // custom signing logic
}
}

const signer = new CustomSigner();

const options: RecordsWriteOptions = {
...
authorizationSignatureInput : {
signer,
protectedHeader: { alg: 'EdDSA', kid: 'did:example:alice#key1' } // see https://www.iana.org/assignments/jose/jose.xhtml for valid signature `alg` values
}
};

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.
Expand Down
17 changes: 15 additions & 2 deletions src/types/jws-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,22 @@ export type SignatureEntry = {
};

export type JwsHeaderParameters = {
/** JWS "alg" (Algorithm) Header Parameter. */
/**
* JWS "alg" (Algorithm) Header Parameter.
*
* 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
*/
alg: string
/** JWS "kid" (Key ID) Parameter. */

/**
* JWS "kid" (Key ID) Parameter.
*
* This property is not a required property per JWS specification, but is required for DWN authentication.
* 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.
*/
kid: string
};

Expand Down
33 changes: 32 additions & 1 deletion tests/interfaces/records-write.spec.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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);
Expand Down Expand Up @@ -195,6 +196,36 @@ 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 async sign (_content: Uint8Array): Promise<Uint8Array> {
return hardCodedSignature;
}
}

const signer = 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(),
authorizationSignatureInput : {
signer,
protectedHeader: { alg: 'unused', kid: 'did:example:alice#key1' }
}
};

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();

Expand Down

0 comments on commit 5a1f9b3

Please sign in to comment.