Skip to content

Commit

Permalink
#2651 Update to openpgp v6 (#2653)
Browse files Browse the repository at this point in the history
* wip: update to openpgp v6

* fix: unit test

* fix: build issue

* feat: load flowcrypt  code in localhost

* fix: openpgp -v6

* fix: code style
  • Loading branch information
ioanmo226 authored Feb 21, 2025
1 parent 72cb26b commit 0949331
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 90 deletions.
90 changes: 20 additions & 70 deletions Core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"version": "0.0.1",
"description": "TypeScript core for FlowCrypt iOS internal use",
"dependencies": {
"@openpgp/web-stream-tools": "0.0.11",
"@openpgp/web-stream-tools": "^0.1.3",
"encoding-japanese": "^2.2.0",
"openpgp": "5.11.2",
"openpgp": "^6.0.1",
"sanitize-html": "^2.8.0",
"zxcvbn": "4.4.2"
},
Expand Down
6 changes: 3 additions & 3 deletions Core/source/core/pgp-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
UserID,
} from 'openpgp';
import { isFullyDecrypted, isFullyEncrypted } from './pgp';
import { MaybeStream, requireStreamReadToEnd } from '../platform/require';
import { requireStreamReadToEnd } from '../platform/require';
import { Str } from './common';

export interface PrvKeyInfo {
Expand Down Expand Up @@ -95,7 +95,7 @@ export class PgpKey {
userIDs: userIds,
passphrase,
format: 'armored',
curve: variant === 'curve25519' ? 'curve25519' : undefined,
curve: variant === 'curve25519' ? 'curve25519Legacy' : undefined,
rsaBits: variant === 'curve25519' ? undefined : variant === 'rsa2048' ? 2048 : 4096,
});
return { public: k.publicKey, private: k.privateKey, revCert: k.revocationCertificate };
Expand Down Expand Up @@ -442,7 +442,7 @@ export class PgpKey {
const readToEnd = await requireStreamReadToEnd();
return {
key,
revocationCertificate: await readToEnd(certificate as MaybeStream<string>),
revocationCertificate: await readToEnd(certificate as unknown as string),
};
}
};
Expand Down
8 changes: 6 additions & 2 deletions Core/source/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ test('generateKey', async t => {
expect(isFullyEncrypted(key)).to.be.true;
expect(isFullyDecrypted(key)).to.be.false;
/* eslint-enable @typescript-eslint/no-unused-expressions */
expect((json.key as { algo: string }).algo).to.deep.equal({ algorithm: 'eddsa', curve: 'ed25519', algorithmId: 22 });
expect((json.key as { algo: string }).algo).to.deep.equal({
algorithm: 'eddsaLegacy',
curve: 'ed25519Legacy',
algorithmId: 22,
});
expectNoData(data);
t.pass();
});
Expand Down Expand Up @@ -662,7 +666,7 @@ test.serial('parseKeys - revoked', async t => {
keywords: 'GALLERY PROTECT TIME CANDY BLEAK ACCESS',
},
],
algo: { algorithm: 'eddsa', curve: 'ed25519', algorithmId: 22 },
algo: { algorithm: 'eddsaLegacy', curve: 'ed25519Legacy', algorithmId: 22 },
created: 1634664782,
lastModified: 1634664811,
revoked: true,
Expand Down
1 change: 1 addition & 0 deletions Core/webpack.bare.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module.exports = {
resolve: {
alias: {
openpgp: path.resolve(__dirname, './node_modules/openpgp/dist/openpgp.mjs'),
'@openpgp/web-stream-tools': path.resolve(__dirname, './node_modules/@openpgp/web-stream-tools/lib/index.js'),
},
fallback: {
stream: false,
Expand Down
4 changes: 4 additions & 0 deletions FlowCrypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
958A45A32B10E77F00FFBA56 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 958A45A22B10E77F00FFBA56 /* SwiftSoup */; };
95E014CF2A8BF27C00D4B4F5 /* AvatarCheckboxNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95E014CE2A8BF27C00D4B4F5 /* AvatarCheckboxNode.swift */; };
95F55F9F2A7B8A720000E50F /* ProgressHUD in Frameworks */ = {isa = PBXBuildFile; productRef = 95F55F9E2A7B8A720000E50F /* ProgressHUD */; };
97FB28CB2D64945E000B8F67 /* simple_webview_file.html in Resources */ = {isa = PBXBuildFile; fileRef = 97FB28CA2D64945E000B8F67 /* simple_webview_file.html */; };
9F003D6125E1B4ED00EB38C0 /* TrashFolderProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F003D6025E1B4ED00EB38C0 /* TrashFolderProvider.swift */; };
9F003D6D25EA8F3200EB38C0 /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F003D6C25EA8F3200EB38C0 /* SessionManager.swift */; };
9F0C3C102316DD5B00299985 /* GoogleAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C0F2316DD5B00299985 /* GoogleAuthManager.swift */; };
Expand Down Expand Up @@ -666,6 +667,7 @@
9582BC592A782DA700439728 /* pass_phrase_hint.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = pass_phrase_hint.html; sourceTree = "<group>"; };
958566B82A612822001C84D3 /* ASButtonNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASButtonNode.swift; sourceTree = "<group>"; };
95E014CE2A8BF27C00D4B4F5 /* AvatarCheckboxNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarCheckboxNode.swift; sourceTree = "<group>"; };
97FB28CA2D64945E000B8F67 /* simple_webview_file.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = simple_webview_file.html; sourceTree = "<group>"; };
9F003D6025E1B4ED00EB38C0 /* TrashFolderProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrashFolderProvider.swift; sourceTree = "<group>"; };
9F003D6C25EA8F3200EB38C0 /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
9F003D9D25EA910B00EB38C0 /* LocalStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalStorageTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1908,6 +1910,7 @@
51E8181B29081D2B00FD428D /* generated */,
C132B9BC1EC2DBD800763715 /* LaunchScreen.storyboard */,
A33BAA8322EA4B4F00CC1B5C /* flowcrypt-ios-icon.png */,
97FB28CA2D64945E000B8F67 /* simple_webview_file.html */,
9582BC592A782DA700439728 /* pass_phrase_hint.html */,
51E1673C270DAFF900D27C52 /* Localizable.stringsdict */,
9F71630A234FC73E0031645E /* Localizable.strings */,
Expand Down Expand Up @@ -2567,6 +2570,7 @@
C132B9BE1EC2DBD800763715 /* LaunchScreen.storyboard in Resources */,
51E8181D29081D2B00FD428D /* flowcrypt-ios-prod.js.txt in Resources */,
51E1673D270DAFF900D27C52 /* Localizable.stringsdict in Resources */,
97FB28CB2D64945E000B8F67 /* simple_webview_file.html in Resources */,
C132B9BB1EC2DBD800763715 /* Assets.xcassets in Resources */,
9F5EF7012753DA7000CC3F88 /* UI Guidance.md in Resources */,
9582BC5A2A782DA700439728 /* pass_phrase_hint.html in Resources */,
Expand Down
52 changes: 41 additions & 11 deletions FlowCrypt/Core/Core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,17 @@ class Core: KeyDecrypter, KeyParser, CoreComposeMessageType {
userController.add(self.coreMessageHandler, name: "coreHost")
let configuration = WKWebViewConfiguration()
configuration.userContentController = userController
configuration.defaultWebpagePreferences.allowsContentJavaScript = true
self.webView = WKWebView(frame: .zero, configuration: configuration)
self.webView.navigationDelegate = self.coreMessageHandler

guard let jsFileSrc = self.getCoreJsFile() else { return }
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "[unknown version]"
self.webView.evaluateJavaScript("const APP_VERSION = 'iOS \(appVersion)';\(jsFileSrc)") { _, _ in }
}
// Load a simple HTML file in the web view and run the `flowcrypt-ios-prod.js.txt` code.
// This mechanism is used because the SubtleCrypto API is only available in secure contexts,
// and `file://` URLs are considered secure.
// More info: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts

