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

Commit 00cd879

Browse files
nhartner0xCLARITY
andauthored
CLI - Chapter 4 - Inspector PayID (#25)
* functions and types for signing and validating a VerifiablePayID * get pem and x5c working simplify verify call to use Embedded option * lint fixes and refactored SigningParams types from interface to classes * exports * packages.json cleanup * WIP: get PKI with certificate chain validation workiing * lint fixes use generated Root CA, Intermediate CA, and server cert in tests * add 'name' to crit section * splitting web-pki into multiple branches * logic to parse a chain of certificates from the x5c section of a JWS * add test to verify self-signed certs pass signature check but fail chain of certificate check * initial stab at some CLI commands * refactoring of commands add commands for signing and verifying signatures * command to save key upper case the network and environment * refactor inspection logic into a standalone class * more refactoring * linting fixes * jsdoc, refactorings and test coverage * npm audit fix * npm audit fix * revert signing and verfication and inspection changes moving to a separate PR * reverting signing and verification related code for a later PR * bringing back commands to build and modify a payid (sans signatures) * bringing back commands to build and modify a payid (sans signatures) * bringing back commands to create and import keys, sign and verify a payid * bringing back command to inspect a PayID's verified addresses * better error messaging * docker file * fix typo in error message * code review feedback change command 'url to-payid' to 'payid from-url' * remove eslint disablement rule from eslint rc in favor of doing it in the one class that violates the rule * reorder fields to match constructor per PR review * documentation * linting changes due to merge * linting changes due to merge * Update src/commands/key-generate-identity-key.ts Co-authored-by: Hans Bergren <[email protected]> * simplify nest if condition * js doc * fix post merge Co-authored-by: Hans Bergren <[email protected]>
1 parent b9391ad commit 00cd879

16 files changed

+564
-79
lines changed

Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
FROM node:12-alpine
2+
3+
ADD . / payid-utils/
4+
5+
RUN cd payid-utils/ &&\
6+
npm cache clean --force &&\
7+
npm install &&\
8+
npm run build && \
9+
npm link
10+
11+
FROM node:12-alpine
12+
13+
RUN mkdir /opt/payid-utils
14+
15+
WORKDIR /opt/payid-utils
16+
17+
COPY --from=0 /payid-utils/dist /opt/payid-utils/dist
18+
COPY --from=0 /payid-utils/node_modules /opt/payid-utils/node_modules
19+
20+
CMD ["node", "/opt/payid-utils/dist/cli.js"]

package-lock.json

Lines changed: 36 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cli.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ new cmd.ListKeysCommand(vorpal, localStorage).setup()
1919
new cmd.LoadIdentityKeyCommand(vorpal, localStorage).setup()
2020
new cmd.LoadServerKeyCommand(vorpal, localStorage).setup()
2121
new cmd.InitPayIdCommand(vorpal, localStorage).setup()
22+
new cmd.InspectPayIdCommand(vorpal, localStorage).setup()
2223
new cmd.LoadPayIdCommand(vorpal, localStorage).setup()
2324
new cmd.ShowPayIdCommand(vorpal, localStorage).setup()
2425
new cmd.SignPayIdCommand(vorpal, localStorage).setup()

src/commands/Command.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ abstract class Command {
3838
try {
3939
await this.action(args)
4040
} catch (error) {
41-
this.vorpal.log(error)
41+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- error has any type
42+
const { message } = error
43+
this.vorpal.log(message)
4244
}
4345
},
4446
)
@@ -54,7 +56,7 @@ abstract class Command {
5456
const info = this.localStorage.getPaymentInfo()
5557
if (info === undefined) {
5658
throw new Error(
57-
`please run 'payid init' or 'payid load' before adding an address`,
59+
`error: no PayID loaded. Run 'payid init' or 'payid load' first.`,
5860
)
5961
}
6062
return info

src/commands/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export { default as ClearKeysCommand } from './keys-clear'
1111
export { default as ListKeysCommand } from './keys-list'
1212
export { default as LocalStorage } from './localstorage'
1313
export { default as InitPayIdCommand } from './payid-init'
14+
export { default as InspectPayIdCommand } from './payid-inspect'
1415
export { default as LoadPayIdCommand } from './payid-load'
1516
export { default as SavePayIdCommand } from './payid-save'
1617
export { default as ShowPayIdCommand } from './payid-show'

src/commands/payid-inspect.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import * as Vorpal from 'vorpal'
2+
3+
import { PaymentInformationInspector } from '../verifiable'
4+
import {
5+
CertificateChainInspectionResult,
6+
SignatureInspectionResult,
7+
} from '../verifiable/payment-information-inspector'
8+
9+
import Command from './Command'
10+
import LocalStorage from './localstorage'
11+
12+
/**
13+
* Inspects the currently loaded PayID and prints inspection details to the console.
14+
* Inspection looks at verified addresses and verifies the signatures and certificate chain (if present).
15+
*/
16+
export default class InspectPayIdCommand extends Command {
17+
private readonly paymentInformationInspector: PaymentInformationInspector
18+
19+
/**
20+
* Creates new command.
21+
*
22+
* @param vorpal - The vorpal instance to use.
23+
* @param localStorage - The localstorage to use.
24+
*/
25+
public constructor(vorpal: Vorpal, localStorage: LocalStorage) {
26+
super(vorpal, localStorage)
27+
this.paymentInformationInspector = new PaymentInformationInspector()
28+
}
29+
30+
/**
31+
* @override
32+
*/
33+
protected command(): string {
34+
return 'payid inspect'
35+
}
36+
37+
/**
38+
* @override
39+
*/
40+
protected description(): string {
41+
return 'Inspect signatures on the loaded PayID'
42+
}
43+
44+
protected async action(): Promise<void> {
45+
const info = this.getPaymentInfo()
46+
const result = this.paymentInformationInspector.inspect(info)
47+
this.vorpal.log(`${info.payId} ${validString(result.isVerified)}`)
48+
result.verifiedAddressesResults.forEach((addressResult) => {
49+
const address = addressResult.address
50+
const cryptoAddress =
51+
'address' in address.addressDetails
52+
? address.addressDetails.address
53+
: ''
54+
55+
const environment: string =
56+
typeof address.environment === 'string' ? address.environment : ''
57+
this.vorpal.log(
58+
`Found verified ${address.paymentNetwork} ${environment} address ${cryptoAddress}`,
59+
)
60+
this.vorpal.log(
61+
`- Signed with ${addressResult.signaturesResults.length} signature(s)`,
62+
)
63+
addressResult.signaturesResults.forEach((signatureResult, sigIndex) => {
64+
this.inspectSignatureResult(sigIndex, signatureResult)
65+
})
66+
})
67+
}
68+
69+
/**
70+
* Inspects and logs information about the inspection result.
71+
*
72+
* @param sigIndex - The index of the signature.
73+
* @param signatureResult -The signature result to inspect.
74+
*/
75+
private inspectSignatureResult(
76+
sigIndex: number,
77+
signatureResult: SignatureInspectionResult,
78+
): void {
79+
this.vorpal.log(
80+
`- Signature ${sigIndex + 1} ${validString(
81+
signatureResult.isSignatureValid,
82+
)}`,
83+
)
84+
if (signatureResult.jwk && signatureResult.keyType) {
85+
const thumbprint = signatureResult.jwk.thumbprint
86+
this.vorpal.log(
87+
` - Signed with ${signatureResult.jwk.kty} ${signatureResult.keyType} with thumbprint ${thumbprint}`,
88+
)
89+
}
90+
if (signatureResult.certificateChainResult) {
91+
this.inspectCertificateChainResult(signatureResult.certificateChainResult)
92+
}
93+
}
94+
95+
/**
96+
* Inspects and logs information about the inspection result.
97+
*
98+
* @param result -The chain to inspect.
99+
*/
100+
private inspectCertificateChainResult(
101+
result: CertificateChainInspectionResult,
102+
): void {
103+
this.vorpal.log(` - Certificate chain ${validString(result.isChainValid)}`)
104+
result.certificateResults.forEach((chainResult, chainIndex) => {
105+
this.vorpal.log(
106+
` - Certificate ${chainIndex + 1} for ${
107+
chainResult.issuedTo
108+
}, issued by ${chainResult.issuedBy}`,
109+
)
110+
})
111+
}
112+
}
113+
114+
/**
115+
* Prints either 'is verfied' or 'is NOT verfied' based on the valid flag.
116+
*
117+
* @param valid - Flag for is / is NOT.
118+
* @returns The is/is NOT string.
119+
*/
120+
function validString(valid: boolean): string {
121+
return `${valid ? 'is' : 'is NOT'} verified`
122+
}

src/commands/payid-sign.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ export default class SignPayIdCommand extends Command {
2222
}
2323
const signingKeys = this.getSigningKeys()
2424
if (signingKeys.length === 0) {
25-
this.vorpal.log(`you must generate or load a key before signing`)
25+
this.vorpal.log(
26+
'you must generate or load a key before signing using ' +
27+
`'keys generate identity-key' or 'keys load identity-key'`,
28+
)
2629
return
2730
}
2831

src/verifiable/certificate-chain-validator.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import * as forge from 'node-forge'
66

77
import { getJwkFromRecipient, isX5C } from './keys'
88

9-
import certificateFromPem = forge.pki.certificateFromPem
10-
119
/**
1210
* Service to validate the certificate chain (using web PKI) for Verifiable PayIDs
1311
* signed with a a server key.
@@ -39,6 +37,9 @@ export default class CertificateChainValidator {
3937
* @returns True if verified.
4038
*/
4139
public verifyCertificateChainRecipient(recipient: JWS.JWSRecipient): boolean {
40+
if (!recipient.protected) {
41+
return false
42+
}
4243
return this.verifyCertificateChain(extractX5CCertificates(recipient))
4344
}
4445

@@ -50,7 +51,7 @@ export default class CertificateChainValidator {
5051
*/
5152
public verifyCertificateChain(chain: forge.pki.Certificate[]): boolean {
5253
if (chain.length === 0) {
53-
return true
54+
return false
5455
}
5556
try {
5657
return forge.pki.verifyCertificateChain(this.caStore, chain)
@@ -104,7 +105,7 @@ export function extractX5CCertificates(
104105
const jwk = getJwkFromRecipient(recipient)
105106
if (jwk && isX5C(jwk) && jwk.x5c) {
106107
return jwk.x5c.map((pem) =>
107-
certificateFromPem(
108+
forge.pki.certificateFromPem(
108109
`-----BEGIN CERTIFICATE-----${pem}-----END CERTIFICATE-----`,
109110
),
110111
)

0 commit comments

Comments
 (0)