You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							185 lines
						
					
					
						
							5.2 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							185 lines
						
					
					
						
							5.2 KiB
						
					
					
				| import { IIdentifier } from "@veramo/core"; | |
| import { getRandomBytesSync } from "ethereum-cryptography/random"; | |
| import { entropyToMnemonic } from "ethereum-cryptography/bip39"; | |
| import { wordlist } from "ethereum-cryptography/bip39/wordlists/english"; | |
| import { HDNode } from "@ethersproject/hdnode"; | |
| import * as didJwt from "did-jwt"; | |
| import * as u8a from "uint8arrays"; | |
| 
 | |
| import { | |
|   createEndorserJwt, | |
|   ENDORSER_JWT_URL_LOCATION, | |
| } from "@/libs/endorserServer"; | |
| import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup"; | |
| 
 | |
| export const DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'"; | |
| 
 | |
| export const LOCAL_KMS_NAME = "local"; | |
| 
 | |
| /** | |
|  * | |
|  * | |
|  * @param {string} address | |
|  * @param {string} publicHex | |
|  * @param {string} privateHex | |
|  * @param {string} derivationPath | |
|  * @return {*}  {Omit<IIdentifier, 'provider'>} | |
|  */ | |
| export const newIdentifier = ( | |
|   address: string, | |
|   publicHex: string, | |
|   privateHex: string, | |
|   derivationPath: string, | |
| ): Omit<IIdentifier, keyof "provider"> => { | |
|   return { | |
|     did: DEFAULT_DID_PROVIDER_NAME + ":" + address, | |
|     keys: [ | |
|       { | |
|         kid: publicHex, | |
|         kms: LOCAL_KMS_NAME, | |
|         meta: { derivationPath: derivationPath }, | |
|         privateKeyHex: privateHex, | |
|         publicKeyHex: publicHex, | |
|         type: "Secp256k1", | |
|       }, | |
|     ], | |
|     provider: DEFAULT_DID_PROVIDER_NAME, | |
|     services: [], | |
|   }; | |
| }; | |
| 
 | |
| /** | |
|  * | |
|  * | |
|  * @param {string} mnemonic | |
|  * @return {*}  {[string, string, string, string]} | |
|  */ | |
| export const deriveAddress = ( | |
|   mnemonic: string, | |
|   derivationPath: string = DEFAULT_ROOT_DERIVATION_PATH, | |
| ): [string, string, string, string] => { | |
|   mnemonic = mnemonic.trim().toLowerCase(); | |
| 
 | |
|   const hdnode: HDNode = HDNode.fromMnemonic(mnemonic); | |
|   const rootNode: HDNode = hdnode.derivePath(derivationPath); | |
|   const privateHex = rootNode.privateKey.substring(2); // original starts with '0x' | |
|   const publicHex = rootNode.publicKey.substring(2); // original starts with '0x' | |
|   const address = rootNode.address; | |
| 
 | |
|   return [address, privateHex, publicHex, derivationPath]; | |
| }; | |
| 
 | |
| export const generateRandomBytes = (numBytes: number): Uint8Array => { | |
|   return getRandomBytesSync(numBytes); | |
| }; | |
| 
 | |
| /** | |
|  * | |
|  * | |
|  * @return {*}  {string} | |
|  */ | |
| export const generateSeed = (): string => { | |
|   const entropy: Uint8Array = getRandomBytesSync(32); | |
|   const mnemonic = entropyToMnemonic(entropy, wordlist); | |
| 
 | |
|   return mnemonic; | |
| }; | |
| 
 | |
| /** | |
|  * Retreive an access token | |
|  * | |
|  * @return {*} | |
|  */ | |
| export const accessToken = async (did?: string) => { | |
|   if (did) { | |
|     const nowEpoch = Math.floor(Date.now() / 1000); | |
|     const endEpoch = nowEpoch + 60; // add one minute | |
|     const tokenPayload = { exp: endEpoch, iat: nowEpoch, iss: did }; | |
|     return createEndorserJwt(did, tokenPayload); | |
|   } else { | |
|     return ""; | |
|   } | |
| }; | |
| 
 | |