private func getCoreJsFile() -> String? {
guard let jsFile = Bundle(for: Self.self).path(
forResource: "flowcrypt-ios-prod.js.txt",
ofType: nil
) else { return nil }
return try? String(contentsOfFile: jsFile)
let url = Bundle.main.url(forResource: "simple_webview_file", withExtension: "html")!
webView.load(URLRequest(url: url))
}

// MARK: - Config
Expand Down Expand Up @@ -264,13 +262,30 @@ class Core: KeyDecrypter, KeyParser, CoreComposeMessageType {
return try r.json.decodeJson(as: CoreRes.ZxcvbnStrengthBar.self)
}

private func waitUntilJavascirptReady(timeout: TimeInterval = 1) async throws {
let functionName = "handleRequestFromHost"
let deadline = Date().addingTimeInterval(timeout)
while Date() < deadline {
if (try? await webView.callAsyncJavaScript(
"return typeof \(functionName) === 'function';",
arguments: [:],
contentWorld: .page
)) as? Bool == true {
return
}
try await Task.sleep(nanoseconds: 100_000_000) // 100ms
}
throw CoreError.value("JavaScript function \(functionName) is not available within \(timeout) seconds.")
}

// MARK: Private calls
@discardableResult
private func call(_ endpoint: String, params: [String: Any?] = [:], data: Data = Data(), retryAttempt: Int = 0) async throws -> RawRes {
let paramsData = try JSONSerialization.data(withJSONObject: params).toStr()
let requestData = [UInt8](data)

do {
try await waitUntilJavascirptReady()
let response = try await webView.callAsyncJavaScript(
"return handleRequestFromHost(\"\(endpoint)\", \(paramsData), \(requestData))",
arguments: [:],
Expand Down Expand Up @@ -315,7 +330,7 @@ class Core: KeyDecrypter, KeyParser, CoreComposeMessageType {
}
}

class CoreMessageHandler: NSObject, WKScriptMessageHandler {
class CoreMessageHandler: NSObject, WKScriptMessageHandler, WKNavigationDelegate {
private lazy var logger = Logger.nested("Core")

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
Expand All @@ -327,6 +342,21 @@ class CoreMessageHandler: NSObject, WKScriptMessageHandler {
logger.logDebug(logMessage)
}
}


private func getCoreJsFile() -> String? {
guard let jsFile = Bundle(for: Self.self).path(
forResource: "flowcrypt-ios-prod.js.txt",
ofType: nil
) else { return nil }
return try? String(contentsOfFile: jsFile)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
guard let jsFileSrc = self.getCoreJsFile() else { return }
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "[unknown version]"
webView.evaluateJavaScript("const APP_VERSION = 'iOS \(appVersion)';\(jsFileSrc)") { _, _ in }
}

}

private extension Encodable {
Expand Down
4 changes: 2 additions & 2 deletions FlowCrypt/Resources/generated/flowcrypt-ios-prod.js.txt

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions FlowCrypt/Resources/simple_webview_file.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!doctype html>
<html>
<head>
<title>Simple HTML file to load the flowcrypt-ios-prod.js.txt file in a web view.</title>
</head>
<body></body>
</html>

0 comments on commit 0949331

Please sign in to comment.