Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Blockchain: Sui authenticate create proof for mainnet 1`] = `"0xd042f28e5ff37a2f68f7d7b96adca0d10b5cba3d4d6d056796653479d8a70c0e"`;

exports[`Blockchain: Sui createLink create proof for mainnet 1`] = `
Object {
"account": "Gy9JCW4+Xb0Pz6nAwM2S2as7IVRLNNXdSmXZi4eLmSI=@sui:mainnet",
"message": "Link this account to your identity

did:3:bafysdfwefwe
Timestamp: 666",
"signature": "KriuHY8KAU87qNeGjdtvutl0TWgSnOPpUDI8fniN8jHGdwcKubYUooJja/hnFZoizq7q66dx5XRPxKS4EcnGDA",
"timestamp": 666,
"type": "sui",
"version": 2,
}
`;
62 changes: 62 additions & 0 deletions packages/blockchain-utils-linking/src/__tests__/sui.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { jest } from '@jest/globals'
import { SuiAuthProvider } from '../sui.js'
import { Base64DataBuffer,Ed25519Keypair } from "@mysten/sui.js";


const did = 'did:3:bafysdfwefwe'
const privKey = 'mdqVWeFekT7pqy5T49+tV12jO0m+ESW7ki4zSU9JiCgbL0kJbj5dvQ/PqcDAzZLZqzshVEs01d1KZdmLh4uZIg=='
const chainRef = 'mainnet'

class MyWalletAdapter {
readonly _keyPair: Ed25519Keypair

constructor(keyPair: Ed25519Keypair) {
this._keyPair = keyPair
}

async signMessage(message: Uint8Array): Promise<Uint8Array> {
const msg = new Base64DataBuffer(message);
const signature = this._keyPair.signData(msg);
return signature.getData()
}
}

let keyPairEd25519: Ed25519Keypair

beforeAll(() => {
keyPairEd25519 = Ed25519Keypair.fromSecretKey(Buffer.from(privKey, 'base64'))
global.Date.now = jest.fn().mockImplementation(() => 666000)
})

afterAll(() => {
jest.clearAllMocks()
})

describe('Blockchain: Sui', () => {
describe('createLink', () => {
test(`create proof for ${chainRef}`, async () => {
const keypair = keyPairEd25519
const provider = new MyWalletAdapter(keypair)
const authProvider = new SuiAuthProvider(
provider,
keyPairEd25519.getPublicKey().toString(),
chainRef
)
const proof = await authProvider.createLink(did)
expect(proof).toMatchSnapshot()
})
})

describe('authenticate', () => {
test(`create proof for ${chainRef}`, async () => {
const provider = new MyWalletAdapter(keyPairEd25519)
const authProvider = new SuiAuthProvider(
provider,
keyPairEd25519.getPublicKey().toString(),
chainRef
)
const result = await authProvider.authenticate('msg')
expect(result).toMatchSnapshot()
})
})
})
2 changes: 2 additions & 0 deletions packages/blockchain-utils-linking/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * as eosio from './eosio.js'
export * as cosmos from './cosmos.js'
export * as tezos from './tezos.js'
export * as solana from './solana.js'
export * as sui from './sui.js'

export { EthereumAuthProvider } from './ethereum.js'
export { EthereumAuthProvider as AvalancheAuthProvider } from './ethereum.js'
Expand All @@ -17,3 +18,4 @@ export { CosmosAuthProvider } from './cosmos.js'
export { NearAuthProvider } from './near.js'
export { TezosAuthProvider, TezosProvider } from './tezos.js'
export { SolanaAuthProvider } from './solana.js'
export { SuiAuthProvider } from './sui.js'
59 changes: 59 additions & 0 deletions packages/blockchain-utils-linking/src/sui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { AccountId } from 'caip'
import { AuthProvider } from './auth-provider.js'
import { asOldCaipString, getConsentMessage, LinkProof } from './util.js'
import * as uint8arrays from 'uint8arrays'
import * as sha256 from '@stablelib/sha256'


export class SuiAuthProvider implements AuthProvider {
readonly isAuthProvider = true

constructor(
private readonly provider: any,
private readonly address: string,
private readonly chainRef: string
) {
console.warn(
'WARN: SuiAuthProvider is not fully supported. You may encounter issues using this.'
)
}

async accountId(): Promise<AccountId> {
return new AccountId({
address: this.address,
chainId: `sui:${this.chainRef}`,
})
}

async authenticate(message: string): Promise<string> {
if (!this.provider.signMessage) {
throw new Error(`Unsupported provider; provider must implement signMessage`)
}
const signatureBytes = await this.provider.signMessage(uint8arrays.fromString(message))
const digest = sha256.hash(signatureBytes)
return `0x${uint8arrays.toString(digest, 'base16')}`
}

async createLink(did: string): Promise<LinkProof> {
if (!this.provider.signMessage) {
throw new Error(`Unsupported provider; provider must implement signMessage`)
}
const { message, timestamp } = getConsentMessage(did, true)
const accountID = await this.accountId()
const signatureBytes = await this.provider.signMessage(uint8arrays.fromString(message))
const signature = uint8arrays.toString(signatureBytes, 'base64')
return {
version: 2,
type: 'sui',
message,
signature,
account: asOldCaipString(accountID),
timestamp,
}
}

withAddress(address: string): AuthProvider {
return new SuiAuthProvider(this.provider, address, this.chainRef)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { validateLink } from '../sui'
import { Base64DataBuffer, Ed25519Keypair } from "@mysten/sui.js";
import { SuiAuthProvider } from '@ceramicnetwork/blockchain-utils-linking'


const did = 'did:3:bafysdfwefwe'
const privKey = 'mdqVWeFekT7pqy5T49+tV12jO0m+ESW7ki4zSU9JiCgbL0kJbj5dvQ/PqcDAzZLZqzshVEs01d1KZdmLh4uZIg=='
const chainRef = 'mainnet'
const keyPairEd25519 = Ed25519Keypair.fromSecretKey(Buffer.from(privKey, 'base64'))

class MyWalletAdapter {
readonly _keyPair: Ed25519Keypair

constructor(keyPair: Ed25519Keypair) {
this._keyPair = keyPair
}

async signMessage(message: Uint8Array): Promise<Uint8Array> {
const msg = new Base64DataBuffer(message);
const signature = this._keyPair.signData(msg);
return signature.getData()
}
}

describe('Blockchain: ', () => {
describe('validateLink', () => {
test(`validate proof for ${chainRef}`, async () => {
const provider = new MyWalletAdapter(keyPairEd25519)
const authProvider = new SuiAuthProvider(provider, keyPairEd25519.getPublicKey().toString(), chainRef)
const proof = await authProvider.createLink(did)
await expect(validateLink(proof)).resolves.toEqual(proof)
})
})
})
36 changes: 36 additions & 0 deletions packages/blockchain-utils-validation/src/blockchains/sui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { BlockchainHandler } from '../blockchain-handler.js'
import { LinkProof } from '@ceramicnetwork/blockchain-utils-linking'
import * as uint8arrays from 'uint8arrays'
import { normalizeAccountId } from '@ceramicnetwork/common'
import nacl from 'tweetnacl';
import { Base64DataBuffer } from "@mysten/sui.js";


const verifySignature = async (
pubKey: Uint8Array,
message: string,
signature: Uint8Array
): Promise<boolean> => {
const signData = new Base64DataBuffer(new TextEncoder().encode(message));
const verified = nacl.sign.detached.verify(
signData.getData(),
signature,
pubKey
)
return verified
}

const namespace = 'sui'

export async function validateLink(proof: LinkProof): Promise<LinkProof | null> {
const pubKey = uint8arrays.fromString(normalizeAccountId(proof.account).address, 'base64')
const msg = proof.message
const sig = uint8arrays.fromString(proof.signature, 'base64')
const is_sig_valid = await verifySignature(pubKey, msg, sig)
return is_sig_valid ? proof : null
}

export const handler: BlockchainHandler = {
namespace,
validateLink,
}
2 changes: 2 additions & 0 deletions packages/blockchain-utils-validation/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { handler as cosmos } from './blockchains/cosmos.js'
import { handler as near } from './blockchains/near.js'
import { handler as tezos } from './blockchains/tezos.js'
import { handler as solana } from './blockchains/solana.js'
import { handler as sui } from './blockchains/sui.js'
import { AccountId } from 'caip'
import { normalizeAccountId } from '@ceramicnetwork/common'

Expand All @@ -19,6 +20,7 @@ const handlers = {
[near.namespace]: near,
[tezos.namespace]: tezos,
[solana.namespace]: solana,
[sui.namespace]: sui,
}

const findDID = (did: string): string | undefined => did.match(/(did:\S+:\S+)/)?.[0]
Expand Down
Loading