diff --git a/bun.lockb b/bun.lockb index 61cfb7d2..f1b6ba1f 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 5ba80d49..7bcb4801 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@othent/kms", - "version": "1.0.8", + "version": "1.0.10", "description": "Arweave Oauth Othent wallets enabled Google Key Management Service.", "main": "dist/index.js", "module": "dist/index.mjs", @@ -39,12 +39,16 @@ "homepage": "https://othent.io", "dependencies": { "@auth0/auth0-spa-js": "^2.1.2", + "@types/node-forge": "^1.3.11", "arweave": "^1.14.4", + "arweave-mnemonic-keys": "^0.0.9", "axios": "^1.6.0", "base64-js": "^1.5.1", "buffer": "^6.0.3", + "crypto": "^1.0.1", "jwk-to-pem": "^2.0.5", "jwt-decode": "^4.0.0", + "node-forge": "^1.3.1", "pem-jwk": "^2.0.0", "tmp-promise": "^3.0.3", "warp-arbundles": "^1.0.4" diff --git a/src/lib/mapping/connect.ts b/src/lib/mapping/connect.ts index 79e1fce5..c001ffb6 100644 --- a/src/lib/mapping/connect.ts +++ b/src/lib/mapping/connect.ts @@ -1,5 +1,6 @@ import { login } from "../auth/login"; import { createUser } from "../operations/createUser"; +import { createUserWithKey } from "../operations/createUserWithKey"; import { getJWT } from "../auth/getJWT"; import { userDetails } from "../auth/userDetails"; import { UserDetailsReturnProps } from "../../types/auth/userDetails"; @@ -8,11 +9,20 @@ import { UserDetailsReturnProps } from "../../types/auth/userDetails"; * Connect the users account, this is the same as login/signup in one function. * @returns The the users details. */ -export async function connect(): Promise { +export async function connect( + seedPhrase?: boolean, +): Promise { const user = await login(); if (user.authSystem === "KMS" && user.owner && user.walletAddress) { return user; + } else if (seedPhrase) { + const mnemonicSeedPhrase = await createUserWithKey(); + const userDetailsJWT = await getJWT(); + localStorage.setItem("id_token", JSON.stringify(userDetailsJWT)); + const userInfo = await userDetails(); + userInfo.mnemonicSeedPhrase = mnemonicSeedPhrase; + return userInfo; } else { await createUser(); const userDetailsJWT = await getJWT(); diff --git a/src/lib/operations/createUserWithKey.ts b/src/lib/operations/createUserWithKey.ts new file mode 100644 index 00000000..8cb70a7e --- /dev/null +++ b/src/lib/operations/createUserWithKey.ts @@ -0,0 +1,30 @@ +import { api } from "./api"; +import { encodeToken } from "../auth/encodeToken"; +import { generateKey, formatKey, encryptKey } from "../utils/kmsUtils"; + +export async function createUserWithKey(): Promise { + // TODO remove walletAddress later + const { mnemonic, JWK, walletAddress } = await generateKey(); + + const formattedKey = formatKey(JWK); + + const pem = "we need to ask the server here for import job key"; + + const encryptedKey = encryptKey(pem, formattedKey); + + const encodedData = await encodeToken({ importedKey: encryptedKey }); + + try { + const createUserRequest = ( + await api.post("/create-user", { encodedData }) + ).data.data; + + if (!createUserRequest) { + throw new Error("Error creating user with key on server."); + } + + return mnemonic; + } catch (e) { + throw new Error("Error creating user with key on server."); + } +} diff --git a/src/lib/utils/kmsUtils.ts b/src/lib/utils/kmsUtils.ts new file mode 100644 index 00000000..cd7ffcc9 --- /dev/null +++ b/src/lib/utils/kmsUtils.ts @@ -0,0 +1,69 @@ +// import { generateMnemonic, getKeyFromMnemonic } from 'arweave-mnemonic-keys' +import { ownerToAddress } from "./arweaveUtils"; +import JWktoPem from "jwk-to-pem"; +import forge from "node-forge"; +import crypto from "crypto"; + +interface JWKInterface { + kty: string; + e: string; + n: string; + d?: string | undefined; + p?: string | undefined; + q?: string | undefined; + dp?: string | undefined; + dq?: string | undefined; + qi?: string | undefined; +} + +export async function generateKey(): Promise<{ + mnemonic: string; + JWK: JWKInterface; + walletAddress: string; +}> { + // const mnemonic = await generateMnemonic() // while testing + // const JWK = await getKeyFromMnemonic(mnemonic) // while testing + + const mnemonic = + "crash buffalo kit mule arena try soup custom round enter enforce nasty"; + + const JWK = { + kty: "RSA", + e: "AQAB", + n: "jAmti34mF2QsgoGt6dkt0ZW3IC-CgHVNaSXbMLCeUYrVgnJ2Qw90kkD1kigHLCMLa82od387mm7oEbEf7eA1tOronVPN1cexulH2BUFr1dBRuLo8flYap9qu-kxarNKdzqimqaPhh0FUUhIcwacQgpLxLBNKYQUr_800DnFebsz_veQmKcdxOBWIE8iTknCJll-LYqu88opwDOjFkhNex98gLtKtwGCLrPlr_Fn54hTmKs1RJ8GIg100HPkQLS3RUZIoMu18yRdtohb0rcSt5cJSoa7TU2KvPU7ur6J93amxaNUGNEDUhU4r0-WlULTph73QhccLA7GeAaBfDIi2m2riNQap4BdN89m43l1-hEIn1jyvS9NRGZVIrL0uOOU91CHf4JZ5t7r5_atvxh2yTSXdWepZ4KO8bzanYX2b8xSQK_c5vKfA9tpWTVulXSOqHaBpPKgHsZhTv0Sz3KvKWWcyUHve_t115HWNM2fpMWcqsA_ySK5pasQUYCL8i3fg-ia_IWdV57L0uvebtoBjOUu8-DsbBaIEHsKPre8tjOy2ZnXt9hcKJ9jweM1AxkRB6LqnzgVpoW-4ktDGcmTDDZsXgAyj-eBP2DPgG2v923iRuY0-Y5FSyUcOc-KoRTjWSnWn9bsZnbIVXBuPHj3lfqbmYE1zeT_M7GgRtknSvNU", + d: "Io_07Du7TQSlU6SL9u5qN5Ma-m-fZvMMown72j2NJZT9c0-qzoxO-hXcRjFo68TOedHtZWjanhgHYO51cfFnb_qWZNYdNEkwHUtDTDLEWaYrtUsJySiZNZpsm0wjCQqGdk37rFzNOeOu2v6raERCd-eqbHVqYx2yqVTNJ09lvjUMXaKkpKUb1XrC9hcIDRmHa7yzGyxF2xq94wHEWXQ8bVotiEDpL1TkxFFfDI5sdRcxY5j_Ea_bcIidZxU1n-DyC47mKwOgS8VjgqDlzMmBHfbjDBKpB6iQlbIYiPKy2_WY2sXQ3S6vBSOw_4BrV8r-5Ei94XapykSZOc-KBf7sCnwtZyhUgZPBmDZhFCg4W50FpXZ3-GryUSZR9xfwKL8wmneY_lR2b7PzYNoqzS0zMN0PxdrJ2nRKWGjFGBhePMCpUzWBj0-Gm3vw_4p5XNRfvkbkLlL6hJZqCuV4c-7QHuJIXF0xSHtloq9Rs3ITIVdnj3ZlGWXXF-wvkS3oGPMGsaACrD0srhYcfu1GIAH5_H-ie8uaGB0xV9ZTeN4iRiBXVXcMdjgqWwecCEk2c3N9fhipZROdhD2WL-0Ve9NYLDMPs9LefWU_GPjwpjuVhs9eC1hZqXZaa1adXZoFmlcA0OAunNb3w6Efnf-IMtoIPqAaaSKP1nPJniOL0kZe2jU", + p: "yJ0D1ZauCeFLu8pWV-lbQPHta5FnbvjMHd4RPzFJHLw1RiAWCDNLa-FAJnDr09kfZWL12HOUbh5ylnli9j01RM3PFW3LlwYxjWzkViciOghfvUGVo-TlGA9JAC_wLQtQ7ZGGOp-Czs1car5t4NZLJTsQwWggSn8Xj5HZKxlj9kTjEUaFOU2baWhPxH8bATGaPw9kdfTCHNe2wjoSvDSRBkoc_wen_bL-5GUii3Nj9rETMKtJ_vgrB_5MkK4aIisILVISUfDT7ix9CtMFCtWnFJgwM8r5NzW9Ia40WuMox_POcW4_cKVhym-Eora_Ey1uNTSDgedx53AUCBEzpfNBow", + q: "srNLZ78TuVVcKgDJfjD2dqRbj11wXcftm9URf72mX2Dpamvcw-qHjoeQNt0WpvfNgSkS2m5TdLTQXzPJ_fSq7oJ4wX9jzxjENyx-KKFcbB2PoY-dqPGhrK785h5C49Df865kFWGk6Vo8Br_6J9WUWE0M8L2ZNJG-4wqFnZMeuGyFIghw4T3aIZeblgt6btAm2asR3JM0arQjIL4z_4hvUN4NwmUjNskD-_NGGAoNxcGOTLqBgi8uiJvxJRaCoaZyXJGlFS1b-6h4_lMSOqt7hKzoQc4reLVvBBzObNxrG1qMlljwLSorpwXQZSM6FK-6l_KjYCVoKglHxyWAJvsfJw", + dp: "BuMtIIYdz4UGnpnhwP7n_SDRL-I8FNlB2LypBuxgQDZN9exgFUP9kOSY2TkDP7CynT56hkXhkK5G7NeaCC4tyADw3SF53eN-jAZzCGoriKaE8vBvfML9AohzzyfWLRW4X4-hdh3H1eXDCH8lMpTo24xdlOZIRYZ7fphZRluGzQusaAltxXvenA3Sv0JF4RKc4xFaN5qBl4_oXF62CfQcDoU43aCbqeAdVCYMu5Dom62UpRUcYz6N66ZVZpozl3y2uSeaLWoBPKoWWOrJv4d6RwC6luyfBcA4kBF1BrHJ0qOSMN7CGtHyu4p8mUGA6d2jbPwyj6Esje-RIH3GsinZ1Q", + dq: "AfdJABYxEFvpDKk_jjzZqUp7m2Mqxk1ZxtocPCyI9Qmq57nSDvG4lg_VvVTHWATn5ODfzTljf6gxlqqKMVoMu10CGRbesTuThQvfQ3ErBCF7IArlcCNgZPbz31A76ie1HwgvH7EYUFzmxig8h02pOSN33fooAlUMJZFLEQW6U8sV198B5uh4SuBRHOB5c1ik5MYmBygzbm0W6dhN2CTXHKukuRvFvGePvKjbf440wpUzjJKMtDLyrxwQFhleTIr5PR15FHeQSH98_UdKrwyN6lLAp2CuR8CvPPtYJghywTVI0Kqf0c9h9Y9x0HsHu4yHX_6SX49pyvSZLo6yDzu6Bw", + qi: "DXqJxy0a7Yriv8JxytDLToL_4_Q-XE035dbHFPrcbpT9PNXP0uvJ7NM66GfigBuHIcobfZfyIXmmRlVDtKZ-DD0lsOSLjNOItZsqsA7jyRyUK9ZXucWO_TflQuOvi17LOgRHAyKuBPNGjZ4fHvnwkt6O7cvCtMtfSGnpeQ1Lmh2Np_ZjXln8E3s2iH-0aL4y_HsBFs91frOzTgp9Y7yxko0EXnxtWC_31VuuqdlcMRIOXKAf9C8_Bg1xcQS-uxT2pnI-wClR27lMnC-sLLIW1Th6I1FxMFx526TF2Urd_RayRawyydswk_gj3k9CUgrDuN0jrTenGAZ67wqIQ660OA", + }; + + const walletAddress = await ownerToAddress(JWK.n); + + return { mnemonic, JWK, walletAddress }; +} + +export function formatKey(JWK: any) { + const pemKey = JWktoPem(JWK, { private: true }); + + const privateKey = forge.pki.privateKeyFromPem(pemKey); + const der = forge.asn1 + .toDer(forge.pki.privateKeyToAsn1(privateKey)) + .getBytes(); + + const derBuffer = Buffer.from(der, "binary"); + + return derBuffer; +} + +export function encryptKey(jobPem: any, targetKey: any) { + return crypto.publicEncrypt( + { + key: jobPem, + oaepHash: "sha256", + padding: crypto.constants.RSA_PKCS1_OAEP_PADDING, + }, + targetKey, + ); +} diff --git a/src/types/auth/userDetails.ts b/src/types/auth/userDetails.ts index eddfa071..a02d4d06 100644 --- a/src/types/auth/userDetails.ts +++ b/src/types/auth/userDetails.ts @@ -19,4 +19,5 @@ export interface UserDetailsReturnProps { sid?: string; nonce?: string; data?: any; + mnemonicSeedPhrase?: any; }