Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | 1x 1x 1x 1x 8x 8x 8x 8x 8x 8x 8x 8x 8x 1x 7x 1x 20x 20x 20x 20x 20x 20x 20x 20x 20x 20x 20x 20x 20x 20x 20x 8x 4x 20x 20x 20x 20x 20x 1x 11x 11x 11x 11x 11x 11x 11x 11x 11x | import { ECPair } from 'bitcoinjs-lib' import { decodeToken, SECP256K1Client, TokenSigner, TokenVerifier } from 'jsontokens' import { TokenInterface } from 'jsontokens/lib/decode' import { nextYear, makeUUID4, ecPairToAddress } from '../utils' /** * Signs a profile token * @param {Object} profile - the JSON of the profile to be signed * @param {String} privateKey - the signing private key * @param {Object} subject - the entity that the information is about * @param {Object} issuer - the entity that is issuing the token * @param {String} signingAlgorithm - the signing algorithm to use * @param {Date} issuedAt - the time of issuance of the token * @param {Date} expiresAt - the time of expiration of the token * @returns {Object} - the signed profile token * */ export function signProfileToken(profile: any, privateKey: string, subject?: any, issuer?: any, signingAlgorithm = 'ES256K', issuedAt = new Date(), expiresAt = nextYear()) { Iif (signingAlgorithm !== 'ES256K') { throw new Error('Signing algorithm not supported') } const publicKey = SECP256K1Client.derivePublicKey(privateKey) Eif (!subject) { subject = { publicKey } } Eif (!issuer) { issuer = { publicKey } } const tokenSigner = new TokenSigner(signingAlgorithm, privateKey) const payload = { jti: makeUUID4(), iat: issuedAt.toISOString(), exp: expiresAt.toISOString(), subject, issuer, claim: profile } return tokenSigner.sign(payload) } /** * Wraps a token for a profile token file * @param {String} token - the token to be wrapped * @returns {Object} - including `token` and `decodedToken` */ export function wrapProfileToken(token: string) { return { token, decodedToken: decodeToken(token) } } /** * Verifies a profile token * @param {String} token - the token to be verified * @param {String} publicKeyOrAddress - the public key or address of the * keypair that is thought to have signed the token * @returns {Object} - the verified, decoded profile token * @throws {Error} - throws an error if token verification fails */ export async function verifyProfileToken(token: string, publicKeyOrAddress: string): Promise<TokenInterface> { const decodedToken = decodeToken(token) const payload = decodedToken.payload Iif (typeof payload === 'string') { throw new Error('Unexpected token payload type of string') } // Inspect and verify the subject Eif (payload.hasOwnProperty('subject')) { Iif (!payload.subject.hasOwnProperty('publicKey')) { throw new Error('Token doesn\'t have a subject public key') } } else { throw new Error('Token doesn\'t have a subject') } // Inspect and verify the issuer Eif (payload.hasOwnProperty('issuer')) { Iif (!payload.issuer.hasOwnProperty('publicKey')) { throw new Error('Token doesn\'t have an issuer public key') } } else { throw new Error('Token doesn\'t have an issuer') } // Inspect and verify the claim Iif (!payload.hasOwnProperty('claim')) { throw new Error('Token doesn\'t have a claim') } const issuerPublicKey = payload.issuer.publicKey as string const publicKeyBuffer = Buffer.from(issuerPublicKey, 'hex') const compressedKeyPair = ECPair.fromPublicKey(publicKeyBuffer, { compressed: true }) const compressedAddress = await ecPairToAddress(compressedKeyPair) const uncompressedKeyPair = ECPair.fromPublicKey(publicKeyBuffer, { compressed: false }) const uncompressedAddress = await ecPairToAddress(uncompressedKeyPair) if (publicKeyOrAddress === issuerPublicKey) { // pass } else if (publicKeyOrAddress === compressedAddress) { // pass } else Eif (publicKeyOrAddress === uncompressedAddress) { // pass } else { throw new Error('Token issuer public key does not match the verifying value') } const tokenVerifier = new TokenVerifier(decodedToken.header.alg, issuerPublicKey) Iif (!tokenVerifier) { throw new Error('Invalid token verifier') } const tokenVerified = await tokenVerifier.verify(token) Iif (!tokenVerified) { throw new Error('Token verification failed') } return decodedToken } /** * Extracts a profile from an encoded token and optionally verifies it, * if `publicKeyOrAddress` is provided. * @param {String} token - the token to be extracted * @param {String} publicKeyOrAddress - the public key or address of the * keypair that is thought to have signed the token * @returns {Object} - the profile extracted from the encoded token * @throws {Error} - if the token isn't signed by the provided `publicKeyOrAddress` */ export async function extractProfile(token: string, publicKeyOrAddress: string | null = null): Promise<any> { let decodedToken Eif (publicKeyOrAddress) { decodedToken = await verifyProfileToken(token, publicKeyOrAddress) } else { decodedToken = decodeToken(token) } let profile = {} Eif (decodedToken.hasOwnProperty('payload')) { const payload = decodedToken.payload Iif (typeof payload === 'string') { throw new Error('Unexpected token payload type of string') } Eif (payload.hasOwnProperty('claim')) { profile = payload.claim } } return profile } |