Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/encrypt decrypt #48

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
21,459 changes: 21,427 additions & 32 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
},
"private": true,
"dependencies": {
"@airgap/beacon-sdk": "2.2.5",
"@airgap/beacon-sdk": "2.3.0-beta.1",
"@airgap/coinlib-core": "0.11.2-beta.1",
"@angular/common": "^11.0.3",
"@angular/core": "^11.0.3",
Expand Down
11 changes: 11 additions & 0 deletions src/app/pages/beacon-request/beacon-request.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ <h2>
</ng-container>
</div>

<div *ngIf="request.type === 'encrypt_payload_request'" class="request--container">
<ng-container>
<p class="ion-padding-top">
You are about to encrypt/decrypt the following payload.
</p>
<ion-row class="rawdata--container ion-margin-bottom">
<pre>{{ request.payload }}</pre>
</ion-row>
</ng-container>
</div>

<div *ngIf="request.type === 'operation_request'" class="request--container">
<ng-container *ngFor="let transaction of transactions">
<beacon-from-to [transaction]="transaction"></beacon-from-to>
Expand Down
40 changes: 34 additions & 6 deletions src/app/pages/beacon-request/beacon-request.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
BeaconMessageType,
BroadcastRequestOutput,
ChromeStorage,
EncryptPayloadRequestOutput,
Network,
OperationRequestOutput,
PermissionRequestOutput,
Expand Down Expand Up @@ -35,6 +36,7 @@ export class BeaconRequestPage implements OnInit {
public request:
| PermissionRequestOutput
| OperationRequestOutput
| EncryptPayloadRequestOutput
| SignPayloadRequestOutput
| BroadcastRequestOutput
| undefined
Expand Down Expand Up @@ -83,6 +85,12 @@ export class BeaconRequestPage implements OnInit {
await this.signRequest(this.request)
}

if (this.request && this.request.type === BeaconMessageType.EncryptPayloadRequest) {
this.title = 'Encrypt/Decrypt Request'
this.requesterName = this.request.appMetadata.name
await this.encryptRequest(this.request)
}

if (this.request && this.request.type === BeaconMessageType.OperationRequest) {
this.title = 'Operation Request'
this.requesterName = this.request.appMetadata.name
Expand Down Expand Up @@ -123,7 +131,7 @@ export class BeaconRequestPage implements OnInit {
name: 'sign',
type: 'checkbox',
label: 'Sign transactions',
value: 'sign',
value: PermissionScope.SIGN,
icon: 'create',
checked: request.scopes.indexOf(PermissionScope.SIGN) >= 0
},
Expand All @@ -132,16 +140,25 @@ export class BeaconRequestPage implements OnInit {
name: 'operation_request',
type: 'checkbox',
label: 'Operation request',
value: 'operation_request',
value: PermissionScope.OPERATION_REQUEST,
icon: 'color-wand',
checked: request.scopes.indexOf(PermissionScope.OPERATION_REQUEST) >= 0
},

{
name: 'encrypt_request',
type: 'checkbox',
label: 'Encrypt request',
value: PermissionScope.ENCRYPT,
icon: 'create',
checked: request.scopes.indexOf(PermissionScope.ENCRYPT) >= 0
},

{
name: 'threshold',
type: 'checkbox',
label: 'Threshold',
value: 'threshold',
value: PermissionScope.THRESHOLD,
icon: 'code-working',
checked: request.scopes.indexOf(PermissionScope.THRESHOLD) >= 0
}
Expand All @@ -167,6 +184,17 @@ export class BeaconRequestPage implements OnInit {
}
}


private async encryptRequest(request: EncryptPayloadRequestOutput): Promise<void> {
this.responseHandler = async (): Promise<void> => {
if (this.walletType === WalletType.LOCAL_MNEMONIC) {
await this.sendResponse(request, {})
} else {
await this.openLedgerModal(request)
}
}
}

private async operationRequest(request: OperationRequestOutput): Promise<void> {
this.transactions = await this.protocol.getAirGapTxFromWrappedOperations({
branch: '',
Expand All @@ -184,7 +212,7 @@ export class BeaconRequestPage implements OnInit {
}

private async openLedgerModal(
request: PermissionRequestOutput | OperationRequestOutput | SignPayloadRequestOutput | BroadcastRequestOutput
request: PermissionRequestOutput | OperationRequestOutput | SignPayloadRequestOutput | EncryptPayloadRequestOutput | BroadcastRequestOutput
): Promise<void> {
const modal = await this.modalController.create({
component: AddLedgerConnectionPage,
Expand Down Expand Up @@ -220,7 +248,7 @@ export class BeaconRequestPage implements OnInit {
}

private async sendResponse(
request: PermissionRequestOutput | OperationRequestOutput | SignPayloadRequestOutput | BroadcastRequestOutput,
request: PermissionRequestOutput | OperationRequestOutput | SignPayloadRequestOutput | EncryptPayloadRequestOutput | BroadcastRequestOutput,
extras: unknown
): Promise<void> {
const response: ExtensionMessageOutputPayload<Action.RESPONSE> = await this.chromeMessagingService.sendChromeMessage(
Expand Down Expand Up @@ -294,7 +322,7 @@ export class BeaconRequestPage implements OnInit {
}

private async sendAbortedError(
request: PermissionRequestOutput | OperationRequestOutput | SignPayloadRequestOutput | BroadcastRequestOutput
request: PermissionRequestOutput | OperationRequestOutput | SignPayloadRequestOutput | EncryptPayloadRequestOutput | BroadcastRequestOutput
): Promise<void> {
await this.sendResponse(request, {
errorType: BeaconErrorType.ABORTED_ERROR
Expand Down
8 changes: 7 additions & 1 deletion src/app/services/settings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ export class SettingsService {
[NetworkType.DELPHINET]: 'https://tezos-delphinet-node.prod.gke.papers.tech',
[NetworkType.EDONET]: 'https://tezos-edonet-node.prod.gke.papers.tech',
[NetworkType.FLORENCENET]: 'https://florence-tezos.giganode.io', // TODO: UPDATE TO PAPERS URL
[NetworkType.GRANADANET]: 'https://granada-tezos.giganode.io', // TODO: UPDATE TO PAPERS URL
[NetworkType.CUSTOM]: ''
}
const apiUrls: { [key in NetworkType]: string } = {
[NetworkType.MAINNET]: 'https://tezos-mainnet-conseil.prod.gke.papers.tech',
[NetworkType.DELPHINET]: 'https://tezos-delphinet-conseil.prod.gke.papers.tech',
[NetworkType.EDONET]: 'https://tezos-edonet-conseil.prod.gke.papers.tech',
[NetworkType.FLORENCENET]: '', // TODO: ADD CONSEIL URL
[NetworkType.GRANADANET]: '', // TODO: ADD CONSEIL URL
[NetworkType.CUSTOM]: ''
}

Expand All @@ -58,25 +60,29 @@ export class SettingsService {
[NetworkType.DELPHINET]: 'Delphinet',
[NetworkType.EDONET]: 'Edonet',
[NetworkType.FLORENCENET]: 'Florencenet',
[NetworkType.GRANADANET]: 'Granadanet',
[NetworkType.CUSTOM]: 'Custom'
}
const airgapNetworks: { [key in NetworkType]: AirGapNetworkType } = {
[NetworkType.MAINNET]: AirGapNetworkType.MAINNET,
[NetworkType.DELPHINET]: AirGapNetworkType.TESTNET,
[NetworkType.EDONET]: AirGapNetworkType.TESTNET,
[NetworkType.FLORENCENET]: AirGapNetworkType.TESTNET,
[NetworkType.GRANADANET]: AirGapNetworkType.TESTNET,
[NetworkType.CUSTOM]: AirGapNetworkType.CUSTOM
}
const blockExplorers: { [key in Exclude<NetworkType, NetworkType.DELPHINET>]: string } = {
[NetworkType.MAINNET]: 'https://tezblock.io',
[NetworkType.EDONET]: 'https://edonet.tezblock.io',
[NetworkType.FLORENCENET]: 'https://florencenet.tezblock.io',
[NetworkType.CUSTOM]: 'https://florencenet.tezblock.io'
[NetworkType.GRANADANET]: 'https://granadanet.tezblock.io',
[NetworkType.CUSTOM]: 'https://granadanet.tezblock.io'
}
const tezosNetworks: { [key in Exclude<NetworkType, NetworkType.DELPHINET>]: TezosNetwork } = {
[NetworkType.MAINNET]: TezosNetwork.MAINNET,
[NetworkType.EDONET]: TezosNetwork.EDONET,
[NetworkType.FLORENCENET]: TezosNetwork.EDONET, // TODO: UPDATE IN COINLIB
[NetworkType.GRANADANET]: TezosNetwork.EDONET, // TODO: UPDATE IN COINLIB
[NetworkType.CUSTOM]: TezosNetwork.EDONET
}

Expand Down
19 changes: 19 additions & 0 deletions src/extension/AirGapSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,25 @@ export class LocalSigner implements Signer {

return protocol.signMessage(message, { privateKey })
}

public async encryptAsync(message: string, mnemonic: string): Promise<string> {
logger.log('Encrypting Message:', message)

const protocol: TezosProtocol = new TezosProtocol()
const publicKey: string = await protocol.getPublicKeyFromMnemonic(mnemonic, protocol.standardDerivationPath)

return protocol.encryptAsymmetric(message, publicKey)
}

public async decryptAsync(message: string, mnemonic: string): Promise<string> {
logger.log('Decrypting Message:', message)

const protocol: TezosProtocol = new TezosProtocol()
const publicKey: string = await protocol.getPublicKeyFromMnemonic(mnemonic, protocol.standardDerivationPath)
const privateKey: Buffer = await protocol.getPrivateKeyFromMnemonic(mnemonic, protocol.standardDerivationPath)

return protocol.decryptAsymmetric(message, { publicKey, privateKey })
}
}

export class LedgerSigner implements Signer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ExtensionClient } from '../ExtensionClient'
import { Logger } from '../Logger'

import { broadcastRequestHandler } from './broadcast-request-handler'
import { encryptDecryptRequestHandler } from './encrypt-decrypt-request-handler'
import { errorResponseHandler } from './error-response-handler'
import { operationRequestHandler } from './operation-request-handler'
import { permissionRequestHandler } from './permission-request-handler'
Expand All @@ -28,10 +29,12 @@ export class BeaconMessageHandler {
[BeaconMessageType.PermissionRequest]: permissionRequestHandler(this.client, logger),
[BeaconMessageType.OperationRequest]: operationRequestHandler(this.client, logger),
[BeaconMessageType.SignPayloadRequest]: signPayloadRequestHandler(this.client, logger),
[BeaconMessageType.EncryptPayloadRequest]: encryptDecryptRequestHandler(this.client, logger),
[BeaconMessageType.BroadcastRequest]: broadcastRequestHandler(this.client, logger),
[BeaconMessageType.PermissionResponse]: beaconMessageHandlerNotSupported,
[BeaconMessageType.OperationResponse]: beaconMessageHandlerNotSupported,
[BeaconMessageType.SignPayloadResponse]: beaconMessageHandlerNotSupported,
[BeaconMessageType.EncryptPayloadResponse]: beaconMessageHandlerNotSupported,
[BeaconMessageType.BroadcastResponse]: beaconMessageHandlerNotSupported,
[BeaconMessageType.Acknowledge]: beaconMessageHandlerNotSupported,
[BeaconMessageType.Error]: errorResponseHandler(this.client, logger),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import {
BEACON_VERSION,
BeaconBaseMessage,
BeaconErrorType,
BeaconMessage,
BeaconMessageType,
getSenderId,
EncryptPayloadRequestOutput,
EncryptPayloadResponse,
EncryptPayloadResponseInput,
EncryptionType,
EncryptionOperation
} from '@airgap/beacon-sdk'
import { LocalSigner } from 'src/extension/AirGapSigner'

import { WalletInfo, WalletType } from '../Actions'
import { ExtensionClient } from '../ExtensionClient'
import { Logger } from '../Logger'
import { to, To } from '../utils'

import { BeaconMessageHandlerFunction } from './BeaconMessageHandler'

export const encryptDecryptRequestHandler: (client: ExtensionClient, logger: Logger) => BeaconMessageHandlerFunction = (
client: ExtensionClient,
logger: Logger
): BeaconMessageHandlerFunction => {
return async (
data: { request: BeaconBaseMessage; extras: unknown },
sendToPage: (message: BeaconMessage) => void,
sendResponseToPopup: (error?: unknown) => void
): Promise<void> => {
const encryptRequest: EncryptPayloadRequestOutput = (data.request as any) as EncryptPayloadRequestOutput
logger.log('encryptDecryptRequestHandler', data)

const sendError: (error: Error, errorType: BeaconErrorType) => Promise<void> = async (
error: Error,
errorType: BeaconErrorType
): Promise<void> => {
logger.log('error', error)
const responseInput = {
id: encryptRequest.id,
type: BeaconMessageType.EncryptPayloadResponse,
errorType
} as any

const response: EncryptPayloadResponse = {
beaconId: await getSenderId(await client.beaconId),
version: BEACON_VERSION,
...responseInput
}
sendToPage(response)
sendResponseToPopup({
error: { name: error.name, message: error.message, stack: error.stack }
})
}

const wallet: WalletInfo | undefined = await client.getWalletByAddress(encryptRequest.sourceAddress)
if (!wallet) {
await sendError(
{ name: 'Wallet Error', message: `No wallet found for address ${encryptRequest.sourceAddress}` },
BeaconErrorType.NO_PRIVATE_KEY_FOUND_ERROR
)

throw new Error('NO WALLET FOUND')
}

let encrypted: To<string> | undefined
if (wallet.type === WalletType.LOCAL_MNEMONIC) {
const localWallet: WalletInfo<WalletType.LOCAL_MNEMONIC> = wallet as WalletInfo<WalletType.LOCAL_MNEMONIC>
const signer: LocalSigner = new LocalSigner()
if (encryptRequest.encryptionType === EncryptionType.ASYMMETRIC) {
if (encryptRequest.cryptoOperation === EncryptionOperation.ENCRYPT) {
encrypted = await to(signer.encryptAsync(encryptRequest.payload, localWallet.info.mnemonic))
} else if (encryptRequest.cryptoOperation === EncryptionOperation.DECRYPT) {
encrypted = await to(signer.decryptAsync(encryptRequest.payload, localWallet.info.mnemonic))
} else {
throw new Error('INVALID ENCRYPTION OPERATION TYPE')
}
} else {
throw new Error('CANNOT HANDLE SYMMETRIC ENCRYPTION')
}
} else {
throw new Error('CANNOT ENCRYPT/DECRYPT WITH LEDGER')
// TODO: Is this possible?
// const signer: Signer = new LedgerSigner()
// signature = await to(signer.encryptMessage(encryptRequest.payload, wallet.derivationPath))
}

if (encrypted.err) {
await sendError(encrypted.err, BeaconErrorType.PARAMETERS_INVALID_ERROR)
throw encrypted.err
}

logger.log('signed: ', encrypted.res)

const responseInput: EncryptPayloadResponseInput = {
id: encryptRequest.id,
type: BeaconMessageType.EncryptPayloadResponse,
cryptoOperation: encryptRequest.cryptoOperation,
encryptionType: encryptRequest.encryptionType,
payload: encrypted.res
}

const response: EncryptPayloadResponse = {
senderId: await getSenderId(await client.beaconId),
version: BEACON_VERSION,
...responseInput
}

sendToPage(response)
sendResponseToPopup()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
OperationResponse,
PermissionRequestOutput,
Serializer,
SignPayloadRequestOutput
SignPayloadRequestOutput,
EncryptPayloadRequestOutput
} from '@airgap/beacon-sdk'
import { TezosWrappedOperation } from '@airgap/coinlib-core/protocols/tezos/types/TezosWrappedOperation'
import { AxiosResponse } from 'axios'
Expand Down Expand Up @@ -191,6 +192,18 @@ export class ToExtensionMessageHandler extends MessageHandler {

return request
}
case BeaconMessageType.EncryptPayloadRequest: {
const result: AppMetadata | undefined = await this.client.getAppMetadata(message.senderId)
if (!result) {
throw new Error('AppMetadata not available')
}
const request: EncryptPayloadRequestOutput = {
appMetadata: result,
...message
}

return request
}
case BeaconMessageType.BroadcastRequest: {
const result: AppMetadata | undefined = await this.client.getAppMetadata(message.senderId)
if (!result) {
Expand Down
Loading