| /** | |
|  *  Copied out of did-jwt since it's deprecated in that library. | |
|  * | |
|  *  The SimpleSigner returns a configured function for signing data. | |
|  * | |
|  *  @example | |
|  *  const signer = SimpleSigner(import.meta.env.PRIVATE_KEY) | |
|  *  signer(data, (err, signature) => { | |
|  *    ... | |
|  *  }) | |
|  * | |
|  *  @param    {String}         hexPrivateKey    a hex encoded private key | |
|  *  @return   {Function}                     a configured signer function | |
|  */ | |
| export function SimpleSigner(hexPrivateKey: string): didJwt.Signer { | |
|   const signer = didJwt.ES256KSigner(didJwt.hexToBytes(hexPrivateKey), true); | |
|   return async (data) => { | |
|     const signature = (await signer(data)) as string; | |
|     return fromJose(signature); | |
|   }; | |
| } | |
| 
 | |
| // from did-jwt/util; see SimpleSigner above | |
| export function fromJose(signature: string): { | |
|   r: string; | |
|   s: string; | |
|   recoveryParam?: number; | |
| } { | |
|   const signatureBytes: Uint8Array = didJwt.base64ToBytes(signature); | |
|   if (signatureBytes.length < 64 || signatureBytes.length > 65) { | |
|     throw new TypeError( | |
|       `Wrong size for signature. Expected 64 or 65 bytes, but got ${signatureBytes.length}`, | |
|     ); | |
|   } | |
|   const r = bytesToHex(signatureBytes.slice(0, 32)); | |
|   const s = bytesToHex(signatureBytes.slice(32, 64)); | |
|   const recoveryParam = | |
|     signatureBytes.length === 65 ? signatureBytes[64] : undefined; | |
|   return { r, s, recoveryParam }; | |
| } | |
| 
 | |
| // from did-jwt/util; see SimpleSigner above | |
| export function bytesToHex(b: Uint8Array): string { | |
|   return u8a.toString(b, "base16"); | |
| } | |
| 
 | |
| /** | |
|  @return results of uportJwtPayload: | |
|  { iat: number, iss: string (DID), own: { name, publicEncKey (base64-encoded key) } } | |
|  | |
|  Note that similar code is also contained in time-safari | |
|  */ | |
| export const getContactPayloadFromJwtUrl = (jwtUrlText: string) => { | |
|   let jwtText = jwtUrlText; | |
|   const endorserContextLoc = jwtText.indexOf(ENDORSER_JWT_URL_LOCATION); | |
|   if (endorserContextLoc > -1) { | |
|     jwtText = jwtText.substring( | |
|       endorserContextLoc + ENDORSER_JWT_URL_LOCATION.length, | |
|     ); | |
|   } | |
| 
 | |
|   // JWT format: { header, payload, signature, data } | |
|   const jwt = didJwt.decodeJWT(jwtText); | |
| 
 | |
|   return jwt.payload; | |
| }; | |
| 
 | |
| export const nextDerivationPath = (origDerivPath: string) => { | |
|   let lastStr = origDerivPath.split("/").slice(-1)[0]; | |
|   if (lastStr.endsWith("'")) { | |
|     lastStr = lastStr.slice(0, -1); | |
|   } | |
|   const lastNum = parseInt(lastStr, 10); | |
|   const newLastNum = lastNum + 1; | |
|   const newLastStr = newLastNum.toString() + (lastStr.endsWith("'") ? "'" : ""); | |
|   const newDerivPath = origDerivPath | |
|     .split("/") | |
|     .slice(0, -1) | |
|     .concat([newLastStr]) | |
|     .join("/"); | |
|   return newDerivPath; | |
| };
 | |
| 
 |