基于区块链的毕业设计TITLE_OF_PACKAGE!! – 包装的标题!!

本文提供基于区块链的毕业设计国外最新区块链项目源码下载,包括solidity,eth,fabric等blockchain区块链,基于区块链的毕业设计TITLE_OF_PACKAGE!! – 包装的标题!! 是一篇很好的国外资料

TITLE_OF_PACKAGE!!

Encryption is scary and hard, knowing your doing the correct things with certain keys is hard to know. Also on top of this the mass amount of ethereum wallets sometimes add bad UX/UI and disjoint the user from your dApps, you may want to create a more integrated approach. On top of this, this package follows a web2 approach of a standard username and password making it simple for the user to use your application. Also creating something which is fully onchain is expensive currently due to transaction per each save state and encrypted keys themselives should be protected at least from people being able to just get a list and try to brute force it. This is a way you can do best of both worlds without you holding anyone’s keys.

The aim of this package is to abstract all this :head_bandage: away for you and give you simple tools to allow you to create none custodial ethereum wallet logins, without worrying about how the encryption links together or the scary thought of saving peoples private keys.

All the decryption will happen on the client side and you will just need a server which stores the encrypted data and returns it. All of this data is useless without the keys generated client side.

Just to note using this kind of encryption is as safe as the username and password the user gives. The package does not force max contraints on the usage, it is up to you to decide how you want to force the user to make sure they enter a strong password and username. We strongly advise you force a strong password on creation of wallets to protect your user. We would not promote this approach for long term storing of crypto either, you can see this logic more as a hot/burner wallet aka your users can use this to interact with your dApp easily and safely. Supporting them without an extension just the native browser.

This model is based on the same model LastPass use for their none custodial password manager. It is also heavily influenced by how the none custodial FunWallet works.

Install package

NPM

$ npm install PACKAGE_NAME

YARN

$ yarn PACKAGE_NAME

Usage and flows

This will walk you through the usage of the package with flow diagrams explaining the flows.

New wallet

Creating a new user.

Flow

TITLE_OF_PACKAGE!! - 包装的标题!!

Code example this includes client + server.

