Skip to content
This repository has been archived by the owner on Mar 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4 from payid-org/clear-addresses-after-signing
Browse files Browse the repository at this point in the history
change the `sign` command to clear unsigned addresses by default
  • Loading branch information
nhartner authored Sep 11, 2020
2 parents 1ab899e + 0635ed4 commit 6f3a651
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 14 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,12 @@ Before you sign an PayID, you must either load the PayID using the `load` comman

Once a PayID has been initialized or loaded, you can sign it using an [identity key](#identity-keys). You must either generate a new key, or load an existing one. Once your PayID has been loaded or initialized, and your identity key has been generated or loaded,
you can sign the PayID using `sign` command. The `sign` command signs each of your PayID address
mappings using the loaded identity keys, and outputs the resulting PayID with a `verifiedAddress` field. Run the `save`
command to save your PayID, with signed addresses, to file.
mappings using the loaded identity keys, and outputs the resulting PayID with a `verifiedAddress` field.

By default, the sign command clears the unsigned `addresses` from the results. If you wish to
retain unsigned addresses after signing, use `sign --keep-addresses` or `sign -k` instead.

Finally, run the `save` command to save your PayID, with signed addresses, to file.

### Inspect a Verified PayID

Expand Down
9 changes: 7 additions & 2 deletions src/commands/Command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,15 @@ abstract class Command {
this.vorpal = vorpal
}

public setup(): void {
/**
* Sets up and registers the vorpal command.
*
* @returns The registered command.
*/
public setup(): Vorpal.Command {
// Register the concrete command to Vorpal.
// Execute the concrete action inside a try/catch wrapper
this.vorpal.command(this.command(), this.description()).action(
return this.vorpal.command(this.command(), this.description()).action(
async (args: Args): Promise<void> => {
await this.action(args).catch((error) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- error has any type
Expand Down
58 changes: 48 additions & 10 deletions src/commands/payid-sign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,35 @@ import {
signWithKeys,
IdentityKeySigningParams,
toKey,
PaymentInformation,
} from '@payid-org/utils'
import { JWKECKey, JWKOctKey, JWKOKPKey, JWKRSAKey } from 'jose'
import * as Vorpal from 'vorpal'

import Command from './Command'

/**
* Signs the currently loaded PayID PaymentInformation using the loaded signings keys.
*/
export default class SignPayIdCommand extends Command {
protected async action(): Promise<void> {
/**
* @override
*/
public setup(): Vorpal.Command {
return super
.setup()
.option(
'-k, --keep-addresses',
'Keep the unverified addresses section after signing.',
)
}

/**
* @override
*/
protected async action(args: Vorpal.Args): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Vorpal.options isn't typed
const isKeepAddresses: boolean = args.options['keep-addresses'] ?? false
const info = this.getPaymentInfo()
const payId = info.payId
if (!payId) {
Expand All @@ -28,15 +47,7 @@ export default class SignPayIdCommand extends Command {
return
}

const updatedAddresses = info.addresses.map((address) => {
const jws = signWithKeys(payId, address, signingKeys)
return convertToVerifiedAddress(jws)
})
const updated = {
payId: info.payId,
addresses: info.addresses,
verifiedAddresses: updatedAddresses,
}
const updated = signPayId(info, signingKeys, isKeepAddresses)

this.localStorage.setPaymentInfo(updated)
this.logPaymentInfo(updated)
Expand Down Expand Up @@ -70,6 +81,33 @@ export default class SignPayIdCommand extends Command {
}
}

/**
* Signs all the addresses for the given payment information and returns
* with verified address.
*
* @param info - The payment information to sign.
* @param signingKeys - The keys to sign with.
* @param isKeepAddresses - If true, the unverified addresses property will be retained instead of cleared.
* @returns A copy of the PaymentInformation but with verified addresses.
*/
export function signPayId(
info: PaymentInformation,
signingKeys: IdentityKeySigningParams[],
isKeepAddresses: boolean,
): PaymentInformation {
const payId = info.payId
const updatedAddresses = info.addresses.map((address) => {
const jws = signWithKeys(payId, address, signingKeys)
return convertToVerifiedAddress(jws)
})
const updated = {
payId: info.payId,
addresses: isKeepAddresses ? info.addresses : [],
verifiedAddresses: updatedAddresses,
}
return updated
}

/**
* Returns the default algorithm to use to sign with the given jwk.
*
Expand Down
50 changes: 50 additions & 0 deletions test/unit/signPayId.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'mocha'
import {
AddressDetailsType,
IdentityKeySigningParams,
PaymentInformation,
} from '@payid-org/utils'
import { assert } from 'chai'
import { JWK } from 'jose'

import { signPayId } from '../../src/commands/payid-sign'

const info: PaymentInformation = {
payId: 'boaty$mcboatface.com',
addresses: [
{
paymentNetwork: 'boatcoin',
environment: 'seanet',
addressDetailsType: AddressDetailsType.CryptoAddress,
addressDetails: {
address: 'xyz12345',
},
},
],
verifiedAddresses: [],
}

describe('when signPayId()', function (): void {
let signingKey: IdentityKeySigningParams

beforeEach('create key', async function (): Promise<void> {
const key = await JWK.generate('EC', 'P-256')
signingKey = new IdentityKeySigningParams(key, 'ES256')
})

it('called with keepAddresses=true, then addresses property is retained', async function (): Promise<
void
> {
const result = signPayId(info, [signingKey], true)
assert.equal(result.addresses, info.addresses)
assert.lengthOf(result.verifiedAddresses, 1)
})

it('called with keepAddresses=false, then addresses property is cleared', async function (): Promise<
void
> {
const result = signPayId(info, [signingKey], false)
assert.isEmpty(result.addresses)
assert.lengthOf(result.verifiedAddresses, 1)
})
})

0 comments on commit 6f3a651

Please sign in to comment.