Skip to content

Commit

Permalink
simply offchainauth (ED25519) (#489)
Browse files Browse the repository at this point in the history
* feat: Register pub key

* feat: Replace zk crypto with ed25519

* chore: Remove file

* chore: Remove console

* chore: Changeset file

* feat: Remove Client wasm url param
  • Loading branch information
rrr523 authored Mar 11, 2024
1 parent 9322418 commit d45791a
Show file tree
Hide file tree
Showing 15 changed files with 464 additions and 725 deletions.
5 changes: 5 additions & 0 deletions .changeset/sixty-mayflies-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@bnb-chain/greenfield-js-sdk': minor
---

feat: Replace zk crypto with ed25519.
4 changes: 1 addition & 3 deletions examples/nextjs/src/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { GREEN_CHAIN_ID, GRPC_URL } from '@/config';
import { Client } from '@bnb-chain/greenfield-js-sdk';

export const client = Client.create(GRPC_URL, String(GREEN_CHAIN_ID), {
zkCryptoUrl: 'https://unpkg.com/@bnb-chain/[email protected]/dist/node/zk-crypto.wasm',
});
export const client = Client.create(GRPC_URL, String(GREEN_CHAIN_ID));

export const getSps = async () => {
const sps = await client.sp.getStorageProviders();
Expand Down
4 changes: 2 additions & 2 deletions examples/nextjs/src/components/bucket/create/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { client, selectSp } from '@/client';
import { getOffchainAuthKeys } from '@/utils/offchainAuth';
import { GRNToString, newBucketGRN, newGroupGRN } from '@bnb-chain/greenfield-js-sdk';
import { add } from 'lodash';
import { GRNToString, newBucketGRN } from '@bnb-chain/greenfield-js-sdk';
import { useState } from 'react';
import { useAccount } from 'wagmi';

Expand Down Expand Up @@ -34,6 +33,7 @@ export const CreateBucket = () => {

const provider = await connector?.getProvider();
const offChainData = await getOffchainAuthKeys(address, provider);
// console.log('offChainData', offChainData);
if (!offChainData) {
alert('No offchain, please create offchain pairs first');
return;
Expand Down
4 changes: 4 additions & 0 deletions examples/nextjs/src/components/deposit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ export const Deposit = () => {
});

console.log(txHash);

if (txHash) {
alert('deposit success');
}
}}
>
deposit
Expand Down
4 changes: 2 additions & 2 deletions packages/js-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
},
"dependencies": {
"@bnb-chain/greenfield-cosmos-types": "0.4.0-alpha.30",
"@bnb-chain/greenfield-zk-crypto": "workspace:*",
"@cosmjs/proto-signing": "^0.32.0",
"@cosmjs/stargate": "^0.32.0",
"@cosmjs/tendermint-rpc": "^0.32.0",
Expand All @@ -70,7 +69,8 @@
"@ethersproject/strings": "5.7.0",
"@ethersproject/units": "^5.7.0",
"@metamask/eth-sig-util": "^5.0.2",
"cross-fetch": "^3.1.6",
"@noble/curves": "^1.3.0",
"cross-fetch": "^4.0.0",
"dayjs": "^1.11.7",
"ethereum-cryptography": "^2.0.0",
"fast-xml-parser": "^4.2.7",
Expand Down
18 changes: 4 additions & 14 deletions packages/js-sdk/src/api/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ import {
MsgUpdateObjectInfo,
} from '@bnb-chain/greenfield-cosmos-types/greenfield/storage/tx';
import { bytesFromBase64 } from '@bnb-chain/greenfield-cosmos-types/helpers';
import { hexlify } from '@ethersproject/bytes';
import { Headers } from 'cross-fetch';
import { bytesToUtf8, hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils';
import { bytesToUtf8, hexToBytes } from 'ethereum-cryptography/utils';
import { container, delay, inject, injectable } from 'tsyringe';
import {
GRNToString,
Expand All @@ -38,7 +37,7 @@ import {
newObjectGRN,
} from '..';
import { RpcQueryClient } from '../clients/queryclient';
import { encodePath, getMsgToSign, getSortQuery, secpSign } from '../clients/spclient/auth';
import { encodePath, getAuthorization, getSortQuery } from '../clients/spclient/auth';
import { getApprovalMetaInfo } from '../clients/spclient/spApis/approval';
import { getGetObjectMetaInfo } from '../clients/spclient/spApis/getObject';
import {
Expand All @@ -63,7 +62,6 @@ import { MsgCancelCreateObjectSDKTypeEIP712 } from '../messages/greenfield/stora
import { MsgCreateObjectSDKTypeEIP712 } from '../messages/greenfield/storage/MsgCreateObject';
import { MsgDeleteObjectSDKTypeEIP712 } from '../messages/greenfield/storage/MsgDeleteObject';
import { MsgUpdateObjectInfoSDKTypeEIP712 } from '../messages/greenfield/storage/MsgUpdateObjectInfo';
import { signSignatureByEddsa } from '../offchainauth';
import {
AuthType,
CreateObjectApprovalRequest,
Expand Down Expand Up @@ -484,17 +482,9 @@ export class Objects implements IObject {
'\n',
].join('\n');

const unsignedMsg = getMsgToSign(utf8ToBytes(canonicalRequest));
let authorization = '';
if (authType.type === 'ECDSA') {
const sig = secpSign(unsignedMsg, authType.privateKey);
authorization = `GNFD1-ECDSA, Signature=${sig.slice(2)}`;
} else {
const sig = await signSignatureByEddsa(authType.seed, hexlify(unsignedMsg).slice(2));
authorization = `GNFD1-EDDSA,Signature=${sig}`;
}
const auth = getAuthorization(canonicalRequest, authType);

return `${url}?Authorization=${encodeURIComponent(authorization)}&${queryRaw}`;
return `${url}?Authorization=${encodeURIComponent(auth)}&${queryRaw}`;
}

public async downloadFile(configParam: GetObjectRequest, authType: AuthType): Promise<void> {
Expand Down
60 changes: 25 additions & 35 deletions packages/js-sdk/src/api/offchainauth.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
import { NORMAL_ERROR_CODE } from '../constants/http';
import {
fetchNonces,
genLocalSignMsg,
genSecondSignMsg,
getCurrentAccountPublicKey,
getCurrentSeedString,
personalSign,
updateSpsPubKey,
} from '../offchainauth';
import { hexlify } from '@ethersproject/bytes';
import { ed25519 } from '@noble/curves/ed25519';
import { injectable } from 'tsyringe';
import { convertTimeStampToDate, getUtcZeroTimestamp, SpResponse } from '..';
import { NORMAL_ERROR_CODE } from '../constants/http';
import { genSecondSignMsg, personalSign, updateSpsPubKey } from '../offchainauth';
import {
IGenOffChainAuthKeyPairAndUpload,
IReturnOffChainAuthKeyPairAndUpload,
ISp,
} from '../types/storage';

export interface IOffChainAuth {
Expand All @@ -34,52 +26,37 @@ export class OffChainAuth implements IOffChainAuth {
provider: any,
) {
try {
// 1. first sign, generate seed string and public key
const spsNonceRaw = await fetchNonces({ sps, address, domain });
const fetchSpsNonceFailed = spsNonceRaw
.filter((item: ISp) => item.nonce === null)
.map((item: ISp) => item.address);
if (fetchSpsNonceFailed.length === spsNonceRaw.length) {
throw new Error(`No SP service is available. Please try again later.`);
}
const spsWithNonce = spsNonceRaw.filter((item: ISp) => item.nonce !== null);
// 2. generate signature key pair
const seedMsg = genLocalSignMsg(spsWithNonce, domain);
// Uint8Array
const seed = await getCurrentSeedString({ message: seedMsg, address, chainId, provider });
const seedString = hexlify(seed);
const pubKey = await getCurrentAccountPublicKey(seedString);
const { privateKey, publicKey } = this.generateKeys();

// 3. second sign for upload public key to server
const curUtcZeroTimestamp = getUtcZeroTimestamp();
const expirationTime = curUtcZeroTimestamp + expirationMs;
const issuedDate = convertTimeStampToDate(curUtcZeroTimestamp);
const expireDate = convertTimeStampToDate(expirationTime);
const signMsg = genSecondSignMsg({
domain,
address,
pubKey,
pubKey: hexlify(publicKey).slice(2),
chainId,
issuedDate,
expireDate,
sps: spsWithNonce,
});
const signRes = await personalSign({ message: signMsg, address, provider });
const jsonSignMsg = JSON.stringify(signMsg).replace(/\"/g, '');
const authorization = `GNFD1-ETH-PERSONAL_SIGN,SignedMsg=${jsonSignMsg},Signature=${signRes}`;
// 4. upload signature and pubKey to server
const res = await updateSpsPubKey({
address,
sps: spsWithNonce,
sps,
domain,
pubKey,
pubKey: hexlify(publicKey).slice(2),
expireDate,
authorization,
});

const uploadSpsPubkeyFailed = res
.filter((item: any) => item.code !== 0)
.map((item: any) => item.data.address);
if (uploadSpsPubkeyFailed.length === spsWithNonce.length) {
if (uploadSpsPubkeyFailed.length === sps.length) {
throw new Error(`No SP service is available. Please try again later.`);
}
const successSps: string[] = [];
Expand All @@ -92,16 +69,29 @@ export class OffChainAuth implements IOffChainAuth {
return {
code: 0,
body: {
seedString,
pubKey,
seedString: hexlify(privateKey),
keypars: {
privateKey: hexlify(privateKey),
publicKey: hexlify(publicKey),
},
expirationTime,
spAddresses: successSps,
failedSpAddresses: [...fetchSpsNonceFailed, ...uploadSpsPubkeyFailed],
failedSpAddresses: uploadSpsPubkeyFailed,
},
message: 'Sign and upload public key success',
};
} catch (error: any) {
return { code: -1, message: error.message, statusCode: error?.status || NORMAL_ERROR_CODE };
}
}

private generateKeys() {
const privateKey = ed25519.utils.randomPrivateKey();
const publicKey = ed25519.getPublicKey(privateKey);

return {
privateKey,
publicKey,
};
}
}
13 changes: 1 addition & 12 deletions packages/js-sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,9 @@ export class Client {
* @chaidId string
* @wasmURL optional, need setting only used for browser
*/
static create(
rpcUrl: string,
chainId: string,
wasmURL?: {
zkCryptoUrl?: string;
},
): Client {
static create(rpcUrl: string, chainId: string): Client {
container.register('RPC_URL', { useValue: rpcUrl });
container.register('CHAIN_ID', { useValue: chainId });
container.register('ZK_CRYPTO', { useValue: wasmURL?.zkCryptoUrl });

if (wasmURL?.zkCryptoUrl) {
(globalThis as any).__PUBLIC_ZKCRYPTO_WASM_PATH__ = wasmURL.zkCryptoUrl;
}

const account = container.resolve<Account>(Account);
const basic = container.resolve<Basic>(Basic);
Expand Down
18 changes: 10 additions & 8 deletions packages/js-sdk/src/clients/spclient/auth.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { signSignatureByEddsa } from '@/offchainauth';
import { AuthType, ReqMeta } from '@/types/auth';
import { hexlify, joinSignature } from '@ethersproject/bytes';
import { SigningKey } from '@ethersproject/signing-key';
import { ed25519 } from '@noble/curves/ed25519';
import { Headers } from 'cross-fetch';
import { keccak256 } from 'ethereum-cryptography/keccak.js';
import { utf8ToBytes } from 'ethereum-cryptography/utils.js';
Expand Down Expand Up @@ -41,11 +41,7 @@ const getSignedHeaders = (reqHeaders: Headers) => {
return sortedHeaders.join(';');
};

export const getAuthorization = async (
reqMeta: Partial<ReqMeta>,
reqHeaders: Headers,
authType: AuthType,
) => {
export const getCanonicalRequest = (reqMeta: Partial<ReqMeta>, reqHeaders: Headers) => {
const canonicalHeaders = getCanonicalHeaders(reqMeta, reqHeaders);
const signedHeaders = getSignedHeaders(reqHeaders);
const canonicalRequestArr = [
Expand All @@ -57,6 +53,11 @@ export const getAuthorization = async (
];

const canonicalRequest = canonicalRequestArr.join('\n');

return canonicalRequest;
};

export const getAuthorization = (canonicalRequest: string, authType: AuthType) => {
// console.log('canonicalRequest', canonicalRequest);

const unsignedMsg = getMsgToSign(utf8ToBytes(canonicalRequest));
Expand All @@ -65,8 +66,8 @@ export const getAuthorization = async (
const sig = secpSign(unsignedMsg, authType.privateKey);
authorization = `GNFD1-ECDSA, Signature=${sig.slice(2)}`;
} else {
const sig = await signSignatureByEddsa(authType.seed, hexlify(unsignedMsg).slice(2));
authorization = `GNFD1-EDDSA,Signature=${sig}`;
const sig = hexlify(ed25519.sign(hexlify(unsignedMsg).slice(2), authType.seed.slice(2)));
authorization = `GNFD2-EDDSA,Signature=${sig.slice(2)}`;
}

return authorization;
Expand Down Expand Up @@ -131,6 +132,7 @@ export const HTTPHeaderContentMD5 = 'Content-MD5';
export const HTTPHeaderUnsignedMsg = 'X-Gnfd-Unsigned-Msg';
export const HTTPHeaderUserAddress = 'X-Gnfd-User-Address';
export const HTTPHeaderAppDomain = 'X-Gnfd-App-Domain';
export const HTTPHeaderRegPubKey = 'X-Gnfd-App-Reg-Public-Key';

const SUPPORTED_HEADERS = [
HTTPHeaderContentSHA256.toLocaleLowerCase(),
Expand Down
62 changes: 0 additions & 62 deletions packages/js-sdk/src/clients/spclient/sign.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ export const updateUserAccountKey = async ({
authorization,
}: IUpdateOneSpPubKeyParams) => {
let result;
const url = `${sp.endpoint}/auth/update_key`;
const nonce = sp.nonce + '';
const url = `${sp.endpoint}/auth/update_key_v2`;
const headers = new Headers({
'X-Gnfd-User-Address': address,
'X-Gnfd-App-Domain': domain,
'X-Gnfd-App-Reg-Nonce': nonce,
'X-Gnfd-App-Reg-Public-Key': pubKey,
'X-Gnfd-Expiry-Timestamp': expireDate,
Authorization: authorization,
Expand Down
Loading

0 comments on commit d45791a

Please sign in to comment.