Client
import { createNewWallet } from 'ethereum-web2-encryption';  // They have just clicked the register button after entering // their username and password export const register = async (username: string, password: string) => {   const encryptedWallet = await createWallet(username, password);   console.log(encryptedWallet);   // {   //    wallet: {   //        ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8',   //        privateKey: '0x602cbc76611ae50bcff99beacb4ab8e84853830f3036da946a8473107c4056e8',   //    },   //    signature: '0xf09eb344c7cbe4aebd7c3d2109eeddd5a3f1ec6a445a26ed1c46f47bce902a274af03b86f19557026055467a796a7e76be4c1fdd19132fd102097abe3124af081c',   //    userAuthenticationToken: '0xace36d94ae1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c84e7',   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    const request = {     username,     ethereumAddress: encryptedWallet.ethereumAddress,     signature: encryptedWallet.signature,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,     encryptedKeyInfo: encryptedWallet.encryptedKeyInfo,   };    // look at server part below to see what your server is expected to do   await fetch('YOUR_SERVER_API_REGISTER_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    // done user is registered! };
Server
import {   verifyEthereumAddress,   hashAuthenticationTokenOnServer, } from 'ethereum-web2-encryption'; import db from 'YOUR_DB';  interface RegisterRequest {   username: string;   ethereumAddress: string;   signature: string;   userAuthenticationToken: string;   encryptedKeyInfo: {     key: string;     iv: string;   }; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const register = async (registerInfo: RegisterRequest) => {   const userExists = await db.userExists(registerInfo.username);   if (userExists) {     throw new Error('Username already exists');   }    const ethereumAddressExists = await db.ethereumAddressExists(     registerInfo.ethereumAddress   );   if (ethereumAddressExists) {     throw new Error('Ethereum address already exists');   }    const ownsEthereumAddress = await verifyEthereumAddress(     registerInfo.signature,     registerInfo.encryptedKeyInfo,     registerInfo.ethereumAddress   );   if (!ownsEthereumAddress) {     throw new Error(       'You do not own the ethereum address so can not register you'     );   }    const serverAuthHashResult = await hashAuthenticationTokenOnServer(     registerInfo.userAuthenticationToken   );    await db.createNewUser({     username: registerInfo.username,     ethereumAddress: registerInfo.ethereumAddress,     serverAuthenticationHash: serverAuthHashResult.serverAuthenticationHash,     salt: serverAuthHashResult.salt,     encryptedPk: registerInfo.encryptedKeyInfo.key,     encryptedPkIv: registerInfo.encryptedKeyInfo.iv,   });    // done user is registered! };

Login existing wallet

User logging into an already created account.

Flow

TITLE_OF_PACKAGE!! - 包装的标题!!

Client
import {   getAuthenticationToken,   decryptWallet, } from 'ethereum-web2-encryption';  // They have just clicked the login button after entering // their username and password export const login = async (username: string, password: string) => {   const authenticationToken = await getAuthenticationToken(username, password);    const request = {     username,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,   };    // look at server part below to see what your server is expected to do   const response = await fetch('YOUR_SERVER_API_LOGIN_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    const encryptedWallet = await response.json();   console.log(encryptedWallet);   // {   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    const decryptedWallet = await decryptWallet(     username,     password,     encryptedWallet.encryptedKeyInfo   );   console.log(decryptedWallet);   // {   //    ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8',   //    privateKey: '0x602cbc76611ae50bcff99beacb4ab8e84853830f3036da946a8473107c4056e8',   //}    // done user is logged in! };
Server
import {   verifyEthereumAddress,   hashAuthenticationTokenOnServer, } from 'ethereum-web2-encryption'; import db from 'YOUR_DB';  interface LoginRequest {   username: string;   userAuthenticationToken: string; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const login = async (loginRequest: LoginRequest) => {   const userAuthenticationInfo = await db.userAuthenticationInfo(     registerInfo.username   );   if (!userAuthenticationInfo) {     throw new Error('User does not exists');   }    const serverHashMatchesClientHash = await serverHashMatchesClientHash(     userAuthenticationInfo.salt,     loginRequest.userAuthenticationToken,     userAuthenticationInfo.serverAuthenticationHash   );   console.log(serverHashMatchesClientHash);   // {   //    salt: '0x2e7199cd889426be35d730aabc3fa073',   //    serverAuthenticationHash: '0xf06e83e0086d2546cc7730eeee08bc739daa2af80fb34691ebc0a0964b96eb34',   //}   if (!serverHashMatchesClientHash) {     throw new Error('Incorrect login.');   }    return {     encryptedKeyInfo: {       key: userAuthenticationInfo.encryptedPk,       iv: userAuthenticationInfo.encryptedPkIv,     },   }; };

Change password

Flow

TITLE_OF_PACKAGE!! - 包装的标题!!

Client
import {   getAuthenticationToken,   changePassword, } from 'ethereum-web2-encryption';  // They have just clicked the change password and entered their username and password // to confirm they want to do it export const getEncryptedInformation = async (   username: string,   password: string ) => {   const authenticationToken = await getAuthenticationToken(username, password);    const request = {     username,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,   };    // look at server part below to see what your server is expected to do   const response = await fetch('YOUR_SERVER_API_GET_ENCRYPTED_INFO_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    const encryptedWallet = await response.json();   console.log(encryptedWallet);   // {   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    // the user now needs to enter their new password, you should hold the   // the encryptedKeyInfo somewhere ready for the next method below   // below method should show you what the next steps are };  interface ChangePassword {   username: string;   oldPassword: string;   newPassword: string;   encryptedKeyInfo: { key: string; iv: string }; }  // They have just clicked entered their new password and pressed enter export const changePassword = async (changePasswordRequest: ChangePassword) => {   const authenticationToken = await getAuthenticationToken(     changePasswordRequest.username,     changePasswordRequest.newPassword   );    const encryptedWallet = await changePassword(     changePasswordRequest.username,     {       oldPassword: changePasswordRequest.oldPassword,       newPassword: changePasswordRequest.newPassword,     },     changePasswordRequest.encryptedKeyInfo   );   console.log(encryptedWallet);   // {   //    wallet: {   //        ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8',   //        privateKey: '0x602cbc76611ae50bcff99beacb4ab8e84853830f3036da946a8473107c4056e8',   //    },   //    signature: '0xf09eb344c7cbe4aebd7c3d2109eeddd5a3f1ec6a445a26ed1c46f47bce902a274af03b86f19557026055467a796a7e76be4c1fdd19132fd102097abe3124af081c',   //    userAuthenticationToken: '0xace36d94ae1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c84e7',   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    // TODO LOOK AT FLOW OF PASSING OLD AUTHENTICATION TOKEN IN?!?!   const request = {     username,     ethereumAddress: encryptedWallet.ethereumAddress,     signature: encryptedWallet.signature,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,     encryptedKeyInfo: encryptedWallet.encryptedKeyInfo,   };   // look at server part below to see what your server is expected to do   await fetch('YOUR_SERVER_API_CHANGE_PASSWORD_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    // change password is done! };
Server
import {   verifyEthereumAddress,   hashAuthenticationTokenOnServer,   serverHashMatchesClientHash, } from 'ethereum-web2-encryption'; import db from 'YOUR_DB';  interface EncryptedInfoRequest {   username: string;   userAuthenticationToken: string; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const encryptedInfo = async (   encryptedInfoRequest: EncryptedInfoRequest ) => {   const encryptedInfo = await db.userAuthenticationInfo(     encryptedInfoRequest.username   );   if (!encryptedInfo) {     throw new Error('User does not exists');   }    const serverHashMatchesClientHash = await serverHashMatchesClientHash(     encryptedInfo.salt,     encryptedInfoRequest.userAuthenticationToken,     encryptedInfo.serverAuthenticationHash   );   if (!serverHashMatchesClientHash) {     throw new Error(       '401 > this does not match the user auth token (wrong username + password).'     );   }    return {     encryptedKeyInfo: {       key: userAuthenticationInfo.encryptedPk,       iv: userAuthenticationInfo.encryptedPkIv,     },   }; };  interface ChangePasswordRequest {   username: string;   ethereumAddress: string;   signature: string;   userAuthenticationToken: string;   encryptedKeyInfo: {     key: string;     iv: string;   }; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const changePassword = async (   changePasswordInfo: ChangePasswordRequest ) => {   const userExists = await db.userExists(changePasswordInfo.username);   if (!userExists) {     throw new Error('Username doesnt exists');   }    const ownsEthereumAddress = await verifyEthereumAddress(     changePasswordInfo.signature,     changePasswordInfo.encryptedKeyInfo,     changePasswordInfo.ethereumAddress   );   if (!ownsEthereumAddress) {     throw new Error(       'You do not own the ethereum address so can not register you'     );   }    const serverAuthHashResult = await hashAuthenticationTokenOnServer(     changePasswordInfo.userAuthenticationToken   );   console.log(serverAuthHashResult);   // {   //    salt: '0x2e7199cd889426be35d730aabc3fa073',   //    serverAuthenticationHash: '0xf06e83e0086d2546cc7730eeee08bc739daa2af80fb34691ebc0a0964b96eb34',   //}    await db.updateUser({     username: changePasswordInfo.username,     serverAuthenticationHash: serverAuthHashResult.serverAuthenticationHash,     salt: serverAuthHashResult.salt,     encryptedPk: changePasswordInfo.encryptedKeyInfo.key,     encryptedPkIv: changePasswordInfo.encryptedKeyInfo.iv,   });    // done user has changed password! };

Change username

Flow

TITLE_OF_PACKAGE!! - 包装的标题!!

Client
import {   getAuthenticationToken,   changeUsername, } from 'ethereum-web2-encryption';  // They have just clicked the change password and entered their username and password // to confirm they want to do it export const getEncryptedInformation = async (   username: string,   password: string ) => {   const authenticationToken = await getAuthenticationToken(username, password);    const request = {     username,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,   };    // look at server part below to see what your server is expected to do   const response = await fetch('YOUR_SERVER_API_GET_ENCRYPTED_INFO_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    const encryptedWallet = await response.json();   console.log(encryptedWallet);   // {   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    // the user now needs to enter their new password, you should hold the   // the encryptedKeyInfo somewhere ready for the next method below   // below method should show you what the next steps are };  interface ChangeEmailRequest {   oldUsername: string;   newUsername: string;   password: string;   encryptedKeyInfo: { key: string; iv: string }; }  // They have just clicked change email entered their new password and pressed enter export const changeEmail = async (changeEmailRequest: ChangeEmailRequest) => {   const authenticationToken = await getAuthenticationToken(     changeEmailRequest.oldUsername,     changeEmailRequest.newPassword   );    const encryptedWallet = await changeUsername(     {       oldUsername: changeEmailRequest.oldUsername,       newUsername: changeEmailRequest.newUsername,     },     changeEmailRequest.password,     changeEmailRequest.encryptedKeyInfo   );   console.log(encryptedWallet);   // {   //    wallet: {   //        ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8',   //        privateKey: '0x602cbc76611ae50bcff99beacb4ab8e84853830f3036da946a8473107c4056e8',   //    },   //    signature: '0xf09eb344c7cbe4aebd7c3d2109eeddd5a3f1ec6a445a26ed1c46f47bce902a274af03b86f19557026055467a796a7e76be4c1fdd19132fd102097abe3124af081c',   //    userAuthenticationToken: '0xace36d94ae1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c84e7',   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    // TODO LOOK AT FLOW OF PASSING OLD AUTHENTICATION TOKEN IN?!?!   const request = {     oldUsername,     newUsername,     ethereumAddress: encryptedWallet.ethereumAddress,     signature: encryptedWallet.signature,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,     encryptedKeyInfo: encryptedWallet.encryptedKeyInfo,   };   // look at server part below to see what your server is expected to do   await fetch('YOUR_SERVER_API_CHANGE_USERNAME_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    // change username is done! };
Server
import {   verifyEthereumAddress,   hashAuthenticationTokenOnServer,   serverHashMatchesClientHash, } from 'ethereum-web2-encryption'; import db from 'YOUR_DB';  interface EncryptedInfoRequest {   username: string;   userAuthenticationToken: string; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const encryptedInfo = async (   encryptedInfoRequest: EncryptedInfoRequest ) => {   const encryptedInfo = await db.userAuthenticationInfo(     encryptedInfoRequest.username   );   if (!encryptedInfo) {     throw new Error('User does not exists');   }    const serverHashMatchesClientHash = await serverHashMatchesClientHash(     encryptedInfo.salt,     encryptedInfoRequest.userAuthenticationToken,     encryptedInfo.serverAuthenticationHash   );   if (!serverHashMatchesClientHash) {     throw new Error(       '401 > this does not match the user auth token (wrong username + password).'     );   }    return {     encryptedKeyInfo: {       key: userAuthenticationInfo.encryptedPk,       iv: userAuthenticationInfo.encryptedPkIv,     },   }; };  interface ChangeUsernameRequest {   oldUsername: string;   newUsername: string;   ethereumAddress: string;   signature: string;   userAuthenticationToken: string;   encryptedKeyInfo: {     key: string;     iv: string;   }; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const changeUsername = async (   changeUsernameInfo: ChangeUsernameRequest ) => {   const userExists = await db.userExists(changeUsernameInfo.oldUsername);   if (!userExists) {     throw new Error('Username does not exists');   }    const newUserExists = await db.userExists(changeUsernameInfo.newUsername);   if (newUserExists) {     throw new Error('Username already exists');   }    const ownsEthereumAddress = await verifyEthereumAddress(     changeUsernameInfo.signature,     changeUsernameInfo.encryptedKeyInfo,     changeUsernameInfo.ethereumAddress   );   if (!ownsEthereumAddress) {     throw new Error(       'You do not own the ethereum address so can not register you'     );   }    const serverAuthHashResult = await hashAuthenticationTokenOnServer(     changeUsernameInfo.userAuthenticationToken   );   console.log(serverAuthHashResult);   // {   //    salt: '0x2e7199cd889426be35d730aabc3fa073',   //    serverAuthenticationHash: '0xf06e83e0086d2546cc7730eeee08bc739daa2af80fb34691ebc0a0964b96eb34',   //}    await db.updateUser({     oldUsername: changeUsernameInfo.oldUsername,     newUsername: changeUsernameInfo.newUsername,     serverAuthenticationHash: serverAuthHashResult.serverAuthenticationHash,     salt: serverAuthHashResult.salt,     encryptedPk: changeUsernameInfo.encryptedKeyInfo.key,     encryptedPkIv: changeUsernameInfo.encryptedKeyInfo.iv,   });    // done user has changed username! };

Recovery

Ability to recover is critical on something which holds real funds. This exposes some easy to call methods to allow you to support this but still remain none custodial.

Generated offline recovery code

Flow

TITLE_OF_PACKAGE!! - 包装的标题!!

Client
import {   getAuthenticationToken,   generateOfflineRecoveryCode, } from 'ethereum-web2-encryption';  // They have just clicked the change password and entered their username and password // to confirm they want to do it export const getEncryptedInformation = async (   username: string,   password: string ) => {   const authenticationToken = await getAuthenticationToken(username, password);    const request = {     username,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,   };    // look at server part below to see what your server is expected to do   const response = await fetch('YOUR_SERVER_API_GET_ENCRYPTED_INFO_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    const encryptedWallet = await response.json();   console.log(encryptedWallet);   // {   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    // the user now needs to enter their new password, you should hold the   // the encryptedKeyInfo somewhere ready for the next method below   // below method should show you what the next steps are };  interface GenerateOfflineRecoveryCodeRequest {   username: string;   password: string;   encryptedKeyInfo: { key: string; iv: string }; }  // They have just clicked change email entered their new password and pressed enter export const generateOfflineRecoveryCode = async (   offlineRecoveryRequest: GenerateOfflineRecoveryCodeRequest ) => {   const authenticationToken = await getAuthenticationToken(     offlineRecoveryRequest.username,     offlineRecoveryRequest.password   );    const generateRecoveryCodeResponse = await generateOfflineRecoveryCode(     offlineRecoveryRequest.username,     offlineRecoveryRequest.password,     offlineRecoveryRequest.encryptedKeyInfo   );   console.log(generateRecoveryCodeResponse);   // {   //    offlineRecoveryCode: {   //       userCode: '0x975d73660b0b2fc1823668769ba0a88ba3d39eece71d15abc25c2fd2e95f042822f85bba3fb98b3d6d7aea326e361ba5a12c494a2ef33d05e1617adb5e26a0e6362bd43d7da0a0e4963fbde594b3b9ed',   //       id: '362bd43d7da0a0e4963fbde594b3b9ed'     }   //    ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8',   //    signature: '0xf09eb344c7cbe4aebd7c3d2109eeddd5a3f1ec6a445a26ed1c46f47bce902a274af03b86f19557026055467a796a7e76be4c1fdd19132fd102097abe3124af081c',   //    userAuthenticationToken: '0xace36d94ae1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c84e7',   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    const request = {     username,     recoveryCodeId: generateRecoveryCodeResponse.offlineRecoveryCode.id,     ethereumAddress: encryptedWallet.ethereumAddress,     signature: encryptedWallet.signature,     userRecoveryCodeAuthenticationToken:       encryptedWallet.userRecoveryCodeAuthenticationToken,     encryptedKeyInfo: encryptedWallet.encryptedKeyInfo,   };   // look at server part below to see what your server is expected to do   await fetch('YOUR_SERVER_API_SAVE_RECOVERY_OFFLINE_CODE_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    // generate offline recovery code is done! };
Server
import {   verifyEthereumAddress,   hashAuthenticationTokenOnServer,   serverHashMatchesClientHash, } from 'ethereum-web2-encryption'; import db from 'YOUR_DB';  interface EncryptedInfoRequest {   username: string;   userAuthenticationToken: string; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const encryptedInfo = async (   encryptedInfoRequest: EncryptedInfoRequest ) => {   const encryptedInfo = await db.userAuthenticationInfo(     encryptedInfoRequest.username   );   if (!encryptedInfo) {     throw new Error('User does not exists');   }    const serverHashMatchesClientHash = await serverHashMatchesClientHash(     encryptedInfo.salt,     encryptedInfoRequest.userAuthenticationToken,     encryptedInfo.serverAuthenticationHash   );   if (!serverHashMatchesClientHash) {     throw new Error(       '401 > this does not match the user auth token (wrong username + password).'     );   }    return {     encryptedKeyInfo: {       key: userAuthenticationInfo.encryptedPk,       iv: userAuthenticationInfo.encryptedPkIv,     },   }; };  interface OfflineRecoveryCodeRequest {   username: string;   recoveryCodeId: string;   ethereumAddress: string;   signature: string;   userRecoveryCodeAuthenticationToken: string;   encryptedKeyInfo: {     key: string;     iv: string;   }; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const saveGeneratedOfflineRecoveryCode = async (   offlineRecoveryCodeRequest: OfflineRecoveryCodeRequest ) => {   const userExists = await db.userExists(offlineRecoveryCodeRequest.username);   if (!userExists) {     throw new Error('Username does not exists');   }    const recoveryCodeExists = await db.recoveryCodeExists(     offlineRecoveryCodeRequest.recoveryCodeId   );   if (recoveryCodeExists) {     throw new Error('Recovery code id already exists');   }    const ownsEthereumAddress = await verifyEthereumAddress(     offlineRecoveryCodeRequest.signature,     offlineRecoveryCodeRequest.encryptedKeyInfo,     offlineRecoveryCodeRequest.ethereumAddress   );   if (!ownsEthereumAddress) {     throw new Error(       'You do not own the ethereum address so can not register you'     );   }    const serverAuthHashResult = await hashAuthenticationTokenOnServer(     offlineRecoveryCodeRequest.userRecoveryCodeAuthenticationToken   );   console.log(serverAuthHashResult);   // {   //    salt: '0x2e7199cd889426be35d730aabc3fa073',   //    serverAuthenticationHash: '0xf06e83e0086d2546cc7730eeee08bc739daa2af80fb34691ebc0a0964b96eb34',   //}    await db.saveOfflineRecoveryCode({     username: changeUsernameInfo.username,     recoveryCodeId: offlineRecoveryCodeRequest.recoveryCodeId,     serverAuthenticationHash: serverAuthHashResult.serverAuthenticationHash,     salt: serverAuthHashResult.salt,     encryptedPk: offlineRecoveryCodeRequest.encryptedKeyInfo.key,     encryptedPkIv: offlineRecoveryCodeRequest.encryptedKeyInfo.iv,   });    // done - user has saved the offline recovery code! };

Recover using offline codes

If the user wants to recover remember you got their recovery encrypted data on the server.

Flow

TITLE_OF_PACKAGE!! - 包装的标题!!

Recovery authentication token

Firstly you need to generate the recovery authentication token (hash of the recovery_master_key):

import { getRecoveryAuthenticationToken } from 'ethereum-web2-encryption'; ... const recoveryAuthenticationToken = await getRecoveryAuthenticationToken(   'THE_USERSNAME',   'OFFLINE_RECOVERY_CODE' ); console.log(recoveryAuthenticationToken); // 0xace36d94ae1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c84e7

you then as explained above – “ends the server the userRecoveryCodeAuthenticationToken which if matches that username you got mapped returns the encryptedKeyInfo for that token”. At this point you got the recoveryEncryptedKeyInfo

import { recoverWithOfflineCode } from 'ethereum-web2-encryption'; ... const recoveryCodeResponse = await recoverWithOfflineCode(   'THE_USERNAME',   'RECOVERY_CODE',   'USERS_NEW_STRONG_PASSWORD',   {     key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',     iv: '0xa3b054976a6ffc7fa1c527577480b663',   } ); console.log(recoveryCodeResponse); // { //    wallet: { //        ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8', //        privateKey: '0x602cbc76611ae50bcff99beacb4ab8e84853830f3036da946a8473107c4056e8', //    }, //    userAuthenticationToken: '0xjhk77d82gj1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c67v1', //    encryptedKeyInfo: { //       key: '0xh3457h9b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3kg3c7m', //        iv: '0xn6j876576a6ffc7fa1c567573480j9876', //    } //}
Response

save the new recovery userAuthenticationToken and encryptedKeyInfo to your server. Deleting the old userAuthenticationToken and encryptedKeyInfo. Also deleting any reference to the userRecoveryCodeAuthenticationToken and its encryptedKeyInfo from your server.

export interface EncryptedWallet {   // The wallet details this contains   // the ethereum address and private key   // you MUST not upload that private key anywhere   // to be able to stay none custodial. That private key   // is the ethereum wallet private key. As long as it stays   // on your client then its all good!   wallet: {     ethereumAddress: string;     privateKey: string;   };   // You must save all of the below to a server somewhere   // this data is not senitive and if someone got it they   // couldn't do much with it minus brute force the decryption.   // If you lose this data then they will not be able to get back   // to their private key so it must be stored safe    // This is basically an authentication token to be able to   // give the user back their encryptedPk key and iv. This is a hash   // of the users master_key (the users username and password). We will   // explain this usage a little later   userAuthenticationToken: string;   // This is the encrypted key which can be decrypted   // with the master_key to get back to the ethereum private key   encryptedKeyInfo: {     key: string;     iv: string;   }; }

Explaining server node calls

If you are not using node for your backend then you can not use the exposed methods in the flow diagram. This will explain what the methods do so you can write them in your backend language of choice.

hashAuthenticationTokenOnServer

This method hashes the client authentication token with a random salt to create you a server authentication token for that user. The built in method returns a salt and a serverAuthenticationHash

  • salt = random generated 16 bytes
  • serverAuthenticationHash = PBKDF(password: client_authentication_token, salt: randomBytes(16), iterations: 100000) then turned into a hex string

serverHashMatchesClientHash

This method compares the passed in client authentication token to the server authentication hash to make sure the token is valid for that user. The build in method returns a boolean.

Parameters:

  • userStoredSalt = The stored salt which was used to do the hashAuthenticationTokenOnServer
  • clientAuthenticationToken = The client authentication that the client passed to the server
  • serverAuthenticationHash = The server authentication hash that was generated by the hashAuthenticationTokenOnServer

algo = serverAuthenticationHash === PBKDF(password: client_authentication_token, salt: userStoredSalt, iterations: 100000)

verifyEthereumAddress

This method uses the ecdsaRecover logic which most languages have ways to support this so I will not explain how it works.


文件包的标题

加密是可怕和困难的,知道你用某些密钥做正确的事情是很难知道的。此外,大量以太坊eth钱包有时会添加糟糕的UX/UI,并将用户与DAPP分离,您可能需要创建一种更为集成的方法。最重要的是,这个软件包遵循标准用户名和密码的web2方法,使用户可以轻松地使用您的应用程序。此外,由于每个保存状态的事务和加密密钥,创建完全开放链的内容目前成本很高,至少应该保护自己,以免人们只能获取列表并试图对其进行暴力攻击。这是一种你可以做到两全其美的方法,而不需要你掌握任何人的钥匙

此软件包的目的是为您抽象所有这些:头巾:并为您提供简单的工具,使您能够创建无保管的以太坊eth钱包登录,而无需担心加密如何连接在一起,也无需担心保存人们私钥的可怕想法

所有解密都将在客户端进行,您只需要一个存储加密数据并返回数据的服务器。如果没有客户端生成的密钥,所有这些数据都是无用的

请注意,使用这种加密与用户提供的用户名和密码一样安全。该软件包不强制使用最大限制,而是由您决定如何强制用户确保他们输入强密码和用户名。我们强烈建议您在创建钱包时强制使用强密码,以保护您的用户。我们也不会将这种方法推广到加密的长期存储中,您可以将这种逻辑更多地看作是一个热/热钱包,即您的用户可以使用它轻松、安全地与您的dApp交互。只支持本机浏览器,无需扩展即可支持它们

此模型与LastPass用于非保管密码管理器的模型相同。它还受到非托管FunWallet工作方式的严重影响

安装软件包

NPM

纱线

使用和流量

新钱包

流量

客户端

服务器

登录现有钱包

流量

客户端

更改密码

服务器

流量

客户端更改用户名

客户端

服务器

恢复

生成的离线恢复代码

客户端

服务器

使用离线代码恢复

恢复身份验证令牌

响应

解释服务器节点调用服务器上的hashAuthenticationTokenOnServer

服务器HashMatchesClienthash

验证以太坊eth地址

salt=随机生成的16字节

  • 服务器身份验证Hash=PBKDF(密码:客户端身份验证令牌,salt:随机字节(16),迭代次数:100000)然后转换为十六进制字符串
  • UserStoredAlt=用于执行hashAuthenticationTokenOnServer
  • clientAuthenticationToken=客户端传递给服务器的客户端身份验证
  • serverAuthenticationHash=由hashAuthenticationTokenOnServer
  • NPM

    纱线

    使用和流量

    新钱包

    流量

    客户端

    服务器

    登录现有钱包

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    恢复身份验证令牌

    响应

    解释服务器节点调用服务器上的hashAuthenticationTokenOnServer

    服务器HashMatchesClienthash

    验证以太坊eth地址

    salt=随机生成的16字节

  • 服务器身份验证Hash=PBKDF(密码:客户端身份验证令牌,salt:随机字节(16),迭代次数:100000)然后转换为十六进制字符串
  • UserStoredAlt=用于执行hashAuthenticationTokenOnServer
  • clientAuthenticationToken=客户端传递给服务器的客户端身份验证
  • serverAuthenticationHash=由hashAuthenticationTokenOnServer
  • $ npm install PACKAGE_NAME

    NPM

    $ yarn PACKAGE_NAME

    使用和流量

    新钱包

    流量

    客户端

    服务器

    登录现有钱包

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    恢复身份验证令牌

    响应

    解释服务器节点调用服务器上的hashAuthenticationTokenOnServer

    服务器HashMatchesClienthash

    验证以太坊eth地址

    salt=随机生成的16字节

  • 服务器身份验证Hash=PBKDF(密码:客户端身份验证令牌,salt:随机字节(16),迭代次数:100000)然后转换为十六进制字符串
  • UserStoredAlt=用于执行hashAuthenticationTokenOnServer
  • clientAuthenticationToken=客户端传递给服务器的客户端身份验证
  • serverAuthenticationHash=由hashAuthenticationTokenOnServer
  • 这将引导您了解软件包的使用,并通过流程图解释流程

    新钱包

    流量

    客户端

    服务器

    登录现有钱包

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    恢复身份验证令牌

    响应

    解释服务器节点调用服务器上的hashAuthenticationTokenOnServer

    服务器HashMatchesClienthash

    验证以太坊eth地址

    salt=随机生成的16字节

  • 服务器身份验证Hash=PBKDF(密码:客户端身份验证令牌,salt:随机字节(16),迭代次数:100000)然后转换为十六进制字符串
  • UserStoredAlt=用于执行hashAuthenticationTokenOnServer
  • clientAuthenticationToken=客户端传递给服务器的客户端身份验证
  • serverAuthenticationHash=由hashAuthenticationTokenOnServer
  • 创建新用户

    新钱包

    流量

    客户端

    服务器

    登录现有钱包

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    恢复身份验证令牌

    响应

    解释服务器节点调用服务器上的hashAuthenticationTokenOnServer

    服务器HashMatchesClienthash

    验证以太坊eth地址

    salt=随机生成的16字节

  • 服务器身份验证Hash=PBKDF(密码:客户端身份验证令牌,salt:随机字节(16),迭代次数:100000)然后转换为十六进制字符串
  • UserStoredAlt=用于执行hashAuthenticationTokenOnServer
  • clientAuthenticationToken=客户端传递给服务器的客户端身份验证
  • serverAuthenticationHash=由hashAuthenticationTokenOnServer
  • TITLE_OF_PACKAGE!! - 包装的标题!!

    代码示例包括客户端+服务器

    流量

    客户端

    服务器

    登录现有钱包

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    恢复身份验证令牌

    响应

    解释服务器节点调用服务器上的hashAuthenticationTokenOnServer

    import { createNewWallet } from 'ethereum-web2-encryption';  // They have just clicked the register button after entering // their username and password export const register = async (username: string, password: string) => {   const encryptedWallet = await createWallet(username, password);   console.log(encryptedWallet);   // {   //    wallet: {   //        ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8',   //        privateKey: '0x602cbc76611ae50bcff99beacb4ab8e84853830f3036da946a8473107c4056e8',   //    },   //    signature: '0xf09eb344c7cbe4aebd7c3d2109eeddd5a3f1ec6a445a26ed1c46f47bce902a274af03b86f19557026055467a796a7e76be4c1fdd19132fd102097abe3124af081c',   //    userAuthenticationToken: '0xace36d94ae1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c84e7',   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    const request = {     username,     ethereumAddress: encryptedWallet.ethereumAddress,     signature: encryptedWallet.signature,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,     encryptedKeyInfo: encryptedWallet.encryptedKeyInfo,   };    // look at server part below to see what your server is expected to do   await fetch('YOUR_SERVER_API_REGISTER_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    // done user is registered! };
    客户端

    服务器

    登录现有钱包

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    恢复身份验证令牌

    响应

    解释服务器节点调用服务器上的hashAuthenticationTokenOnServer

    import {   verifyEthereumAddress,   hashAuthenticationTokenOnServer, } from 'ethereum-web2-encryption'; import db from 'YOUR_DB';  interface RegisterRequest {   username: string;   ethereumAddress: string;   signature: string;   userAuthenticationToken: string;   encryptedKeyInfo: {     key: string;     iv: string;   }; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const register = async (registerInfo: RegisterRequest) => {   const userExists = await db.userExists(registerInfo.username);   if (userExists) {     throw new Error('Username already exists');   }    const ethereumAddressExists = await db.ethereumAddressExists(     registerInfo.ethereumAddress   );   if (ethereumAddressExists) {     throw new Error('Ethereum address already exists');   }    const ownsEthereumAddress = await verifyEthereumAddress(     registerInfo.signature,     registerInfo.encryptedKeyInfo,     registerInfo.ethereumAddress   );   if (!ownsEthereumAddress) {     throw new Error(       'You do not own the ethereum address so can not register you'     );   }    const serverAuthHashResult = await hashAuthenticationTokenOnServer(     registerInfo.userAuthenticationToken   );    await db.createNewUser({     username: registerInfo.username,     ethereumAddress: registerInfo.ethereumAddress,     serverAuthenticationHash: serverAuthHashResult.serverAuthenticationHash,     salt: serverAuthHashResult.salt,     encryptedPk: registerInfo.encryptedKeyInfo.key,     encryptedPkIv: registerInfo.encryptedKeyInfo.iv,   });    // done user is registered! };

    服务器

    登录现有钱包

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    恢复身份验证令牌

    用户登录到已创建的帐户

    登录现有钱包

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    TITLE_OF_PACKAGE!! - 包装的标题!!

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    import {   getAuthenticationToken,   decryptWallet, } from 'ethereum-web2-encryption';  // They have just clicked the login button after entering // their username and password export const login = async (username: string, password: string) => {   const authenticationToken = await getAuthenticationToken(username, password);    const request = {     username,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,   };    // look at server part below to see what your server is expected to do   const response = await fetch('YOUR_SERVER_API_LOGIN_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    const encryptedWallet = await response.json();   console.log(encryptedWallet);   // {   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    const decryptedWallet = await decryptWallet(     username,     password,     encryptedWallet.encryptedKeyInfo   );   console.log(decryptedWallet);   // {   //    ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8',   //    privateKey: '0x602cbc76611ae50bcff99beacb4ab8e84853830f3036da946a8473107c4056e8',   //}    // done user is logged in! };
    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    import {   verifyEthereumAddress,   hashAuthenticationTokenOnServer, } from 'ethereum-web2-encryption'; import db from 'YOUR_DB';  interface LoginRequest {   username: string;   userAuthenticationToken: string; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const login = async (loginRequest: LoginRequest) => {   const userAuthenticationInfo = await db.userAuthenticationInfo(     registerInfo.username   );   if (!userAuthenticationInfo) {     throw new Error('User does not exists');   }    const serverHashMatchesClientHash = await serverHashMatchesClientHash(     userAuthenticationInfo.salt,     loginRequest.userAuthenticationToken,     userAuthenticationInfo.serverAuthenticationHash   );   console.log(serverHashMatchesClientHash);   // {   //    salt: '0x2e7199cd889426be35d730aabc3fa073',   //    serverAuthenticationHash: '0xf06e83e0086d2546cc7730eeee08bc739daa2af80fb34691ebc0a0964b96eb34',   //}   if (!serverHashMatchesClientHash) {     throw new Error('Incorrect login.');   }    return {     encryptedKeyInfo: {       key: userAuthenticationInfo.encryptedPk,       iv: userAuthenticationInfo.encryptedPkIv,     },   }; };

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    <TITLE_OF_PACKAGE!!><TITLE_OF_PACKAGE!!>

    <TITLE_OF_PACKAGE!!><TITLE_OF_PACKAGE!!><TITLE_OF_PACKAGE!!>

    恢复能力对于持有真实资金的东西至关重要。这公开了一些易于调用的方法,允许您支持这一点,但仍然保持非托管状态

    TITLE_OF_PACKAGE!! - 包装的标题!!

    如果用户想要恢复,请记住您在服务器上获得了他们的恢复加密数据

    TITLE_OF_PACKAGE!! - 包装的标题!!

    首先,您需要生成恢复身份验证令牌(恢复主密钥的哈希):

    然后,如上文所述—“将userRecoveryCodeAuthenticationToken(如果与您映射的用户名匹配,则返回该令牌的encryptedKeyInfo)终止服务器。”。此时,您获得了recoveryEncryptedKeyInfo,请将新的recovery userAuthenticationToken和encryptedKeyInfo保存到服务器。正在删除旧的userAuthenticationToken和encryptedKeyInfo。同时从服务器中删除对userRecoveryCodeAuthenticationToken及其encryptedKeyInfo的任何引用

    如果您的后端未使用node,则不能使用流程图中公开的方法。这将解释这些方法的作用,以便您可以使用所选的后端语言编写它们

    此方法使用随机salt散列客户端身份验证令牌,为该用户创建服务器身份验证令牌。内置方法返回salt和serverAuthenticationHash

    此方法将传入的客户端身份验证令牌与服务器身份验证哈希进行比较,以确保该令牌对该用户有效。内置方法返回一个布尔值

    参数:

    algo=serverAuthenticationHash==PBKDF(密码:clientu authenticationu token,salt:userStoredSalt,iterations:100000)

    此方法使用ecdsaRecover逻辑,大多数语言都有支持此逻辑的方法,因此我将不解释其工作原理

    文件包的标题

    安装软件包

    NPM

    纱线

    使用和流量

    新钱包

    流量

    客户端

    服务器

    登录现有钱包

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    恢复身份验证令牌

    响应

    解释服务器节点调用服务器上的hashAuthenticationTokenOnServer

    服务器HashMatchesClienthash

    验证以太坊eth地址

    salt=随机生成的16字节

  • 服务器身份验证Hash=PBKDF(密码:客户端身份验证令牌,salt:随机字节(16),迭代次数:100000)然后转换为十六进制字符串
  • UserStoredAlt=用于执行hashAuthenticationTokenOnServer
  • clientAuthenticationToken=客户端传递给服务器的客户端身份验证
  • serverAuthenticationHash=由hashAuthenticationTokenOnServer
  • 流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    import {   getAuthenticationToken,   changePassword, } from 'ethereum-web2-encryption';  // They have just clicked the change password and entered their username and password // to confirm they want to do it export const getEncryptedInformation = async (   username: string,   password: string ) => {   const authenticationToken = await getAuthenticationToken(username, password);    const request = {     username,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,   };    // look at server part below to see what your server is expected to do   const response = await fetch('YOUR_SERVER_API_GET_ENCRYPTED_INFO_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    const encryptedWallet = await response.json();   console.log(encryptedWallet);   // {   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    // the user now needs to enter their new password, you should hold the   // the encryptedKeyInfo somewhere ready for the next method below   // below method should show you what the next steps are };  interface ChangePassword {   username: string;   oldPassword: string;   newPassword: string;   encryptedKeyInfo: { key: string; iv: string }; }  // They have just clicked entered their new password and pressed enter export const changePassword = async (changePasswordRequest: ChangePassword) => {   const authenticationToken = await getAuthenticationToken(     changePasswordRequest.username,     changePasswordRequest.newPassword   );    const encryptedWallet = await changePassword(     changePasswordRequest.username,     {       oldPassword: changePasswordRequest.oldPassword,       newPassword: changePasswordRequest.newPassword,     },     changePasswordRequest.encryptedKeyInfo   );   console.log(encryptedWallet);   // {   //    wallet: {   //        ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8',   //        privateKey: '0x602cbc76611ae50bcff99beacb4ab8e84853830f3036da946a8473107c4056e8',   //    },   //    signature: '0xf09eb344c7cbe4aebd7c3d2109eeddd5a3f1ec6a445a26ed1c46f47bce902a274af03b86f19557026055467a796a7e76be4c1fdd19132fd102097abe3124af081c',   //    userAuthenticationToken: '0xace36d94ae1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c84e7',   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    // TODO LOOK AT FLOW OF PASSING OLD AUTHENTICATION TOKEN IN?!?!   const request = {     username,     ethereumAddress: encryptedWallet.ethereumAddress,     signature: encryptedWallet.signature,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,     encryptedKeyInfo: encryptedWallet.encryptedKeyInfo,   };   // look at server part below to see what your server is expected to do   await fetch('YOUR_SERVER_API_CHANGE_PASSWORD_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    // change password is done! };
    客户端更改用户名

    客户端

    服务器

    import {   verifyEthereumAddress,   hashAuthenticationTokenOnServer,   serverHashMatchesClientHash, } from 'ethereum-web2-encryption'; import db from 'YOUR_DB';  interface EncryptedInfoRequest {   username: string;   userAuthenticationToken: string; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const encryptedInfo = async (   encryptedInfoRequest: EncryptedInfoRequest ) => {   const encryptedInfo = await db.userAuthenticationInfo(     encryptedInfoRequest.username   );   if (!encryptedInfo) {     throw new Error('User does not exists');   }    const serverHashMatchesClientHash = await serverHashMatchesClientHash(     encryptedInfo.salt,     encryptedInfoRequest.userAuthenticationToken,     encryptedInfo.serverAuthenticationHash   );   if (!serverHashMatchesClientHash) {     throw new Error(       '401 > this does not match the user auth token (wrong username + password).'     );   }    return {     encryptedKeyInfo: {       key: userAuthenticationInfo.encryptedPk,       iv: userAuthenticationInfo.encryptedPkIv,     },   }; };  interface ChangePasswordRequest {   username: string;   ethereumAddress: string;   signature: string;   userAuthenticationToken: string;   encryptedKeyInfo: {     key: string;     iv: string;   }; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const changePassword = async (   changePasswordInfo: ChangePasswordRequest ) => {   const userExists = await db.userExists(changePasswordInfo.username);   if (!userExists) {     throw new Error('Username doesnt exists');   }    const ownsEthereumAddress = await verifyEthereumAddress(     changePasswordInfo.signature,     changePasswordInfo.encryptedKeyInfo,     changePasswordInfo.ethereumAddress   );   if (!ownsEthereumAddress) {     throw new Error(       'You do not own the ethereum address so can not register you'     );   }    const serverAuthHashResult = await hashAuthenticationTokenOnServer(     changePasswordInfo.userAuthenticationToken   );   console.log(serverAuthHashResult);   // {   //    salt: '0x2e7199cd889426be35d730aabc3fa073',   //    serverAuthenticationHash: '0xf06e83e0086d2546cc7730eeee08bc739daa2af80fb34691ebc0a0964b96eb34',   //}    await db.updateUser({     username: changePasswordInfo.username,     serverAuthenticationHash: serverAuthHashResult.serverAuthenticationHash,     salt: serverAuthHashResult.salt,     encryptedPk: changePasswordInfo.encryptedKeyInfo.key,     encryptedPkIv: changePasswordInfo.encryptedKeyInfo.iv,   });    // done user has changed password! };

    客户端

    <TITLE_OF_PACKAGE!!><TITLE_OF_PACKAGE!!><TITLE_OF_PACKAGE!!>

    恢复能力对于持有真实资金的东西至关重要。这公开了一些易于调用的方法,允许您支持这一点,但仍然保持非托管状态

    TITLE_OF_PACKAGE!! - 包装的标题!!

    如果用户想要恢复,请记住您在服务器上获得了他们的恢复加密数据

    TITLE_OF_PACKAGE!! - 包装的标题!!

    首先,您需要生成恢复身份验证令牌(恢复主密钥的哈希):

    然后,如上文所述—“将userRecoveryCodeAuthenticationToken(如果与您映射的用户名匹配,则返回该令牌的encryptedKeyInfo)终止服务器。”。此时,您获得了recoveryEncryptedKeyInfo,请将新的recovery userAuthenticationToken和encryptedKeyInfo保存到服务器。正在删除旧的userAuthenticationToken和encryptedKeyInfo。同时从服务器中删除对userRecoveryCodeAuthenticationToken及其encryptedKeyInfo的任何引用

    如果您的后端未使用node,则不能使用流程图中公开的方法。这将解释这些方法的作用,以便您可以使用所选的后端语言编写它们

    此方法使用随机salt散列客户端身份验证令牌,为该用户创建服务器身份验证令牌。内置方法返回salt和serverAuthenticationHash

    此方法将传入的客户端身份验证令牌与服务器身份验证哈希进行比较,以确保该令牌对该用户有效。内置方法返回一个布尔值

    参数:

    algo=serverAuthenticationHash==PBKDF(密码:clientu authenticationu token,salt:userStoredSalt,iterations:100000)

    此方法使用ecdsaRecover逻辑,大多数语言都有支持此逻辑的方法,因此我将不解释其工作原理

    文件包的标题

    安装软件包

    NPM

    纱线

    使用和流量

    新钱包

    流量

    客户端

    服务器

    登录现有钱包

    流量

    客户端

    更改密码

    服务器

    流量

    客户端更改用户名

    客户端

    服务器

    恢复

    生成的离线恢复代码

    客户端

    服务器

    使用离线代码恢复

    恢复身份验证令牌

    响应

    解释服务器节点调用服务器上的hashAuthenticationTokenOnServer

    服务器HashMatchesClienthash

    验证以太坊eth地址

    salt=随机生成的16字节

  • 服务器身份验证Hash=PBKDF(密码:客户端身份验证令牌,salt:随机字节(16),迭代次数:100000)然后转换为十六进制字符串
  • UserStoredAlt=用于执行hashAuthenticationTokenOnServer
  • clientAuthenticationToken=客户端传递给服务器的客户端身份验证
  • serverAuthenticationHash=由hashAuthenticationTokenOnServer
  • 恢复
    import {   getAuthenticationToken,   changeUsername, } from 'ethereum-web2-encryption';  // They have just clicked the change password and entered their username and password // to confirm they want to do it export const getEncryptedInformation = async (   username: string,   password: string ) => {   const authenticationToken = await getAuthenticationToken(username, password);    const request = {     username,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,   };    // look at server part below to see what your server is expected to do   const response = await fetch('YOUR_SERVER_API_GET_ENCRYPTED_INFO_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    const encryptedWallet = await response.json();   console.log(encryptedWallet);   // {   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    // the user now needs to enter their new password, you should hold the   // the encryptedKeyInfo somewhere ready for the next method below   // below method should show you what the next steps are };  interface ChangeEmailRequest {   oldUsername: string;   newUsername: string;   password: string;   encryptedKeyInfo: { key: string; iv: string }; }  // They have just clicked change email entered their new password and pressed enter export const changeEmail = async (changeEmailRequest: ChangeEmailRequest) => {   const authenticationToken = await getAuthenticationToken(     changeEmailRequest.oldUsername,     changeEmailRequest.newPassword   );    const encryptedWallet = await changeUsername(     {       oldUsername: changeEmailRequest.oldUsername,       newUsername: changeEmailRequest.newUsername,     },     changeEmailRequest.password,     changeEmailRequest.encryptedKeyInfo   );   console.log(encryptedWallet);   // {   //    wallet: {   //        ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8',   //        privateKey: '0x602cbc76611ae50bcff99beacb4ab8e84853830f3036da946a8473107c4056e8',   //    },   //    signature: '0xf09eb344c7cbe4aebd7c3d2109eeddd5a3f1ec6a445a26ed1c46f47bce902a274af03b86f19557026055467a796a7e76be4c1fdd19132fd102097abe3124af081c',   //    userAuthenticationToken: '0xace36d94ae1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c84e7',   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    // TODO LOOK AT FLOW OF PASSING OLD AUTHENTICATION TOKEN IN?!?!   const request = {     oldUsername,     newUsername,     ethereumAddress: encryptedWallet.ethereumAddress,     signature: encryptedWallet.signature,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,     encryptedKeyInfo: encryptedWallet.encryptedKeyInfo,   };   // look at server part below to see what your server is expected to do   await fetch('YOUR_SERVER_API_CHANGE_USERNAME_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    // change username is done! };
    服务器HashMatchesClienthash
    import {   verifyEthereumAddress,   hashAuthenticationTokenOnServer,   serverHashMatchesClientHash, } from 'ethereum-web2-encryption'; import db from 'YOUR_DB';  interface EncryptedInfoRequest {   username: string;   userAuthenticationToken: string; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const encryptedInfo = async (   encryptedInfoRequest: EncryptedInfoRequest ) => {   const encryptedInfo = await db.userAuthenticationInfo(     encryptedInfoRequest.username   );   if (!encryptedInfo) {     throw new Error('User does not exists');   }    const serverHashMatchesClientHash = await serverHashMatchesClientHash(     encryptedInfo.salt,     encryptedInfoRequest.userAuthenticationToken,     encryptedInfo.serverAuthenticationHash   );   if (!serverHashMatchesClientHash) {     throw new Error(       '401 > this does not match the user auth token (wrong username + password).'     );   }    return {     encryptedKeyInfo: {       key: userAuthenticationInfo.encryptedPk,       iv: userAuthenticationInfo.encryptedPkIv,     },   }; };  interface ChangeUsernameRequest {   oldUsername: string;   newUsername: string;   ethereumAddress: string;   signature: string;   userAuthenticationToken: string;   encryptedKeyInfo: {     key: string;     iv: string;   }; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const changeUsername = async (   changeUsernameInfo: ChangeUsernameRequest ) => {   const userExists = await db.userExists(changeUsernameInfo.oldUsername);   if (!userExists) {     throw new Error('Username does not exists');   }    const newUserExists = await db.userExists(changeUsernameInfo.newUsername);   if (newUserExists) {     throw new Error('Username already exists');   }    const ownsEthereumAddress = await verifyEthereumAddress(     changeUsernameInfo.signature,     changeUsernameInfo.encryptedKeyInfo,     changeUsernameInfo.ethereumAddress   );   if (!ownsEthereumAddress) {     throw new Error(       'You do not own the ethereum address so can not register you'     );   }    const serverAuthHashResult = await hashAuthenticationTokenOnServer(     changeUsernameInfo.userAuthenticationToken   );   console.log(serverAuthHashResult);   // {   //    salt: '0x2e7199cd889426be35d730aabc3fa073',   //    serverAuthenticationHash: '0xf06e83e0086d2546cc7730eeee08bc739daa2af80fb34691ebc0a0964b96eb34',   //}    await db.updateUser({     oldUsername: changeUsernameInfo.oldUsername,     newUsername: changeUsernameInfo.newUsername,     serverAuthenticationHash: serverAuthHashResult.serverAuthenticationHash,     salt: serverAuthHashResult.salt,     encryptedPk: changeUsernameInfo.encryptedKeyInfo.key,     encryptedPkIv: changeUsernameInfo.encryptedKeyInfo.iv,   });    // done user has changed username! };

    验证以太坊eth地址

    Ability to recover is critical on something which holds real funds. This exposes some easy to call methods to allow you to support this but still remain none custodial.

    Generated offline recovery code

    Flow

    恢复能力对于持有真实资金的东西至关重要。这公开了一些易于调用的方法,允许您支持这一点,但仍然保持非托管状态

    Client
    import {   getAuthenticationToken,   generateOfflineRecoveryCode, } from 'ethereum-web2-encryption';  // They have just clicked the change password and entered their username and password // to confirm they want to do it export const getEncryptedInformation = async (   username: string,   password: string ) => {   const authenticationToken = await getAuthenticationToken(username, password);    const request = {     username,     userAuthenticationToken: encryptedWallet.userAuthenticationToken,   };    // look at server part below to see what your server is expected to do   const response = await fetch('YOUR_SERVER_API_GET_ENCRYPTED_INFO_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    const encryptedWallet = await response.json();   console.log(encryptedWallet);   // {   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    // the user now needs to enter their new password, you should hold the   // the encryptedKeyInfo somewhere ready for the next method below   // below method should show you what the next steps are };  interface GenerateOfflineRecoveryCodeRequest {   username: string;   password: string;   encryptedKeyInfo: { key: string; iv: string }; }  // They have just clicked change email entered their new password and pressed enter export const generateOfflineRecoveryCode = async (   offlineRecoveryRequest: GenerateOfflineRecoveryCodeRequest ) => {   const authenticationToken = await getAuthenticationToken(     offlineRecoveryRequest.username,     offlineRecoveryRequest.password   );    const generateRecoveryCodeResponse = await generateOfflineRecoveryCode(     offlineRecoveryRequest.username,     offlineRecoveryRequest.password,     offlineRecoveryRequest.encryptedKeyInfo   );   console.log(generateRecoveryCodeResponse);   // {   //    offlineRecoveryCode: {   //       userCode: '0x975d73660b0b2fc1823668769ba0a88ba3d39eece71d15abc25c2fd2e95f042822f85bba3fb98b3d6d7aea326e361ba5a12c494a2ef33d05e1617adb5e26a0e6362bd43d7da0a0e4963fbde594b3b9ed',   //       id: '362bd43d7da0a0e4963fbde594b3b9ed'     }   //    ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8',   //    signature: '0xf09eb344c7cbe4aebd7c3d2109eeddd5a3f1ec6a445a26ed1c46f47bce902a274af03b86f19557026055467a796a7e76be4c1fdd19132fd102097abe3124af081c',   //    userAuthenticationToken: '0xace36d94ae1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c84e7',   //   encryptedKeyInfo: {   //       key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',   //       iv: '0xa3b054976a6ffc7fa1c527577480b663',   //    }   //}    const request = {     username,     recoveryCodeId: generateRecoveryCodeResponse.offlineRecoveryCode.id,     ethereumAddress: encryptedWallet.ethereumAddress,     signature: encryptedWallet.signature,     userRecoveryCodeAuthenticationToken:       encryptedWallet.userRecoveryCodeAuthenticationToken,     encryptedKeyInfo: encryptedWallet.encryptedKeyInfo,   };   // look at server part below to see what your server is expected to do   await fetch('YOUR_SERVER_API_SAVE_RECOVERY_OFFLINE_CODE_ENDPOINT', {     method: 'POST',     headers: {       'Content-Type': 'application/json',     },     body: JSON.stringify(request),   });    // generate offline recovery code is done! };
    Server
    import {   verifyEthereumAddress,   hashAuthenticationTokenOnServer,   serverHashMatchesClientHash, } from 'ethereum-web2-encryption'; import db from 'YOUR_DB';  interface EncryptedInfoRequest {   username: string;   userAuthenticationToken: string; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const encryptedInfo = async (   encryptedInfoRequest: EncryptedInfoRequest ) => {   const encryptedInfo = await db.userAuthenticationInfo(     encryptedInfoRequest.username   );   if (!encryptedInfo) {     throw new Error('User does not exists');   }    const serverHashMatchesClientHash = await serverHashMatchesClientHash(     encryptedInfo.salt,     encryptedInfoRequest.userAuthenticationToken,     encryptedInfo.serverAuthenticationHash   );   if (!serverHashMatchesClientHash) {     throw new Error(       '401 > this does not match the user auth token (wrong username + password).'     );   }    return {     encryptedKeyInfo: {       key: userAuthenticationInfo.encryptedPk,       iv: userAuthenticationInfo.encryptedPkIv,     },   }; };  interface OfflineRecoveryCodeRequest {   username: string;   recoveryCodeId: string;   ethereumAddress: string;   signature: string;   userRecoveryCodeAuthenticationToken: string;   encryptedKeyInfo: {     key: string;     iv: string;   }; }  // They client has called the server endpoint which then calls this // will keep in 1 method so its easy to follow export const saveGeneratedOfflineRecoveryCode = async (   offlineRecoveryCodeRequest: OfflineRecoveryCodeRequest ) => {   const userExists = await db.userExists(offlineRecoveryCodeRequest.username);   if (!userExists) {     throw new Error('Username does not exists');   }    const recoveryCodeExists = await db.recoveryCodeExists(     offlineRecoveryCodeRequest.recoveryCodeId   );   if (recoveryCodeExists) {     throw new Error('Recovery code id already exists');   }    const ownsEthereumAddress = await verifyEthereumAddress(     offlineRecoveryCodeRequest.signature,     offlineRecoveryCodeRequest.encryptedKeyInfo,     offlineRecoveryCodeRequest.ethereumAddress   );   if (!ownsEthereumAddress) {     throw new Error(       'You do not own the ethereum address so can not register you'     );   }    const serverAuthHashResult = await hashAuthenticationTokenOnServer(     offlineRecoveryCodeRequest.userRecoveryCodeAuthenticationToken   );   console.log(serverAuthHashResult);   // {   //    salt: '0x2e7199cd889426be35d730aabc3fa073',   //    serverAuthenticationHash: '0xf06e83e0086d2546cc7730eeee08bc739daa2af80fb34691ebc0a0964b96eb34',   //}    await db.saveOfflineRecoveryCode({     username: changeUsernameInfo.username,     recoveryCodeId: offlineRecoveryCodeRequest.recoveryCodeId,     serverAuthenticationHash: serverAuthHashResult.serverAuthenticationHash,     salt: serverAuthHashResult.salt,     encryptedPk: offlineRecoveryCodeRequest.encryptedKeyInfo.key,     encryptedPkIv: offlineRecoveryCodeRequest.encryptedKeyInfo.iv,   });    // done - user has saved the offline recovery code! };

    Recover using offline codes

    TITLE_OF_PACKAGE!! - 包装的标题!!

    Flow

    如果用户想要恢复,请记住您在服务器上获得了他们的恢复加密数据

    Recovery authentication token

    TITLE_OF_PACKAGE!! - 包装的标题!!

    import { getRecoveryAuthenticationToken } from 'ethereum-web2-encryption'; ... const recoveryAuthenticationToken = await getRecoveryAuthenticationToken(   'THE_USERSNAME',   'OFFLINE_RECOVERY_CODE' ); console.log(recoveryAuthenticationToken); // 0xace36d94ae1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c84e7

    首先,您需要生成恢复身份验证令牌(恢复主密钥的哈希):

    import { recoverWithOfflineCode } from 'ethereum-web2-encryption'; ... const recoveryCodeResponse = await recoverWithOfflineCode(   'THE_USERNAME',   'RECOVERY_CODE',   'USERS_NEW_STRONG_PASSWORD',   {     key: '0xd0286e5b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3bb4b9a',     iv: '0xa3b054976a6ffc7fa1c527577480b663',   } ); console.log(recoveryCodeResponse); // { //    wallet: { //        ethereumAddress: '0xa31e0D672AA9c6c4Ce863Bd17d1c7c9d6C56D5E8', //        privateKey: '0x602cbc76611ae50bcff99beacb4ab8e84853830f3036da946a8473107c4056e8', //    }, //    userAuthenticationToken: '0xjhk77d82gj1397b87135d363f207a440c5b30a0f2ce2ebf181b6ded0df9c67v1', //    encryptedKeyInfo: { //       key: '0xh3457h9b69d6003022a523e26bff0cdb1c2f28579ab692b10c0e68a7d3kg3c7m', //        iv: '0xn6j876576a6ffc7fa1c567573480j9876', //    } //}
    Response

    然后,如上文所述—“将userRecoveryCodeAuthenticationToken(如果与您映射的用户名匹配,则返回该令牌的encryptedKeyInfo)终止服务器。”。此时,您获得了recoveryEncryptedKeyInfo,请将新的recovery userAuthenticationToken和encryptedKeyInfo保存到服务器。正在删除旧的userAuthenticationToken和encryptedKeyInfo。同时从服务器中删除对userRecoveryCodeAuthenticationToken及其encryptedKeyInfo的任何引用

    export interface EncryptedWallet {   // The wallet details this contains   // the ethereum address and private key   // you MUST not upload that private key anywhere   // to be able to stay none custodial. That private key   // is the ethereum wallet private key. As long as it stays   // on your client then its all good!   wallet: {     ethereumAddress: string;     privateKey: string;   };   // You must save all of the below to a server somewhere   // this data is not senitive and if someone got it they   // couldn't do much with it minus brute force the decryption.   // If you lose this data then they will not be able to get back   // to their private key so it must be stored safe    // This is basically an authentication token to be able to   // give the user back their encryptedPk key and iv. This is a hash   // of the users master_key (the users username and password). We will   // explain this usage a little later   userAuthenticationToken: string;   // This is the encrypted key which can be decrypted   // with the master_key to get back to the ethereum private key   encryptedKeyInfo: {     key: string;     iv: string;   }; }

    Explaining server node calls

    如果您的后端未使用node,则不能使用流程图中公开的方法。这将解释这些方法的作用,以便您可以使用所选的后端语言编写它们

    hashAuthenticationTokenOnServer

    此方法使用随机salt散列客户端身份验证令牌,为该用户创建服务器身份验证令牌。内置方法返回salt和serverAuthenticationHash

    • 服务器身份验证Hash=PBKDF(密码:客户端身份验证令牌,salt:随机字节(16),迭代次数:100000)然后转换为十六进制字符串
    • UserStoredAlt=用于执行hashAuthenticationTokenOnServer

    serverHashMatchesClientHash

    此方法将传入的客户端身份验证令牌与服务器身份验证哈希进行比较,以确保该令牌对该用户有效。内置方法返回一个布尔值

    参数:

    • clientAuthenticationToken=客户端传递给服务器的客户端身份验证
    • serverAuthenticationHash=由hashAuthenticationTokenOnServer
    • serverAuthenticationHash = The server authentication hash that was generated by the hashAuthenticationTokenOnServer

    algo=serverAuthenticationHash==PBKDF(密码:clientu authenticationu token,salt:userStoredSalt,iterations:100000)

    verifyEthereumAddress

    此方法使用ecdsaRecover逻辑,大多数语言都有支持此逻辑的方法,因此我将不解释其工作原理

    部分转自网络,侵权联系删除区块链源码网

    www.interchains.cc

    https://www.interchains.cc/24028.html

    区块链毕设网(www.interchains.cc)全网最靠谱的原创区块链毕设代做网站 部分资料来自网络,侵权联系删除! 最全最大的区块链源码站 ! QQ3039046426
    区块链知识分享网, 以太坊dapp资源网, 区块链教程, fabric教程下载, 区块链书籍下载, 区块链资料下载, 区块链视频教程下载, 区块链基础教程, 区块链入门教程, 区块链资源 » 基于区块链的毕业设计TITLE_OF_PACKAGE!! – 包装的标题!!

    提供最优质的资源集合

    立即查看 了解详情