mirror of
https://github.com/localsend/localsend.git
synced 2026-08-07 07:14:52 +00:00
refactor: extract cert generation to core/
This commit is contained in:
@@ -166,7 +166,7 @@ class PersistenceService {
|
||||
}
|
||||
|
||||
if (prefs.getString(_securityContext) == null) {
|
||||
await prefs.setString(_securityContext, jsonEncode(generateSecurityContext()));
|
||||
await prefs.setString(_securityContext, jsonEncode(await generateSecurityContext()));
|
||||
}
|
||||
|
||||
if (isFirstAppStart) {
|
||||
|
||||
@@ -30,7 +30,7 @@ class SecurityService extends ReduxNotifier<StoredSecurityContext> {
|
||||
class ResetSecurityContextAction extends AsyncReduxAction<SecurityService, StoredSecurityContext> {
|
||||
@override
|
||||
Future<StoredSecurityContext> reduce() async {
|
||||
final securityContext = generateSecurityContext();
|
||||
final securityContext = await generateSecurityContext();
|
||||
await notifier._persistence.setSecurityContext(securityContext);
|
||||
return securityContext;
|
||||
}
|
||||
|
||||
@@ -1,67 +1,17 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:basic_utils/basic_utils.dart';
|
||||
import 'package:convert/convert.dart';
|
||||
import 'package:localsend_isolates/model/stored_security_context.dart';
|
||||
import 'package:localsend_isolates/rust/api/crypto.dart' as rust;
|
||||
|
||||
/// Generates a random [SecurityContextResult].
|
||||
StoredSecurityContext generateSecurityContext([AsymmetricKeyPair? keyPair]) {
|
||||
keyPair ??= CryptoUtils.generateRSAKeyPair();
|
||||
final privateKey = keyPair.privateKey as RSAPrivateKey;
|
||||
final publicKey = keyPair.publicKey as RSAPublicKey;
|
||||
final dn = {
|
||||
'CN': 'LocalSend User',
|
||||
'O': '',
|
||||
'OU': '',
|
||||
'L': '',
|
||||
'S': '',
|
||||
'C': '',
|
||||
};
|
||||
final csr = X509Utils.generateRsaCsrPem(dn, privateKey, publicKey);
|
||||
final certificate = X509Utils.generateSelfSignedCertificate(keyPair.privateKey, csr, 365 * 10);
|
||||
|
||||
final hash = calculateHashOfCertificate(certificate);
|
||||
final spki = extractPublicKeyFromCertificate(certificate);
|
||||
|
||||
/// Generates a random [StoredSecurityContext].
|
||||
Future<StoredSecurityContext> generateSecurityContext() async {
|
||||
final result = await rust.generateSecurityContext();
|
||||
return StoredSecurityContext(
|
||||
privateKey: CryptoUtils.encodeRSAPrivateKeyToPemPkcs1(privateKey),
|
||||
publicKey: spki,
|
||||
certificate: certificate,
|
||||
certificateHash: hash,
|
||||
privateKey: result.privateKey,
|
||||
publicKey: result.publicKey,
|
||||
certificate: result.certificate,
|
||||
certificateHash: result.certificateHash,
|
||||
);
|
||||
}
|
||||
|
||||
/// Calculates the hash of a certificate.
|
||||
String calculateHashOfCertificate(String certificate) {
|
||||
// Convert PEM to DER
|
||||
final pemContent = certificate.replaceAll('\r\n', '\n').split('\n').where((line) => line.isNotEmpty && !line.startsWith('---')).join();
|
||||
final der = base64Decode(pemContent);
|
||||
|
||||
// Calculate hash
|
||||
return CryptoUtils.getHash(
|
||||
Uint8List.fromList(der),
|
||||
algorithmName: 'SHA-256',
|
||||
);
|
||||
}
|
||||
|
||||
String extractPublicKeyFromCertificate(String certificate) {
|
||||
final cert = X509Utils.x509CertificateFromPem(certificate);
|
||||
final publicHex = cert.tbsCertificate!.subjectPublicKeyInfo.bytes!;
|
||||
return _hexToSpkiPem(publicHex);
|
||||
}
|
||||
|
||||
String _hexToSpkiPem(String hexBytes) {
|
||||
final publicBytes = hex.decode(hexBytes);
|
||||
final publicBase64 = base64Encode(publicBytes);
|
||||
final temp =
|
||||
'''-----BEGIN PUBLIC KEY-----
|
||||
$publicBase64
|
||||
-----END PUBLIC KEY-----''';
|
||||
return X509Utils.fixPem(temp);
|
||||
}
|
||||
|
||||
/// Verifies a certificate with a public key.
|
||||
/// Throws an exception if the certificate is invalid.
|
||||
Future<void> verifyCertificate({
|
||||
|
||||
+1
-17
@@ -73,14 +73,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.1"
|
||||
basic_utils:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: basic_utils
|
||||
sha256: "548047bef0b3b697be19fa62f46de54d99c9019a69fb7db92c69e19d87f633c7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.8.2"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -226,7 +218,7 @@ packages:
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
convert:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
@@ -1263,14 +1255,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -10,10 +10,8 @@ environment:
|
||||
sdk: ^3.11.0
|
||||
|
||||
dependencies:
|
||||
basic_utils: 5.8.2
|
||||
collection: ^1.17.2 # allow newer versions, so it can compile with newer Flutter versions
|
||||
connectivity_plus: 7.2.0
|
||||
convert: 3.1.2
|
||||
dart_mappable: 4.8.0
|
||||
desktop_drop: 0.7.1
|
||||
device_apps: 2.2.0
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
import 'package:basic_utils/basic_utils.dart';
|
||||
import 'package:localsend_app/util/security_helper.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('generateSecurityContext', () {
|
||||
// certificate is dependent on time, so we skip this test
|
||||
test('should generate a security context', () {
|
||||
const modulus =
|
||||
'17655370025740741038113156454854199388193503449104440877037318492305460691763108986359629213738068251658702764711191809795804339624629386590749322066892437190139062703234048708020881575102122778994526543290880868262952627726488977960776519938694907994518427540419624537894956809241753063372413058294447378662255052300193831939738828105579615087656442141880311261067266950751778487270744852541404898857201772242498437323791185553734672978477936608290952211534467577849886694581791190994405933665028620559373744808123499229584489594678456882464781972729864955090923226165749030219906764402228914184366893373631422503021';
|
||||
const privateExponent =
|
||||
'14421008176265737041842552728760854897987189421761902811979258986703749275840685218255600827633436556136869519473805770330945910011841184271318521744401163544644005150782334580299156378790480712915696482733480991790654395453357362699213084041660086971551428119477591606848878236042545176547425781216107887562053872086603693550866169434312772263069345523523039280590638079481520003143359443743141464111595442124530326974852967024986370324441963907065113439224776888067662971143404975436160178871895218249902778285379960904695389191962540588337134680308483675532942364276364403564632886857709821444086544738315317174145';
|
||||
const privateP =
|
||||
'178524065934350447993678840182944627942309006340524319909851831752106332954026000610471033106817833873729348468467401050633468292518369706764968836071362023882930741070266054692417392199462715540565991951570137107225695543150432250384310373010291328019775467135529294964213555809388513634733256540639643918349';
|
||||
const privateQ =
|
||||
'98896302486373120089171491460892017227056533661820429564718404666288200233919205658787592871460875274200632466484814562010402123837563042503616158962868253836369412258319855713924996179145010239261725685588005640861685432715579085801665010203646507394652948031357507454692937609788924433344012456182622364129';
|
||||
const publicExponent = '65537';
|
||||
|
||||
final keyPair = AsymmetricKeyPair(
|
||||
RSAPublicKey(
|
||||
BigInt.parse(modulus),
|
||||
BigInt.parse(publicExponent),
|
||||
),
|
||||
RSAPrivateKey(
|
||||
BigInt.parse(modulus),
|
||||
BigInt.parse(privateExponent),
|
||||
BigInt.parse(privateP),
|
||||
BigInt.parse(privateQ),
|
||||
),
|
||||
);
|
||||
|
||||
final context = generateSecurityContext(keyPair);
|
||||
expect(
|
||||
context.privateKey,
|
||||
'''-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAi9uDMYRn63SZtEPGRogZGdu5XXBAoQeMO60mycoinqLKDWyZ
|
||||
dpMo+XWY3wYVhoAyxgzDOcPjIf+Uq1oEy/0K4WwfpbK8SCy851qgYkfMCT9D9mFv
|
||||
XwWoULJCUHFF7f947ArDE1nmuK1nNx2RodN2wJCXyzPjw0jn06bwGeg0EqfUC8wv
|
||||
W4FTZ6t1tErzmRqRdMUWuCJwsk1IMbDbFePhiK5jecOBG0RVVWLuw+TkuX8TUgrp
|
||||
IktH2+qEM1KdLyAMnL71hx2wMvE+lDKFKK9p37zXK8omjl+VgTC8ocjGeYDDsl43
|
||||
ZtW09V0pb7Vz2FM8b7BgM06kvJl48PIe5puYbQIDAQABAoIBAHI8hAW/TA7j9+Rp
|
||||
c5wK8M3RTrCGzxN5Ec9w2Hz80ZhYCcg7S4KyI0bYCl0pIA3zywVASXp2iaEsnSf1
|
||||
aHOipe+JHLSAsRAXEtm6icSdNojbF005OvoXqer4H/iK/X6wLPpItByrUyzH3sYD
|
||||
LgBLtPUHZiWBpenONCYKdpYCt/3/uyAeC4qj+sBlfN5Fio2A86qIgntg3n6he8Cd
|
||||
Yq0cjyEGzRnIa9EUDHoHUU30oPPj16LkK0uZxR7H+UKZkYNXQUGtPt0cnAvgS/2f
|
||||
UnOAIZop1r9NkS7sntg4l9iuDqI4xYMg9wtBStrgLx65nV1l4HDNZGq/LDo3msam
|
||||
NQoeX4ECgYEA/joJk42iTF3D2IJjFzMf539V9D/1OaXcxmiLMODsIW8ZcyKRRmzD
|
||||
NIBfMxX/0+X8IgkMwkcnZWOFMWw+2FDgfz2l+PMACJvGEGokxFDiJe68W4KAy8dr
|
||||
uB0M0ydhRWLG4ZD6U+gvwyqqlMNpEosCH0fJilag+tW/fx6ti2MBYA0CgYEAjNVA
|
||||
HItnlpnOmzOuyQob9kqE/8eDTSeX5ZFD/OZOypvLPukdg6qWnzaBzUgSJg/Tl9RD
|
||||
XWE9WgKc2Gpki7/EYvtRW4YwBcoZKBTkAio4t8WBRmLT6L3PUKKdJHOtFbmwlUPw
|
||||
mfw9HdJG/TgDiTlyAdTbuGhGY/unuUdPMvt2oeECgYEApthqHoeOo3XKKZbw93Hb
|
||||
F3AvdhxfkVT0ftZvu0VyU0L5veFK3KBWwGcbk4h1nJjMj33G/N370gOtj1EOMaNq
|
||||
ordP7QF13TB2naE7vgejU+fJgHk2lAauAGg4WX/3y7TW94TRdS3l4r1mtDlHBR9r
|
||||
5iGT+JGAFv8fLYtxtA/nACUCgYAb6Gpi/bESY/pQQSaiyjEOVmgSs7uuP2lXYbkC
|
||||
VbVJayQUnGdv3w8oD8obHuwRxNMeZD7RM2LQAnKIZFT2aJMHNlxB8c50Zz8i9TjV
|
||||
wP4qVKYwh4cMuQhrJz5SqeWjx39ZpPP538VQsonExiPVPp/8Au1jlq5UQ9tR2PK1
|
||||
3KT+oQKBgQCkmNd8ZHKBoIbG3oV0T4f4IFk8424QD5hQX7Zmb7gxwnCgQScOPBM/
|
||||
tk4v/rwNoiO9EUW4w4zZZIlvcFJu38+9pPX+rTFxGh6TZ6aRvw7962m2RBmqgYcq
|
||||
IWwwBbDLI6KuU/iqqvk/1syLDHqeaCdDqTmmyoaKKa7kUhkZkhIlLw==
|
||||
-----END RSA PRIVATE KEY-----''',
|
||||
);
|
||||
|
||||
expect(
|
||||
context.publicKey,
|
||||
'''-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi9uDMYRn63SZtEPGRogZ
|
||||
Gdu5XXBAoQeMO60mycoinqLKDWyZdpMo+XWY3wYVhoAyxgzDOcPjIf+Uq1oEy/0K
|
||||
4WwfpbK8SCy851qgYkfMCT9D9mFvXwWoULJCUHFF7f947ArDE1nmuK1nNx2RodN2
|
||||
wJCXyzPjw0jn06bwGeg0EqfUC8wvW4FTZ6t1tErzmRqRdMUWuCJwsk1IMbDbFePh
|
||||
iK5jecOBG0RVVWLuw+TkuX8TUgrpIktH2+qEM1KdLyAMnL71hx2wMvE+lDKFKK9p
|
||||
37zXK8omjl+VgTC8ocjGeYDDsl43ZtW09V0pb7Vz2FM8b7BgM06kvJl48PIe5puY
|
||||
bQIDAQAB
|
||||
-----END PUBLIC KEY-----''',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('calculateHashOfCertificate', () {
|
||||
test('should calculate hash of certificate', () {
|
||||
const certificate = '''-----BEGIN CERTIFICATE-----
|
||||
MIIDGTCCAgGgAwIBAgIBATANBgkqhkiG9w0BAQsFADBQMRcwFQYDVQQDEw5Mb2Nh
|
||||
bFNlbmQgVXNlcjEJMAcGA1UEChMAMQkwBwYDVQQLEwAxCTAHBgNVBAcTADEJMAcG
|
||||
A1UECBMAMQkwBwYDVQQGEwAwHhcNMjMwNDIxMjM0NTM3WhcNMzMwNDE4MjM0NTM3
|
||||
WjBQMRcwFQYDVQQDEw5Mb2NhbFNlbmQgVXNlcjEJMAcGA1UEChMAMQkwBwYDVQQL
|
||||
EwAxCTAHBgNVBAcTADEJMAcGA1UECBMAMQkwBwYDVQQGEwAwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQCL24MxhGfrdJm0Q8ZGiBkZ27ldcEChB4w7rSbJ
|
||||
yiKeosoNbJl2kyj5dZjfBhWGgDLGDMM5w+Mh/5SrWgTL/QrhbB+lsrxILLznWqBi
|
||||
R8wJP0P2YW9fBahQskJQcUXt/3jsCsMTWea4rWc3HZGh03bAkJfLM+PDSOfTpvAZ
|
||||
6DQSp9QLzC9bgVNnq3W0SvOZGpF0xRa4InCyTUgxsNsV4+GIrmN5w4EbRFVVYu7D
|
||||
5OS5fxNSCukiS0fb6oQzUp0vIAycvvWHHbAy8T6UMoUor2nfvNcryiaOX5WBMLyh
|
||||
yMZ5gMOyXjdm1bT1XSlvtXPYUzxvsGAzTqS8mXjw8h7mm5htAgMBAAEwDQYJKoZI
|
||||
hvcNAQELBQADggEBAIs+T8Nkbl0gecT22CKW9/jMvUS1PGAyMqlwP8fNTsyv2xE9
|
||||
hLsyUrxsscuv+HGJu6Cz1R3hLI8YY5jEShmaelI0stlLahH9Fbm43EZuadGXOVKZ
|
||||
gMrNzQqLY5lec55rmS17GJlkm5opidkq4OlsCHfrBJitX6071atb0B1cdAjysWwV
|
||||
x40mnwq0TmYgBLDhWaM4/ZfZQRJQpPCtBJO06Nk7gTPiqJGJU5iEaz1PLvARq69o
|
||||
bJobSekf9tx3uwOIfioaoQvX0khkZ3ljFuNUpW3IE87OfPnYJQhu5xsTx00wi+Ce
|
||||
x64ghD4CzRa7wYsOjeb8cUUDMSj030NO9fBGVtA=
|
||||
-----END CERTIFICATE-----''';
|
||||
|
||||
final hash = calculateHashOfCertificate(certificate);
|
||||
expect(hash, '247E5F7CF21DE14438EAE733E07AC5440593D0612570C7413674130608DF69A9');
|
||||
});
|
||||
});
|
||||
}
|
||||
Generated
+1
@@ -1812,6 +1812,7 @@ dependencies = [
|
||||
"pem 3.0.6",
|
||||
"percent-encoding",
|
||||
"rand 0.9.5",
|
||||
"rcgen 0.14.8",
|
||||
"reqwest",
|
||||
"rsa",
|
||||
"rustls",
|
||||
|
||||
@@ -86,20 +86,14 @@ impl Identity {
|
||||
}
|
||||
|
||||
fn generate(alias: String, port: u16) -> anyhow::Result<Self> {
|
||||
let key_pair = rcgen::KeyPair::generate()?;
|
||||
let mut params = rcgen::CertificateParams::default();
|
||||
params.distinguished_name = rcgen::DistinguishedName::new();
|
||||
params
|
||||
.distinguished_name
|
||||
.push(rcgen::DnType::CommonName, "LocalSend User");
|
||||
let cert = params.self_signed(&key_pair)?;
|
||||
let cert = localsend::crypto::cert::generate_self_signed()?;
|
||||
|
||||
Ok(Self {
|
||||
alias,
|
||||
port,
|
||||
fingerprint: fingerprint_from_cert_der(cert.der()),
|
||||
cert_pem: cert.pem(),
|
||||
key_pem: key_pair.serialize_pem(),
|
||||
fingerprint: cert.fingerprint,
|
||||
cert_pem: cert.certificate_pem,
|
||||
key_pem: cert.private_key_pem,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Generated
+37
-4
@@ -190,6 +190,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -549,7 +558,7 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
"rand 0.9.2",
|
||||
"rand_core 0.6.4",
|
||||
"rcgen",
|
||||
"rcgen 0.13.2",
|
||||
"ring",
|
||||
"rustls",
|
||||
"sec1",
|
||||
@@ -1262,7 +1271,7 @@ dependencies = [
|
||||
"pem",
|
||||
"percent-encoding",
|
||||
"rand 0.9.2",
|
||||
"rcgen",
|
||||
"rcgen 0.14.8",
|
||||
"reqwest",
|
||||
"rsa",
|
||||
"rustls",
|
||||
@@ -1756,7 +1765,21 @@ dependencies = [
|
||||
"rustls-pki-types",
|
||||
"time",
|
||||
"x509-parser 0.16.0",
|
||||
"yasna",
|
||||
"yasna 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rcgen"
|
||||
version = "0.14.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57f6d249aad744e274e682777a50283a225a32705394ee6d5fcc01efa25e4055"
|
||||
dependencies = [
|
||||
"pem",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"time",
|
||||
"x509-parser 0.18.0",
|
||||
"yasna 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2908,7 +2931,7 @@ dependencies = [
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"rand 0.9.2",
|
||||
"rcgen",
|
||||
"rcgen 0.13.2",
|
||||
"regex",
|
||||
"ring",
|
||||
"rtcp",
|
||||
@@ -3418,6 +3441,16 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yasna"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
|
||||
@@ -18,6 +18,7 @@ hyper-util = { version = "0.1.19", features = ["server"], optional = true }
|
||||
lru = "0.16.3"
|
||||
pem = { version = "3.0.6", optional = true }
|
||||
percent-encoding = { version = "2.3", optional = true }
|
||||
rcgen = { version = "0.14", default-features = false, features = ["crypto", "pem", "ring"], optional = true }
|
||||
reqwest = { version = "0.13.1", features = ["charset", "http2", "system-proxy", "json", "rustls-no-provider", "stream", "webpki-roots"], default-features = false, optional = true }
|
||||
rand = "0.9.1"
|
||||
rsa = { version = "0.9.8", optional = true }
|
||||
@@ -39,14 +40,19 @@ uuid = { version = "1.20.0", features = ["serde", "v4"] }
|
||||
webrtc = { version = "0.14.0", optional = true }
|
||||
x509-parser = { version = "0.18.0", features = ["verify"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rcgen = "0.13.2"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
crypto = ["ed25519-dalek", "rsa", "sha2", "tokio-util"]
|
||||
crypto = ["ed25519-dalek", "rcgen", "rsa", "sha2", "tokio-util"]
|
||||
http = ["crypto", "form_urlencoded", "http-body-util", "hyper", "hyper-util", "pem", "percent-encoding", "reqwest", "rustls", "socket2", "tokio-rustls", "tokio-util", "x509-parser"]
|
||||
multicast = ["if-addrs", "socket2", "tokio-util"]
|
||||
webrtc-signaling = ["tokio-tungstenite"]
|
||||
webrtc = ["crypto", "flate2", "dep:webrtc", "webrtc-signaling", "x509-parser"]
|
||||
full = ["crypto", "http", "multicast", "webrtc"]
|
||||
|
||||
# RSA key generation is bignum-heavy and takes ~10x longer unoptimized;
|
||||
# keep the crypto crates optimized in dev so tests stay fast.
|
||||
[profile.dev.package.rsa]
|
||||
opt-level = 2
|
||||
|
||||
[profile.dev.package.num-bigint-dig]
|
||||
opt-level = 2
|
||||
|
||||
@@ -4,6 +4,57 @@ use x509_parser::certificate::X509Certificate;
|
||||
use x509_parser::pem::Pem;
|
||||
use x509_parser::x509::SubjectPublicKeyInfo;
|
||||
|
||||
/// A freshly generated device identity: an RSA-2048 key pair and a
|
||||
/// self-signed certificate whose SHA-256 fingerprint identifies the device.
|
||||
pub struct SelfSignedCert {
|
||||
/// The private key, PEM-encoded (PKCS#8).
|
||||
pub private_key_pem: String,
|
||||
/// The public key, PEM-encoded (SPKI).
|
||||
pub public_key_pem: String,
|
||||
/// The self-signed certificate, PEM-encoded.
|
||||
pub certificate_pem: String,
|
||||
/// The SHA-256 fingerprint of the certificate in DER format,
|
||||
/// encoded as uppercase hex (see [fingerprint_from_cert_der]).
|
||||
pub fingerprint: String,
|
||||
}
|
||||
|
||||
/// Generates a new device identity, used for both the HTTP server and client
|
||||
/// certificates.
|
||||
///
|
||||
/// - RSA-2048, matching the certificates the Flutter app has historically
|
||||
/// generated in Dart.
|
||||
/// - `CN=LocalSend User` and no SANs: peers identify each other purely by the
|
||||
/// certificate fingerprint, so the name carries no information.
|
||||
/// - The serial number is derived from the hash of the public key
|
||||
/// (rcgen's default when no serial number is set).
|
||||
/// - Validity is rcgen's default (1975 to 4096), so certificates do not expire
|
||||
/// in practice and never need to be rotated for time reasons.
|
||||
pub fn generate_self_signed() -> anyhow::Result<SelfSignedCert> {
|
||||
use rsa::pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding};
|
||||
|
||||
let mut rng = rsa::rand_core::OsRng;
|
||||
let private_key = rsa::RsaPrivateKey::new(&mut rng, 2048)?;
|
||||
let private_key_pem = private_key.to_pkcs8_pem(LineEnding::LF)?;
|
||||
let public_key_pem = private_key
|
||||
.to_public_key()
|
||||
.to_public_key_pem(LineEnding::LF)?;
|
||||
|
||||
let key_pair = rcgen::KeyPair::try_from(private_key.to_pkcs8_der()?.as_bytes())?;
|
||||
let mut params = rcgen::CertificateParams::default();
|
||||
params.distinguished_name = rcgen::DistinguishedName::new();
|
||||
params
|
||||
.distinguished_name
|
||||
.push(rcgen::DnType::CommonName, "LocalSend User");
|
||||
let certificate = params.self_signed(&key_pair)?;
|
||||
|
||||
Ok(SelfSignedCert {
|
||||
private_key_pem: private_key_pem.to_string(),
|
||||
public_key_pem,
|
||||
certificate_pem: certificate.pem(),
|
||||
fingerprint: fingerprint_from_cert_der(certificate.der()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn verify_cert_from_pem(cert: String, public_key: Option<&str>) -> anyhow::Result<()> {
|
||||
let (cert_pem, _) = Pem::read(Cursor::new(cert.into_bytes()))?;
|
||||
let parsed_cert: X509Certificate = cert_pem.parse_x509()?;
|
||||
@@ -188,6 +239,40 @@ nidU/qXQvBJ7NPUkXXgbcgqxK735iijOqQHmKts=
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_self_signed() {
|
||||
let generated = generate_self_signed().unwrap();
|
||||
|
||||
assert!(generated
|
||||
.private_key_pem
|
||||
.starts_with("-----BEGIN PRIVATE KEY-----"));
|
||||
assert!(generated
|
||||
.public_key_pem
|
||||
.starts_with("-----BEGIN PUBLIC KEY-----"));
|
||||
|
||||
// The certificate is self-consistent and carries the generated public key.
|
||||
verify_cert_from_pem(
|
||||
generated.certificate_pem.clone(),
|
||||
Some(&generated.public_key_pem),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The fingerprint matches the DER bytes of the certificate.
|
||||
let (cert_pem, _) =
|
||||
Pem::read(Cursor::new(generated.certificate_pem.as_bytes().to_vec())).unwrap();
|
||||
assert_eq!(
|
||||
generated.fingerprint,
|
||||
fingerprint_from_cert_der(&cert_pem.contents)
|
||||
);
|
||||
|
||||
// The public key extracted from the certificate is the generated one.
|
||||
let extracted = public_key_from_cert_der(&cert_pem.contents).unwrap();
|
||||
assert_eq!(
|
||||
extracted.replace("\r\n", "\n").trim(),
|
||||
generated.public_key_pem.trim()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fingerprint_from_cert_der() {
|
||||
let (cert_pem, _) = Pem::read(Cursor::new(CERT.as_bytes().to_vec())).unwrap();
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures_util::StreamExt;
|
||||
use localsend::crypto::cert::fingerprint_from_cert_der;
|
||||
use localsend::http::client::{ClientError, LsHttpClientV2};
|
||||
use localsend::http::dto::ProtocolType;
|
||||
use localsend::http::dto_v2::{PrepareUploadRequestDtoV2, ProtocolTypeV2, RegisterDtoV2};
|
||||
@@ -30,16 +29,12 @@ struct Identity {
|
||||
}
|
||||
|
||||
fn generate_identity() -> Identity {
|
||||
let key_pair = rcgen::KeyPair::generate().unwrap();
|
||||
let cert = rcgen::CertificateParams::new(vec!["LocalSend User".to_string()])
|
||||
.unwrap()
|
||||
.self_signed(&key_pair)
|
||||
.unwrap();
|
||||
let cert = localsend::crypto::cert::generate_self_signed().unwrap();
|
||||
|
||||
Identity {
|
||||
cert: cert.pem(),
|
||||
private_key: key_pair.serialize_pem(),
|
||||
fingerprint: fingerprint_from_cert_der(cert.der()),
|
||||
cert: cert.certificate_pem,
|
||||
private_key: cert.private_key_pem,
|
||||
fingerprint: cert.fingerprint,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@ Future<void> verifyCert({required String cert, required String publicKey}) =>
|
||||
|
||||
Future<KeyPair> generateKeyPair() => RustLib.instance.api.crateApiCryptoGenerateKeyPair();
|
||||
|
||||
/// Generates a new device identity: an RSA-2048 key pair and a self-signed
|
||||
/// certificate whose SHA-256 fingerprint identifies the device.
|
||||
Future<SecurityContext> generateSecurityContext() => RustLib.instance.api.crateApiCryptoGenerateSecurityContext();
|
||||
|
||||
/// Computes the SHA-256 checksum of a file, encoded as lowercase hex.
|
||||
///
|
||||
/// The file is read chunk by chunk, so it is never fully loaded into memory.
|
||||
@@ -41,3 +45,30 @@ class KeyPair {
|
||||
identical(this, other) ||
|
||||
other is KeyPair && runtimeType == other.runtimeType && privateKey == other.privateKey && publicKey == other.publicKey;
|
||||
}
|
||||
|
||||
class SecurityContext {
|
||||
final String privateKey;
|
||||
final String publicKey;
|
||||
final String certificate;
|
||||
final String certificateHash;
|
||||
|
||||
const SecurityContext({
|
||||
required this.privateKey,
|
||||
required this.publicKey,
|
||||
required this.certificate,
|
||||
required this.certificateHash,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode => privateKey.hashCode ^ publicKey.hashCode ^ certificate.hashCode ^ certificateHash.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is SecurityContext &&
|
||||
runtimeType == other.runtimeType &&
|
||||
privateKey == other.privateKey &&
|
||||
publicKey == other.publicKey &&
|
||||
certificate == other.certificate &&
|
||||
certificateHash == other.certificateHash;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
String get codegenVersion => '2.12.0';
|
||||
|
||||
@override
|
||||
int get rustContentHash => 1795427439;
|
||||
int get rustContentHash => -286605518;
|
||||
|
||||
static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig(
|
||||
stem: 'rust_lib_localsend_app',
|
||||
@@ -247,6 +247,8 @@ abstract class RustLibApi extends BaseApi {
|
||||
|
||||
Future<KeyPair> crateApiCryptoGenerateKeyPair();
|
||||
|
||||
Future<SecurityContext> crateApiCryptoGenerateSecurityContext();
|
||||
|
||||
Future<String> crateApiCryptoHashFile({String? path, int? fileDescriptor, Uint8List? bytes, required RsCancellationToken cancelToken});
|
||||
|
||||
Future<RsMulticast> crateApiMulticastStartMulticast({
|
||||
@@ -1670,6 +1672,30 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
argNames: [],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<SecurityContext> crateApiCryptoGenerateSecurityContext() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 45, port: port_);
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_security_context,
|
||||
decodeErrorData: sse_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiCryptoGenerateSecurityContextConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiCryptoGenerateSecurityContextConstMeta => const TaskConstMeta(
|
||||
debugName: 'generate_security_context',
|
||||
argNames: [],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<String> crateApiCryptoHashFile({String? path, int? fileDescriptor, Uint8List? bytes, required RsCancellationToken cancelToken}) {
|
||||
return handler.executeNormal(
|
||||
@@ -1680,7 +1706,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_opt_box_autoadd_i_32(fileDescriptor, serializer);
|
||||
sse_encode_opt_list_prim_u_8_strict(bytes, serializer);
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsCancellationToken(cancelToken, serializer);
|
||||
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 45, port: port_);
|
||||
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 46, port: port_);
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_String,
|
||||
@@ -1727,7 +1753,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_String(fingerprint, serializer);
|
||||
sse_encode_protocol_type_v_2(protocol, serializer);
|
||||
sse_encode_bool(download, serializer);
|
||||
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 46, port: port_);
|
||||
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 47, port: port_);
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsMulticast,
|
||||
@@ -1784,7 +1810,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_opt_String(pin, serializer);
|
||||
sse_encode_opt_box_autoadd_web_send_params(webSend, serializer);
|
||||
sse_encode_opt_String(showToken, serializer);
|
||||
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 47, port: port_);
|
||||
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 48, port: port_);
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpServer,
|
||||
@@ -1810,7 +1836,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||
sse_encode_String(cert, serializer);
|
||||
sse_encode_String(publicKey, serializer);
|
||||
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 48, port: port_);
|
||||
pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 49, port: port_);
|
||||
},
|
||||
codec: SseCodec(
|
||||
decodeSuccessData: sse_decode_unit,
|
||||
@@ -2913,6 +2939,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
SecurityContext dco_decode_security_context(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 4) throw Exception('unexpected arr length: expect 4 but see ${arr.length}');
|
||||
return SecurityContext(
|
||||
privateKey: dco_decode_String(arr[0]),
|
||||
publicKey: dco_decode_String(arr[1]),
|
||||
certificate: dco_decode_String(arr[2]),
|
||||
certificateHash: dco_decode_String(arr[3]),
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
SessionEndReasonV2 dco_decode_session_end_reason_v_2(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -4126,6 +4165,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
SecurityContext sse_decode_security_context(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var var_privateKey = sse_decode_String(deserializer);
|
||||
var var_publicKey = sse_decode_String(deserializer);
|
||||
var var_certificate = sse_decode_String(deserializer);
|
||||
var var_certificateHash = sse_decode_String(deserializer);
|
||||
return SecurityContext(privateKey: var_privateKey, publicKey: var_publicKey, certificate: var_certificate, certificateHash: var_certificateHash);
|
||||
}
|
||||
|
||||
@protected
|
||||
SessionEndReasonV2 sse_decode_session_end_reason_v_2(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -5335,6 +5384,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_security_context(SecurityContext self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_String(self.privateKey, serializer);
|
||||
sse_encode_String(self.publicKey, serializer);
|
||||
sse_encode_String(self.certificate, serializer);
|
||||
sse_encode_String(self.certificateHash, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_session_end_reason_v_2(SessionEndReasonV2 self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
@@ -435,6 +435,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
RTCStatus dco_decode_rtc_status(dynamic raw);
|
||||
|
||||
@protected
|
||||
SecurityContext dco_decode_security_context(dynamic raw);
|
||||
|
||||
@protected
|
||||
SessionEndReasonV2 dco_decode_session_end_reason_v_2(dynamic raw);
|
||||
|
||||
@@ -865,6 +868,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
RTCStatus sse_decode_rtc_status(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
SecurityContext sse_decode_security_context(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
SessionEndReasonV2 sse_decode_session_end_reason_v_2(SseDeserializer deserializer);
|
||||
|
||||
@@ -1341,6 +1347,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_rtc_status(RTCStatus self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_security_context(SecurityContext self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_session_end_reason_v_2(SessionEndReasonV2 self, SseSerializer serializer);
|
||||
|
||||
|
||||
@@ -437,6 +437,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
RTCStatus dco_decode_rtc_status(dynamic raw);
|
||||
|
||||
@protected
|
||||
SecurityContext dco_decode_security_context(dynamic raw);
|
||||
|
||||
@protected
|
||||
SessionEndReasonV2 dco_decode_session_end_reason_v_2(dynamic raw);
|
||||
|
||||
@@ -867,6 +870,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
RTCStatus sse_decode_rtc_status(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
SecurityContext sse_decode_security_context(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
SessionEndReasonV2 sse_decode_session_end_reason_v_2(SseDeserializer deserializer);
|
||||
|
||||
@@ -1343,6 +1349,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_rtc_status(RTCStatus self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_security_context(SecurityContext self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_session_end_reason_v_2(SessionEndReasonV2 self, SseSerializer serializer);
|
||||
|
||||
|
||||
+37
-3
@@ -255,6 +255,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -666,7 +675,7 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
"rand 0.9.5",
|
||||
"rand_core 0.6.4",
|
||||
"rcgen",
|
||||
"rcgen 0.13.2",
|
||||
"ring",
|
||||
"rustls",
|
||||
"sec1",
|
||||
@@ -1477,6 +1486,7 @@ dependencies = [
|
||||
"pem",
|
||||
"percent-encoding",
|
||||
"rand 0.9.5",
|
||||
"rcgen 0.14.8",
|
||||
"reqwest",
|
||||
"rsa",
|
||||
"rustls",
|
||||
@@ -2005,7 +2015,21 @@ dependencies = [
|
||||
"rustls-pki-types",
|
||||
"time",
|
||||
"x509-parser 0.16.0",
|
||||
"yasna",
|
||||
"yasna 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rcgen"
|
||||
version = "0.14.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57f6d249aad744e274e682777a50283a225a32705394ee6d5fcc01efa25e4055"
|
||||
dependencies = [
|
||||
"pem",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"time",
|
||||
"x509-parser 0.18.1",
|
||||
"yasna 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3194,7 +3218,7 @@ dependencies = [
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"rand 0.9.5",
|
||||
"rcgen",
|
||||
"rcgen 0.13.2",
|
||||
"regex",
|
||||
"ring",
|
||||
"rtcp",
|
||||
@@ -3630,6 +3654,16 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yasna"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.3"
|
||||
|
||||
@@ -20,6 +20,26 @@ pub struct KeyPair {
|
||||
pub public_key: String,
|
||||
}
|
||||
|
||||
/// Generates a new device identity: an RSA-2048 key pair and a self-signed
|
||||
/// certificate whose SHA-256 fingerprint identifies the device.
|
||||
pub fn generate_security_context() -> anyhow::Result<SecurityContext> {
|
||||
let cert = localsend::crypto::cert::generate_self_signed()?;
|
||||
|
||||
Ok(SecurityContext {
|
||||
private_key: cert.private_key_pem,
|
||||
public_key: cert.public_key_pem,
|
||||
certificate: cert.certificate_pem,
|
||||
certificate_hash: cert.fingerprint,
|
||||
})
|
||||
}
|
||||
|
||||
pub struct SecurityContext {
|
||||
pub private_key: String,
|
||||
pub public_key: String,
|
||||
pub certificate: String,
|
||||
pub certificate_hash: String,
|
||||
}
|
||||
|
||||
/// Computes the SHA-256 checksum of a file, encoded as lowercase hex.
|
||||
///
|
||||
/// The file is read chunk by chunk, so it is never fully loaded into memory.
|
||||
|
||||
@@ -44,7 +44,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
|
||||
default_rust_auto_opaque = RustAutoOpaqueMoi,
|
||||
);
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0";
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1795427439;
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -286605518;
|
||||
|
||||
// Section: executor
|
||||
|
||||
@@ -2638,6 +2638,40 @@ fn wire__crate__api__crypto__generate_key_pair_impl(
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__crypto__generate_security_context_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
rust_vec_len_: i32,
|
||||
data_len_: i32,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::SseCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "generate_security_context",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let message = unsafe {
|
||||
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
|
||||
ptr_,
|
||||
rust_vec_len_,
|
||||
data_len_,
|
||||
)
|
||||
};
|
||||
let mut deserializer =
|
||||
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
|
||||
deserializer.end();
|
||||
move |context| {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || {
|
||||
let output_ok = crate::api::crypto::generate_security_context()?;
|
||||
Ok(output_ok)
|
||||
})(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__crypto__hash_file_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
@@ -4262,6 +4296,22 @@ impl SseDecode for crate::api::webrtc::RTCStatus {
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for crate::api::crypto::SecurityContext {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
let mut var_privateKey = <String>::sse_decode(deserializer);
|
||||
let mut var_publicKey = <String>::sse_decode(deserializer);
|
||||
let mut var_certificate = <String>::sse_decode(deserializer);
|
||||
let mut var_certificateHash = <String>::sse_decode(deserializer);
|
||||
return crate::api::crypto::SecurityContext {
|
||||
private_key: var_privateKey,
|
||||
public_key: var_publicKey,
|
||||
certificate: var_certificate,
|
||||
certificate_hash: var_certificateHash,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for crate::api::server::SessionEndReasonV2 {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
@@ -4622,10 +4672,16 @@ fn pde_ffi_dispatcher_primary_impl(
|
||||
wire__crate__api__logging__enable_debug_logging_impl(port, ptr, rust_vec_len, data_len)
|
||||
}
|
||||
44 => wire__crate__api__crypto__generate_key_pair_impl(port, ptr, rust_vec_len, data_len),
|
||||
45 => wire__crate__api__crypto__hash_file_impl(port, ptr, rust_vec_len, data_len),
|
||||
46 => wire__crate__api__multicast__start_multicast_impl(port, ptr, rust_vec_len, data_len),
|
||||
47 => wire__crate__api__server__start_server_impl(port, ptr, rust_vec_len, data_len),
|
||||
48 => wire__crate__api__crypto__verify_cert_impl(port, ptr, rust_vec_len, data_len),
|
||||
45 => wire__crate__api__crypto__generate_security_context_impl(
|
||||
port,
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
46 => wire__crate__api__crypto__hash_file_impl(port, ptr, rust_vec_len, data_len),
|
||||
47 => wire__crate__api__multicast__start_multicast_impl(port, ptr, rust_vec_len, data_len),
|
||||
48 => wire__crate__api__server__start_server_impl(port, ptr, rust_vec_len, data_len),
|
||||
49 => wire__crate__api__crypto__verify_cert_impl(port, ptr, rust_vec_len, data_len),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -5516,6 +5572,29 @@ impl flutter_rust_bridge::IntoIntoDart<FrbWrapper<crate::api::webrtc::RTCStatus>
|
||||
}
|
||||
}
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
impl flutter_rust_bridge::IntoDart for crate::api::crypto::SecurityContext {
|
||||
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
|
||||
[
|
||||
self.private_key.into_into_dart().into_dart(),
|
||||
self.public_key.into_into_dart().into_dart(),
|
||||
self.certificate.into_into_dart().into_dart(),
|
||||
self.certificate_hash.into_into_dart().into_dart(),
|
||||
]
|
||||
.into_dart()
|
||||
}
|
||||
}
|
||||
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive
|
||||
for crate::api::crypto::SecurityContext
|
||||
{
|
||||
}
|
||||
impl flutter_rust_bridge::IntoIntoDart<crate::api::crypto::SecurityContext>
|
||||
for crate::api::crypto::SecurityContext
|
||||
{
|
||||
fn into_into_dart(self) -> crate::api::crypto::SecurityContext {
|
||||
self
|
||||
}
|
||||
}
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
impl flutter_rust_bridge::IntoDart for FrbWrapper<crate::api::server::SessionEndReasonV2> {
|
||||
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
|
||||
match self.0 {
|
||||
@@ -6669,6 +6748,16 @@ impl SseEncode for crate::api::webrtc::RTCStatus {
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for crate::api::crypto::SecurityContext {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<String>::sse_encode(self.private_key, serializer);
|
||||
<String>::sse_encode(self.public_key, serializer);
|
||||
<String>::sse_encode(self.certificate, serializer);
|
||||
<String>::sse_encode(self.certificate_hash, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for crate::api::server::SessionEndReasonV2 {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
|
||||
Reference in New Issue
Block a user