diff --git a/src/asp.js b/src/asp.js index 316aa0d..2ed52b8 100644 --- a/src/asp.js +++ b/src/asp.js @@ -19,6 +19,7 @@ import { base32, base64url } from 'rfc4648' import { Claim } from './claim.js' import { Persona } from './persona.js' import { Profile } from './profile.js' +import { ProfileType } from './enums.js' const SupportedCryptoAlg = ['EdDSA', 'ES256', 'ES256K', 'ES384', 'ES512'] @@ -129,9 +130,12 @@ export async function parseProfileJws (profileJws, uri) { const profileClaimsParsed = profileClaims.map(x => new Claim(x, uri)) - const pe = new Persona(profileName, profileDescription || '', profileClaimsParsed) - const pr = new Profile([pe]) - pr.primaryPersona = 0 + const pe = new Persona(profileName, profileClaimsParsed) + if (profileDescription) { + pe.setDescription(profileDescription) + } + + const pr = new Profile(ProfileType.ASP, uri, [pe]) return pr } diff --git a/src/enums.js b/src/enums.js index 661d39d..915ecba 100644 --- a/src/enums.js +++ b/src/enums.js @@ -75,9 +75,9 @@ export const EntityEncodingFormat = { * @readonly * @enum {string} */ -export const ProofAccess = { +export const ProofAccessRestriction = { /** Any HTTP request will work */ - GENERIC: 'generic', + NONE: 'none', /** CORS requests are denied */ NOCORS: 'nocors', /** HTTP requests must contain API or access tokens */ @@ -127,13 +127,72 @@ export const ClaimRelation = { /** * Status of the Claim instance * @readonly - * @enum {string} + * @enum {number} */ export const ClaimStatus = { /** Claim has been initialized */ - INIT: 'init', + INIT: 100, /** Claim has matched its URI to candidate claim definitions */ - MATCHED: 'matched', - /** Claim has verified one or multiple candidate claim definitions */ - VERIFIED: 'verified' + MATCHED: 101, + /** Claim was successfully verified */ + VERIFIED: 200, + /** Claim was successfully verified using proxied data */ + VERIFIED_VIA_PROXY: 201, + /** Unknown matching error */ + MATCHING_ERROR: 300, + /** No matched service providers */ + NO_MATCHES: 301, + /** Unknown matching error */ + VERIFICATION_ERROR: 400, + /** No proof found in data returned by service providers */ + NO_PROOF_FOUND: 401 +} + +/** + * Profile type + * @readonly + * @enum {string} + */ +export const ProfileType = { + /** ASP profile */ + ASP: 'asp', + /** OpenPGP profile */ + OPENPGP: 'openpgp' +} + +/** + * Public key type + * @readonly + * @enum {string} + */ +export const PublicKeyType = { + EDDSA: 'eddsa', + ES256: 'es256', + OPENPGP: 'openpgp', + NONE: 'none' +} + +/** + * Public key format + * @readonly + * @enum {string} + */ +export const PublicKeyEncoding = { + PEM: 'pem', + JWK: 'jwk', + ARMORED_PGP: 'armored_pgp', + NONE: 'none' +} + +/** + * Method to fetch the public key + * @readonly + * @enum {string} + */ +export const PublicKeyFetchMethod = { + ASPE: 'aspe', + HKP: 'hkp', + WKD: 'wkd', + HTTP: 'http', + NONE: 'none' } diff --git a/src/index.js b/src/index.js index 5b34e87..ec2d94f 100644 --- a/src/index.js +++ b/src/index.js @@ -17,6 +17,7 @@ export { Profile } from './profile.js' export { Persona } from './persona.js' export { Claim } from './claim.js' export { ServiceProvider } from './serviceProvider.js' +export * as ServiceProviderDefinitions from './serviceProviders/index.js' export * as proofs from './proofs.js' export * as openpgp from './openpgp.js' export * as asp from './asp.js' diff --git a/src/proofs.js b/src/proofs.js index e37cd94..deee758 100644 --- a/src/proofs.js +++ b/src/proofs.js @@ -16,7 +16,7 @@ limitations under the License. import { isNode } from 'browser-or-node' import * as fetcher from './fetcher/index.js' import { generateProxyURL } from './utils.js' -import { Fetcher, ProxyPolicy, ProofAccess } from './enums.js' +import { ProxyPolicy, ProofAccessRestriction } from './enums.js' /** * @module proofs @@ -29,20 +29,11 @@ import { Fetcher, ProxyPolicy, ProofAccess } from './enums.js' * choose the right approach to fetch the proof. An error will be thrown if no * approach is possible. * @async - * @param {object} data - Data from a claim definition + * @param {import('./serviceProvider.js').ServiceProvider} data - Data from a claim definition * @param {object} opts - Options to enable the request * @returns {Promise} */ export async function fetch (data, opts) { - switch (data.proof.request.fetcher) { - case Fetcher.HTTP: - data.proof.request.data.format = data.proof.request.format - break - - default: - break - } - if (isNode) { return handleNodeRequests(data, opts) } @@ -50,6 +41,11 @@ export async function fetch (data, opts) { return handleBrowserRequests(data, opts) } +/** + * @param {import('./serviceProvider.js').ServiceProvider} data - Data from a claim definition + * @param {object} opts - Options to enable the request + * @returns {Promise} + */ const handleBrowserRequests = (data, opts) => { switch (opts.proxy.policy) { case ProxyPolicy.ALWAYS: @@ -57,11 +53,11 @@ const handleBrowserRequests = (data, opts) => { case ProxyPolicy.NEVER: switch (data.proof.request.access) { - case ProofAccess.GENERIC: - case ProofAccess.GRANTED: + case ProofAccessRestriction.NONE: + case ProofAccessRestriction.GRANTED: return createDefaultRequestPromise(data, opts) - case ProofAccess.NOCORS: - case ProofAccess.SERVER: + case ProofAccessRestriction.NOCORS: + case ProofAccessRestriction.SERVER: throw new Error( 'Impossible to fetch proof (bad combination of service access and proxy policy)' ) @@ -71,13 +67,13 @@ const handleBrowserRequests = (data, opts) => { case ProxyPolicy.ADAPTIVE: switch (data.proof.request.access) { - case ProofAccess.GENERIC: + case ProofAccessRestriction.NONE: return createFallbackRequestPromise(data, opts) - case ProofAccess.NOCORS: + case ProofAccessRestriction.NOCORS: return createProxyRequestPromise(data, opts) - case ProofAccess.GRANTED: + case ProofAccessRestriction.GRANTED: return createFallbackRequestPromise(data, opts) - case ProofAccess.SERVER: + case ProofAccessRestriction.SERVER: return createProxyRequestPromise(data, opts) default: throw new Error('Invalid proof access value') @@ -88,6 +84,11 @@ const handleBrowserRequests = (data, opts) => { } } +/** + * @param {import('./serviceProvider.js').ServiceProvider} data - Data from a claim definition + * @param {object} opts - Options to enable the request + * @returns {Promise} + */ const handleNodeRequests = (data, opts) => { switch (opts.proxy.policy) { case ProxyPolicy.ALWAYS: @@ -104,13 +105,21 @@ const handleNodeRequests = (data, opts) => { } } +/** + * @param {import('./serviceProvider.js').ServiceProvider} data - Data from a claim definition + * @param {object} opts - Options to enable the request + * @returns {Promise} + */ const createDefaultRequestPromise = (data, opts) => { return new Promise((resolve, reject) => { - fetcher[data.proof.request.fetcher] + if (!(data.proof.request.protocol in fetcher)) { + reject(new Error(`fetcher for ${data.proof.request.protocol} not found`)) + } + fetcher[data.proof.request.protocol] .fn(data.proof.request.data, opts) .then((res) => { return resolve({ - fetcher: data.proof.request.fetcher, + protocol: data.proof.request.protocol, data, viaProxy: false, result: res @@ -122,12 +131,17 @@ const createDefaultRequestPromise = (data, opts) => { }) } +/** + * @param {import('./serviceProvider.js').ServiceProvider} data - Data from a claim definition + * @param {object} opts - Options to enable the request + * @returns {Promise} + */ const createProxyRequestPromise = (data, opts) => { return new Promise((resolve, reject) => { let proxyUrl try { proxyUrl = generateProxyURL( - data.proof.request.fetcher, + data.proof.request.protocol, data.proof.request.data, opts ) @@ -138,13 +152,13 @@ const createProxyRequestPromise = (data, opts) => { const requestData = { url: proxyUrl, format: data.proof.request.format, - fetcherTimeout: fetcher[data.proof.request.fetcher].timeout + fetcherTimeout: fetcher[data.proof.request.protocol].timeout } fetcher.http .fn(requestData, opts) .then((res) => { return resolve({ - fetcher: 'http', + protocol: 'http', data, viaProxy: true, result: res @@ -156,6 +170,11 @@ const createProxyRequestPromise = (data, opts) => { }) } +/** + * @param {import('./serviceProvider.js').ServiceProvider} data - Data from a claim definition + * @param {object} opts - Options to enable the request + * @returns {Promise} + */ const createFallbackRequestPromise = (data, opts) => { return new Promise((resolve, reject) => { createDefaultRequestPromise(data, opts) diff --git a/src/signatures.js b/src/signatures.js index 3f1bbf8..b8bd853 100644 --- a/src/signatures.js +++ b/src/signatures.js @@ -15,7 +15,7 @@ limitations under the License. */ import { readCleartextMessage, verify } from 'openpgp' import { Claim } from './claim.js' -import { fetchURI } from './keys.js' +import { fetchURI } from './openpgp.js' /** * @module signatures @@ -89,7 +89,7 @@ export async function process (signature) { if (sigKeys.length > 0) { try { result.key.uri = sigKeys[0] - result.key.data = await fetchURI(result.key.uri) + result.key.data = (await fetchURI(result.key.uri)).publicKey.key result.key.fetchMethod = result.key.uri.split(':')[0] } catch (e) {} } @@ -97,7 +97,7 @@ export async function process (signature) { if (!result.key.data && signersUserID) { try { result.key.uri = `wkd:${signersUserID}` - result.key.data = await fetchURI(result.key.uri) + result.key.data = (await fetchURI(result.key.uri)).publicKey.key result.key.fetchMethod = 'wkd' } catch (e) {} } @@ -106,7 +106,7 @@ export async function process (signature) { try { const match = preferredKeyServer.match(/^(.*:\/\/)?([^/]*)(?:\/)?$/i) result.key.uri = `hkp:${match[2]}:${issuerKeyID || signersUserID}` - result.key.data = await fetchURI(result.key.uri) + result.key.data = (await fetchURI(result.key.uri)).publicKey.key result.key.fetchMethod = 'hkp' } catch (e) { throw new Error('Public key not found') diff --git a/src/verifications.js b/src/verifications.js index c421507..7b682ce 100644 --- a/src/verifications.js +++ b/src/verifications.js @@ -176,7 +176,7 @@ const containsProof = async (data, params) => { /** * @function * @param {any} proofData - * @param {string} checkPath + * @param {string[]} checkPath * @param {object} params * @param {string} params.target * @param {string} params.claimFormat @@ -231,9 +231,9 @@ const runJSON = async (proofData, checkPath, params) => { /** * Run the verification by finding the formatted fingerprint in the proof * @async - * @param {object} proofData - The proof data - * @param {object} claimData - The claim data - * @param {string} fingerprint - The fingerprint + * @param {object} proofData - The proof data + * @param {import('./serviceProvider.js').ServiceProvider} claimData - The claim data + * @param {string} fingerprint - The fingerprint * @returns {Promise} */ export async function run (proofData, claimData, fingerprint) { @@ -243,10 +243,10 @@ export async function run (proofData, claimData, fingerprint) { errors: [] } - switch (claimData.proof.request.format) { + switch (claimData.proof.response.format) { case ProofFormat.JSON: - for (let index = 0; index < claimData.claim.length; index++) { - const claimMethod = claimData.claim[index] + for (let index = 0; index < claimData.proof.target.length; index++) { + const claimMethod = claimData.proof.target[index] try { res.result = res.result || await runJSON( proofData, @@ -265,8 +265,8 @@ export async function run (proofData, claimData, fingerprint) { res.completed = true break case ProofFormat.TEXT: - for (let index = 0; index < claimData.claim.length; index++) { - const claimMethod = claimData.claim[index] + for (let index = 0; index < claimData.proof.target.length; index++) { + const claimMethod = claimData.proof.target[index] try { res.result = res.result || await containsProof( proofData,