-
Notifications
You must be signed in to change notification settings - Fork 518
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Message encryption for session validation (#41)
* move/rename cipher stuff * MWP message encryption * ArrayBuffer can be transferred over postMessage * inject KeyStorage * cleanup * cleanup * cleanup * separate out message conversion * more granular processing so that host can reuse * cleanup * rename. handshake request to conform to Action * host * host implementation * cleanup * fix test * fix errors
- Loading branch information
Showing
14 changed files
with
180 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,132 @@ | ||
import { standardErrors } from '../../core/error'; | ||
import { AddressString } from '../../core/type'; | ||
import { RequestArguments } from '../../provider/ProviderInterface'; | ||
import { PopUpCommunicator } from '../../transport/PopUpCommunicator'; | ||
import { LIB_VERSION } from '../../version'; | ||
import { Connector } from '../ConnectorInterface'; | ||
import { exportKeyToHexString, importKeyFromHexString } from './protocol/key/Cipher'; | ||
import { KeyStorage } from './protocol/key/KeyStorage'; | ||
import { | ||
decryptContent, | ||
encryptContent, | ||
SCWRequestMessage, | ||
SCWResponseMessage, | ||
} from './protocol/SCWMessage'; | ||
import { Action, SupportedEthereumMethods } from './protocol/type/Action'; | ||
import { Request } from './protocol/type/Request'; | ||
import { AddressString } from ':wallet-sdk/src/core/type'; | ||
import { RequestArguments } from ':wallet-sdk/src/provider/ProviderInterface'; | ||
import { SCWResponse } from './protocol/type/Response'; | ||
|
||
export class SCWConnector implements Connector { | ||
protected appName = ''; | ||
private appLogoUrl: string | null = null; | ||
private appName: string; | ||
private appLogoUrl: string | null; | ||
// TODO: handle chainId | ||
private activeChainId = 1; | ||
|
||
private puc: PopUpCommunicator; | ||
private keyStorage: KeyStorage; | ||
|
||
constructor(options: { appName: string; appLogoUrl: string | null; puc: PopUpCommunicator }) { | ||
constructor(options: { | ||
appName: string; | ||
appLogoUrl: string | null; | ||
puc: PopUpCommunicator; | ||
keyStorage: KeyStorage; | ||
}) { | ||
this.appName = options.appName; | ||
this.appLogoUrl = options.appLogoUrl; | ||
this.puc = options.puc; | ||
this.keyStorage = options.keyStorage; | ||
} | ||
|
||
public async handshake() { | ||
// first method called by provider, for now just returns ethereum accounts | ||
// later: handle passing dapp metadata, storing session, etc. | ||
// later: return spec-compliant errors for unsupported methods | ||
public async handshake(): Promise<AddressString[]> { | ||
// TODO | ||
await this.puc.connect(); | ||
|
||
// request accounts | ||
return await this.request<AddressString[]>({ | ||
method: 'eth_requestAccounts', | ||
params: { appName: this.appName, appLogoUrl: this.appLogoUrl }, | ||
const handshakeMessage = await this.createRequestMessage({ | ||
handshake: { | ||
method: SupportedEthereumMethods.EthRequestAccounts, | ||
params: { | ||
appName: this.appName, | ||
appLogoUrl: this.appLogoUrl, | ||
}, | ||
}, | ||
}); | ||
} | ||
|
||
private _checkMethod(method: string): boolean { | ||
return Object.values(SupportedEthereumMethods).includes(method as SupportedEthereumMethods); | ||
const response = (await this.puc.request(handshakeMessage)) as SCWResponseMessage; | ||
|
||
// throw protocol level error | ||
if ('error' in response.content) { | ||
throw response.content.error; | ||
} | ||
|
||
// take the peer's public key and store it | ||
const peerPublicKey = await importKeyFromHexString('public', response.sender); | ||
await this.keyStorage.setPeerPublicKey(peerPublicKey); | ||
|
||
return this.decodeResponseMessage<AddressString[]>(response); | ||
} | ||
|
||
public async request<T>(request: RequestArguments): Promise<T> { | ||
if (!this._checkMethod(request.method)) { | ||
return Promise.reject( | ||
standardErrors.provider.unsupportedMethod( | ||
`${request.method} is not supported for SCW at this time` | ||
) | ||
); | ||
} | ||
// TODO: this check makes sense, but connected isn't set properly so it prevents | ||
// need to investigate | ||
// if (!this.puc.connected) { | ||
await this.puc.connect(); | ||
// } | ||
|
||
const pucRequest: Request = { | ||
action: request as Action, | ||
const sharedSecret = await this.keyStorage.getSharedSecret(); | ||
if (!sharedSecret) { | ||
// TODO: better error | ||
throw new Error('Invalid session'); | ||
} | ||
|
||
const encrypted = await encryptContent( | ||
{ | ||
action: request as Action, | ||
chainId: this.activeChainId, | ||
}, | ||
sharedSecret | ||
); | ||
const message = await this.createRequestMessage({ encrypted }); | ||
|
||
return this.puc | ||
.request(message) | ||
.then((response) => response as SCWResponseMessage) | ||
.then(this.decodeResponseMessage<T>); | ||
} | ||
|
||
private async createRequestMessage( | ||
content: SCWRequestMessage['content'] | ||
): Promise<SCWRequestMessage> { | ||
const publicKey = await exportKeyToHexString('public', await this.keyStorage.getOwnPublicKey()); | ||
return { | ||
type: 'scw', | ||
id: crypto.randomUUID(), | ||
sender: publicKey, | ||
content, | ||
version: LIB_VERSION, | ||
timestamp: new Date(), | ||
}; | ||
} | ||
|
||
return this.puc.request<T>(pucRequest).then((response) => { | ||
const result = response.content.result; | ||
private async decodeResponseMessage<T>(message: SCWResponseMessage): Promise<T> { | ||
const content = message.content; | ||
|
||
if ('error' in result) { | ||
throw result.error; | ||
} | ||
// throw protocol level error | ||
if ('error' in content) { | ||
throw content.error; | ||
} | ||
|
||
return result.value; | ||
}); | ||
const sharedSecret = await this.keyStorage.getSharedSecret(); | ||
if (!sharedSecret) { | ||
// TODO: better error | ||
throw new Error('Invalid session'); | ||
} | ||
|
||
const decrypted: SCWResponse<T> = await decryptContent(content.encrypted, sharedSecret); | ||
const result = decrypted.result; | ||
|
||
// check for ActionResult error | ||
if ('error' in result) { | ||
throw result.error; | ||
} | ||
|
||
return result.value; | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
packages/wallet-sdk/src/connector/scw/protocol/SCWMessage.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { UUID } from 'crypto'; | ||
|
||
import { Message } from '../../../transport/CrossDomainCommunicator'; | ||
import { decrypt, encrypt, EncryptedData } from './key/Cipher'; | ||
import { SupportedEthereumMethods } from './type/Action'; | ||
import { SCWRequest } from './type/Request'; | ||
import { SCWResponse } from './type/Response'; | ||
|
||
interface SCWMessage extends Message { | ||
type: 'scw'; | ||
id: UUID; | ||
sender: string; // hex encoded public key of the sender | ||
content: unknown; | ||
version: string; | ||
timestamp: Date; | ||
} | ||
|
||
export interface SCWRequestMessage extends SCWMessage { | ||
content: | ||
| { | ||
handshake: { | ||
method: SupportedEthereumMethods.EthRequestAccounts; | ||
params: { | ||
appName: string; | ||
appLogoUrl: string | null; | ||
}; | ||
}; | ||
} | ||
| { | ||
encrypted: EncryptedData; | ||
}; | ||
} | ||
|
||
export interface SCWResponseMessage extends SCWMessage { | ||
requestId: UUID; | ||
content: | ||
| { | ||
encrypted: EncryptedData; | ||
} | ||
| { | ||
error: Error; | ||
}; | ||
} | ||
|
||
export async function encryptContent<T>( | ||
content: SCWRequest | SCWResponse<T>, | ||
sharedSecret: CryptoKey | ||
): Promise<EncryptedData> { | ||
return encrypt(sharedSecret, JSON.stringify(content)); | ||
} | ||
|
||
export async function decryptContent<R extends SCWRequest | SCWResponse<U>, U>( | ||
encryptedData: EncryptedData, | ||
sharedSecret: CryptoKey | ||
): Promise<R> { | ||
return JSON.parse(await decrypt(sharedSecret, encryptedData)); | ||
} |
2 changes: 1 addition & 1 deletion
2
...connector/scw/protocol/Keymanager.test.ts → ...connector/scw/protocol/key/Cipher.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 2 additions & 2 deletions
4
.../src/connector/scw/protocol/KeyManager.ts → .../src/connector/scw/protocol/key/Cipher.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
packages/wallet-sdk/src/connector/scw/protocol/key/KeyStorage.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 3 additions & 2 deletions
5
packages/wallet-sdk/src/connector/scw/protocol/type/Request.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { Action } from './Action'; | ||
|
||
export type Request = { | ||
action: Action; | ||
export type SCWRequest = { | ||
action: Action; // JSON-RPC call | ||
chainId: number; | ||
}; |
5 changes: 3 additions & 2 deletions
5
packages/wallet-sdk/src/connector/scw/protocol/type/Response.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { ActionResult } from './ActionResult'; | ||
|
||
export type Response<T> = { | ||
result: ActionResult<T>; | ||
export type SCWResponse<T> = { | ||
result: ActionResult<T>; // JSON-RPC result | ||
data?: unknown; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters