Skip to content

Commit

Permalink
Simplify zklogin SDK (MystenLabs#14186)
Browse files Browse the repository at this point in the history
## Description 

This makes a couple changes to simplify usage of the zklogin SDK.
- Randomness now returns a base64-encoded string
- Expose new `getExtendedEphemeralPublicKey` helper that returns a
base64-encoded string of the public key (this helper is really just a
one-liner but it makes it obvious what you should do, so I think there's
value in keeping it).
- Remove `toBigIntBE` as an export.

## Test Plan 

How did you test the new or updated feature?

---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [ ] protocol change
- [ ] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
  • Loading branch information
Jordan-Mysten authored Oct 12, 2023
1 parent a41390b commit d80a6ed
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/funny-flies-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@mysten/zklogin': minor
---

Remove toBigIntBE, expose new `getExtendedEphemeralPublicKey` method. Methods now return base64-encoded strings instead of bigints.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ module.exports = {
},
},
{
files: ['sdk/ledgerjs-hw-app-sui/**/*', 'apps/wallet/**/*', 'sdk/zklogin/**/*'],
files: ['sdk/ledgerjs-hw-app-sui/**/*', 'apps/wallet/**/*'],
rules: {
// ledgerjs-hw-app-sui and wallet use Buffer
'no-restricted-globals': ['off'],
Expand Down
6 changes: 2 additions & 4 deletions apps/wallet/src/background/accounts/zklogin/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Ed25519Keypair } from '@mysten/sui.js/keypairs/ed25519';
import {
generateNonce,
generateRandomness,
toBigIntBE,
getExtendedEphemeralPublicKey,
type getZkLoginSignature,
} from '@mysten/zklogin';
import { randomBytes } from '@noble/hashes/utils';
Expand Down Expand Up @@ -157,9 +157,7 @@ export async function createPartialZkLoginSignature({
},
body: JSON.stringify({
jwt,
extendedEphemeralPublicKey: toBigIntBE(
Buffer.from(ephemeralPublicKey.toSuiBytes()),
).toString(),
extendedEphemeralPublicKey: getExtendedEphemeralPublicKey(ephemeralPublicKey),
maxEpoch,
jwtRandomness: jwtRandomness.toString(),
salt: userSalt.toString(),
Expand Down
22 changes: 20 additions & 2 deletions sdk/typescript/src/zklogin/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,25 @@

import { hexToBytes } from '@noble/hashes/utils';

export function toBigEndianBytes(num: bigint, width: number) {
function findFirstNonZeroIndex(bytes: Uint8Array) {
for (let i = 0; i < bytes.length; i++) {
if (bytes[i] !== 0) {
return i;
}
}

return -1;
}

export function toBigEndianBytes(num: bigint, width: number): Uint8Array {
const hex = num.toString(16);
return hexToBytes(hex.padStart(width * 2, '0').slice(-width * 2));
const bytes = hexToBytes(hex.padStart(width * 2, '0').slice(-width * 2));

const firstNonZeroIndex = findFirstNonZeroIndex(bytes);

if (firstNonZeroIndex === -1) {
return new Uint8Array([0]);
}

return bytes.slice(firstNonZeroIndex);
}
9 changes: 9 additions & 0 deletions sdk/typescript/test/unit/zklogin/address.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ describe('zkLogin address', () => {
),
).toBe('0xf7badc2b245c7f74d7509a4aa357ecf80a29e7713fb4c44b0e7541ec43885ee1');
});

test('generates the correct address for a seed with leading zeros', () => {
expect(
computeZkLoginAddressFromSeed(
BigInt('380704556853533152350240698167704405529973457670972223618755249929828551006'),
'https://accounts.google.com',
),
).toBe('0xbd8b8ed42d90aebc71518385d8a899af14cef8b5a171c380434dd6f5bbfe7bf3');
});
});
4 changes: 2 additions & 2 deletions sdk/zklogin/src/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { lengthChecks } from './checks';
import { JSONProcessor } from './jsonprocessor.js';
import { genAddressSeed } from './utils.js';

export function jwtToAddress(jwt: string, userSalt: bigint) {
export function jwtToAddress(jwt: string, userSalt: string | bigint) {
const decodedJWT = decodeJwt(jwt);
if (!decodedJWT.iss) {
throw new Error('Missing iss');
Expand Down Expand Up @@ -41,7 +41,7 @@ export function jwtToAddress(jwt: string, userSalt: bigint) {
export interface ComputeZkLoginAddressOptions {
claimName: string;
claimValue: string;
userSalt: bigint;
userSalt: string | bigint;
iss: string;
aud: string;
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/zklogin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ export { poseidonHash } from './poseidon.js';

export { generateNonce, generateRandomness } from './nonce.js';

export { hashASCIIStrToField, genAddressSeed, toBigIntBE } from './utils.js';
export { hashASCIIStrToField, genAddressSeed, getExtendedEphemeralPublicKey } from './utils.js';
16 changes: 12 additions & 4 deletions sdk/zklogin/src/nonce.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { toB64, toHEX } from '@mysten/bcs';
import { PublicKey } from '@mysten/sui.js/cryptography';
import { toBigEndianBytes } from '@mysten/sui.js/zklogin';
import { randomBytes } from '@noble/hashes/utils';
import { base64url } from 'jose';

import { poseidonHash } from './poseidon.js';
import { toBigIntBE } from './utils.js';

const NONCE_LENGTH = 27;

function toBigIntBE(bytes: Uint8Array) {
const hex = toHEX(bytes);
if (hex.length === 0) {
return BigInt(0);
}
return BigInt(`0x${hex}`);
}

export function generateRandomness() {
// Once Node 20 enters LTS, we can just use crypto.getRandomValues(new Uint8Array(16)), but until then this improves compatibility:
return toBigIntBE(Buffer.from(randomBytes(16)));
return toB64(randomBytes(16));
}

export function generateNonce(publicKey: PublicKey, maxEpoch: number, randomness: bigint) {
const publicKeyBytes = toBigIntBE(Buffer.from(publicKey.toSuiBytes()));
export function generateNonce(publicKey: PublicKey, maxEpoch: number, randomness: bigint | string) {
const publicKeyBytes = toBigIntBE(publicKey.toSuiBytes());
const eph_public_key_0 = publicKeyBytes / 2n ** 128n;
const eph_public_key_1 = publicKeyBytes % 2n ** 128n;
const bigNum = poseidonHash([eph_public_key_0, eph_public_key_1, maxEpoch, randomness]);
Expand Down
15 changes: 7 additions & 8 deletions sdk/zklogin/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { toB64 } from '@mysten/bcs';
import { PublicKey } from '@mysten/sui.js/cryptography';

import { poseidonHash } from './poseidon.js';

const MAX_KEY_CLAIM_NAME_LENGTH = 32;
export const MAX_KEY_CLAIM_VALUE_LENGTH = 115;
export const MAX_AUD_VALUE_LENGTH = 145;
const PACK_WIDTH = 248;

export function toBigIntBE(buffer: Buffer) {
const hex = buffer.toString('hex');
if (hex.length === 0) {
return BigInt(0);
}
return BigInt(`0x${hex}`);
export function getExtendedEphemeralPublicKey(publicKey: PublicKey) {
return toB64(publicKey.toSuiBytes());
}

/**
Expand Down Expand Up @@ -60,14 +59,14 @@ export function hashASCIIStrToField(str: string, maxSize: number) {
}

export function genAddressSeed(
salt: bigint,
salt: string | bigint,
name: string,
value: string,
aud: string,
max_name_length = MAX_KEY_CLAIM_NAME_LENGTH,
max_value_length = MAX_KEY_CLAIM_VALUE_LENGTH,
max_aud_length = MAX_AUD_VALUE_LENGTH,
) {
): bigint {
return poseidonHash([
hashASCIIStrToField(name, max_name_length),
hashASCIIStrToField(value, max_value_length),
Expand Down

0 comments on commit d80a6ed

Please sign in to comment.