diff --git a/android/BUILD.gn b/android/BUILD.gn index 878875f741bf..e92bcd159773 100644 --- a/android/BUILD.gn +++ b/android/BUILD.gn @@ -6,6 +6,7 @@ import("//brave/components/ai_chat/core/common/buildflags/buildflags.gni") import("//brave/components/ipfs/buildflags/buildflags.gni") import("//brave/components/p3a/buildflags.gni") +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") import("//brave/components/webcompat_reporter/buildflags/buildflags.gni") import("//build/config/android/rules.gni") @@ -29,5 +30,6 @@ java_cpp_template("brave_config_java") { "BRAVE_ANDROID_ENABLE_IPFS=$enable_ipfs", "BRAVE_ANDROID_WEBCOMPAT_REPORT_ENDPOINT=\"$webcompat_report_api_endpoint\"", "BRAVE_ANDROID_AI_CHAT_ENABLED=$enable_ai_chat", + "BRAVE_ANDROID_WEB_DISCOVERY_ENABLED=$enable_web_discovery_native", ] } diff --git a/android/java/org/chromium/base/BraveFeatureList.java b/android/java/org/chromium/base/BraveFeatureList.java index f850435e94d8..9992d39deb54 100644 --- a/android/java/org/chromium/base/BraveFeatureList.java +++ b/android/java/org/chromium/base/BraveFeatureList.java @@ -33,4 +33,5 @@ public abstract class BraveFeatureList { "BraveShowStrictFingerprintingMode"; public static final String BRAVE_DAY_ZERO_EXPERIMENT = "BraveDayZeroExperiment"; public static final String BRAVE_FALLBACK_DOH_PROVIDER = "BraveFallbackDoHProvider"; + public static final String BRAVE_WEB_DISCOVERY_NATIVE = "BraveWebDiscoveryNative"; } diff --git a/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java b/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java index f2a0d6a8e0bc..148448c0aa59 100644 --- a/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java +++ b/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java @@ -86,6 +86,7 @@ public class BravePrivacySettings extends PrivacySettings implements ConnectionE public static final String PREF_FINGERPRINTING_PROTECTION2 = "fingerprinting_protection2"; private static final String PREF_CLOSE_TABS_ON_EXIT = "close_tabs_on_exit"; private static final String PREF_SEND_P3A = "send_p3a_analytics"; + private static final String PREF_SEND_WEB_DISCOVERY = "send_web_discovery"; private static final String PREF_SEND_CRASH_REPORTS = "send_crash_reports"; private static final String PREF_BRAVE_STATS_USAGE_PING = "brave_stats_usage_ping"; private static final String PREF_SEARCH_SUGGESTIONS = "search_suggestions"; @@ -156,6 +157,7 @@ public class BravePrivacySettings extends PrivacySettings implements ConnectionE PREF_PHONE_AS_A_SECURITY_KEY, PREF_CLOSE_TABS_ON_EXIT, PREF_SEND_P3A, + PREF_SEND_WEB_DISCOVERY, PREF_SEND_CRASH_REPORTS, PREF_BRAVE_STATS_USAGE_PING, PREF_SHOW_AUTOCOMPLETE_IN_ADDRESS_BAR, @@ -191,6 +193,7 @@ public class BravePrivacySettings extends PrivacySettings implements ConnectionE private ChromeSwitchPreference mForgetFirstPartyStoragePref; private ChromeSwitchPreference mCloseTabsOnExitPref; private ChromeSwitchPreference mSendP3A; + private ChromeSwitchPreference mSendWebDiscovery; private ChromeSwitchPreference mSendCrashReports; private ChromeSwitchPreference mBraveStatsUsagePing; private ChromeSwitchPreference mIpfsGatewayPref; @@ -328,6 +331,14 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { mSendP3A = (ChromeSwitchPreference) findPreference(PREF_SEND_P3A); mSendP3A.setOnPreferenceChangeListener(this); + if (BraveConfig.WEB_DISCOVERY_ENABLED + && ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_WEB_DISCOVERY_NATIVE)) { + mSendWebDiscovery = (ChromeSwitchPreference) findPreference(PREF_SEND_WEB_DISCOVERY); + mSendWebDiscovery.setOnPreferenceChangeListener(this); + } else { + removePreferenceIfPresent(PREF_SEND_WEB_DISCOVERY); + } + mSendCrashReports = (ChromeSwitchPreference) findPreference(PREF_SEND_CRASH_REPORTS); mSendCrashReports.setOnPreferenceChangeListener(this); mBraveStatsUsagePing = (ChromeSwitchPreference) findPreference(PREF_BRAVE_STATS_USAGE_PING); @@ -556,6 +567,9 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { } else if (PREF_SEND_P3A.equals(key)) { BraveLocalState.get().setBoolean(BravePref.P3A_ENABLED, (boolean) newValue); BraveLocalState.commitPendingWrite(); + } else if (PREF_SEND_WEB_DISCOVERY.equals(key)) { + UserPrefs.get(ProfileManager.getLastUsedRegularProfile()) + .setBoolean(BravePref.WEB_DISCOVERY_NATIVE_ENABLED, (boolean) newValue); } else if (PREF_SEND_CRASH_REPORTS.equals(key)) { UmaSessionStats.changeMetricsReportingConsent( (boolean) newValue, ChangeMetricsReportingStateCalledFrom.UI_SETTINGS); @@ -730,6 +744,16 @@ private void updateBravePreferences() { getPreferenceScreen().removePreference(mSendP3A); } + if (mSendWebDiscovery != null) { + mSendWebDiscovery.setTitle( + getActivity().getResources().getString(R.string.send_web_discovery_title)); + mSendWebDiscovery.setSummary( + getActivity().getResources().getString(R.string.send_web_discovery_summary)); + mSendWebDiscovery.setChecked( + UserPrefs.get(ProfileManager.getLastUsedRegularProfile()) + .getBoolean(BravePref.WEB_DISCOVERY_NATIVE_ENABLED)); + } + mSendCrashReports.setChecked(mPrivacyPrefManager.isUsageAndCrashReportingPermittedByUser()); mBraveStatsUsagePing.setChecked( diff --git a/android/java/res/xml/brave_privacy_preferences.xml b/android/java/res/xml/brave_privacy_preferences.xml index ef69a07e7ed5..86e95afd69d4 100644 --- a/android/java/res/xml/brave_privacy_preferences.xml +++ b/android/java/res/xml/brave_privacy_preferences.xml @@ -191,6 +191,12 @@ android:title="@string/send_p3a_analytics_title" android:summary="@string/send_p3a_analytics_summary" android:defaultValue="false" /> + RegisterBooleanPref(kWebDiscoveryEnabled, false); + registry->RegisterBooleanPref(kWebDiscoveryExtensionEnabled, false); registry->RegisterDictionaryPref(kWebDiscoveryCTAState); #endif @@ -508,6 +512,10 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { registry->SetDefaultPrefValue(prefs::kSearchSuggestEnabled, base::Value(false)); + +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) + web_discovery::WDPService::RegisterProfilePrefs(registry); +#endif } } // namespace brave diff --git a/browser/brave_stats/brave_stats_updater_browsertest.cc b/browser/brave_stats/brave_stats_updater_browsertest.cc index 477465328909..39fc2b354649 100644 --- a/browser/brave_stats/brave_stats_updater_browsertest.cc +++ b/browser/brave_stats/brave_stats_updater_browsertest.cc @@ -32,7 +32,6 @@ #if BUILDFLAG(IS_ANDROID) #include "chrome/test/base/android/android_browser_test.h" #else -#include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h" #endif diff --git a/browser/brave_tab_helpers.cc b/browser/brave_tab_helpers.cc index 4516155624f6..f759d5046ec1 100644 --- a/browser/brave_tab_helpers.cc +++ b/browser/brave_tab_helpers.cc @@ -34,6 +34,7 @@ #include "brave/components/request_otr/common/buildflags/buildflags.h" #include "brave/components/speedreader/common/buildflags/buildflags.h" #include "brave/components/tor/buildflags/buildflags.h" +#include "brave/components/web_discovery/common/buildflags/buildflags.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/common/channel_info.h" @@ -66,6 +67,12 @@ #include "brave/components/ai_chat/core/browser/utils.h" #endif +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) +#include "brave/browser/web_discovery/wdp_service_factory.h" +#include "brave/components/web_discovery/browser/web_discovery_tab_helper.h" +#include "brave/components/web_discovery/common/features.h" +#endif + #if BUILDFLAG(ENABLE_WIDEVINE) #include "brave/browser/brave_drm_tab_helper.h" #endif @@ -218,6 +225,18 @@ void AttachTabHelpers(content::WebContents* web_contents) { } } #endif // BUILDFLAG(ENABLE_PLAYLIST) + +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) + if (base::FeatureList::IsEnabled( + web_discovery::features::kWebDiscoveryNative)) { + auto* wdp_service = web_discovery::WDPServiceFactory::GetForBrowserContext( + web_contents->GetBrowserContext()); + if (wdp_service) { + web_discovery::WebDiscoveryTabHelper::CreateForWebContents(web_contents, + wdp_service); + } + } +#endif } } // namespace brave diff --git a/browser/browser_context_keyed_service_factories.cc b/browser/browser_context_keyed_service_factories.cc index 5f00475ef7c9..48061a474399 100644 --- a/browser/browser_context_keyed_service_factories.cc +++ b/browser/browser_context_keyed_service_factories.cc @@ -41,6 +41,7 @@ #include "brave/components/request_otr/common/buildflags/buildflags.h" #include "brave/components/speedreader/common/buildflags/buildflags.h" #include "brave/components/tor/buildflags/buildflags.h" +#include "brave/components/web_discovery/common/buildflags/buildflags.h" #if BUILDFLAG(ENABLE_BRAVE_VPN) #include "brave/browser/brave_vpn/brave_vpn_service_factory.h" @@ -110,6 +111,10 @@ #include "brave/components/ai_chat/content/browser/model_service_factory.h" #endif +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) +#include "brave/browser/web_discovery/wdp_service_factory.h" +#endif + namespace brave { void EnsureBrowserContextKeyedServiceFactoriesBuilt() { @@ -216,6 +221,10 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() { #if BUILDFLAG(ENABLE_AI_CHAT) ai_chat::ModelServiceFactory::GetInstance(); #endif + +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) + web_discovery::WDPServiceFactory::GetInstance(); +#endif } } // namespace brave diff --git a/browser/extensions/api/settings_private/brave_prefs_util.cc b/browser/extensions/api/settings_private/brave_prefs_util.cc index cd53aa553475..f1931f8344e0 100644 --- a/browser/extensions/api/settings_private/brave_prefs_util.cc +++ b/browser/extensions/api/settings_private/brave_prefs_util.cc @@ -28,6 +28,7 @@ #include "brave/components/request_otr/common/pref_names.h" #include "brave/components/speedreader/common/buildflags/buildflags.h" #include "brave/components/tor/buildflags/buildflags.h" +#include "brave/components/web_discovery/common/buildflags/buildflags.h" #include "chrome/browser/content_settings/cookie_settings_factory.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/extensions/api/settings_private/prefs_util.h" @@ -80,6 +81,10 @@ #include "brave/components/playlist/browser/pref_names.h" #endif +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) +#include "brave/components/web_discovery/browser/pref_names.h" +#endif + namespace extensions { using ntp_background_images::prefs::kNewTabPageShowBackgroundImage; @@ -204,7 +209,12 @@ const PrefsUtil::TypedPrefMap& BravePrefsUtil::GetAllowlistedKeys() { settings_api::PrefType::kNumber; #if BUILDFLAG(ENABLE_EXTENSIONS) // Web discovery prefs - (*s_brave_allowlist)[kWebDiscoveryEnabled] = settings_api::PrefType::kBoolean; + (*s_brave_allowlist)[kWebDiscoveryExtensionEnabled] = + settings_api::PrefType::kBoolean; +#endif +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) + (*s_brave_allowlist)[web_discovery::kWebDiscoveryNativeEnabled] = + settings_api::PrefType::kBoolean; #endif // Clear browsing data on exit prefs. (*s_brave_allowlist)[browsing_data::prefs::kDeleteBrowsingHistoryOnExit] = diff --git a/browser/profiles/brave_profile_manager.cc b/browser/profiles/brave_profile_manager.cc index 89a437216f96..54fbdb5ae11a 100644 --- a/browser/profiles/brave_profile_manager.cc +++ b/browser/profiles/brave_profile_manager.cc @@ -26,6 +26,7 @@ #include "brave/components/ipfs/buildflags/buildflags.h" #include "brave/components/request_otr/common/buildflags/buildflags.h" #include "brave/components/tor/buildflags/buildflags.h" +#include "brave/components/web_discovery/common/buildflags/buildflags.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile_attributes_entry.h" #include "chrome/browser/profiles/profile_attributes_storage.h" @@ -58,6 +59,10 @@ #include "brave/components/tor/tor_constants.h" #endif +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) +#include "brave/components/web_discovery/browser/wdp_service.h" +#endif + using content::BrowserThread; BraveProfileManager::BraveProfileManager(const base::FilePath& user_data_dir) @@ -100,6 +105,10 @@ void BraveProfileManager::InitProfileUserPrefs(Profile* profile) { #if BUILDFLAG(ENABLE_IPFS) ipfs::IpfsService::MigrateProfilePrefs(profile->GetPrefs()); #endif +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) + web_discovery::WDPService::SetExtensionPrefIfNativeDisabled( + profile->GetPrefs()); +#endif } void BraveProfileManager::DoFinalInitForServices(Profile* profile, diff --git a/browser/resources/settings/BUILD.gn b/browser/resources/settings/BUILD.gn index 67bbc5e76d88..2905c82c9241 100644 --- a/browser/resources/settings/BUILD.gn +++ b/browser/resources/settings/BUILD.gn @@ -8,6 +8,7 @@ import("//brave/build/config.gni") import("//brave/components/brave_vpn/common/buildflags/buildflags.gni") import("//brave/components/brave_wayback_machine/buildflags/buildflags.gni") import("//brave/components/tor/buildflags/buildflags.gni") +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") import("//brave/resources/brave_grit.gni") import("//chrome/common/features.gni") import("//extensions/buildflags/buildflags.gni") @@ -75,6 +76,7 @@ preprocess_if_expr("preprocess") { "enable_brave_vpn_wireguard=$enable_brave_vpn_wireguard", "enable_extensions=$enable_extensions", "enable_pin_shortcut=$enable_pin_shortcut", + "enable_web_discovery_native=$enable_web_discovery_native", ] out_folder = "$root_gen_dir/chrome/browser/resources/settings/$preprocess_folder" diff --git a/browser/resources/settings/brave_search_engines_page/brave_search_engines_page.html b/browser/resources/settings/brave_search_engines_page/brave_search_engines_page.html index 69dc389036e9..735879d62fe2 100644 --- a/browser/resources/settings/brave_search_engines_page/brave_search_engines_page.html +++ b/browser/resources/settings/brave_search_engines_page/brave_search_engines_page.html @@ -18,13 +18,26 @@ - - + + + + GetBoolean(kWebDiscoveryEnabled)); - UMA_HISTOGRAM_BOOLEAN(kWebDiscoveryAndAdsMetric, - profile_prefs_->GetBoolean(kWebDiscoveryEnabled) && - profile_prefs_->GetBoolean( - brave_ads::prefs::kOptedInToNotificationAds)); + bool enabled = false; +#if BUILDFLAG(ENABLE_EXTENSIONS) + enabled = profile_prefs_->GetBoolean(kWebDiscoveryExtensionEnabled); +#endif +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) + if (base::FeatureList::IsEnabled( + web_discovery::features::kWebDiscoveryNative)) { + enabled = + profile_prefs_->GetBoolean(web_discovery::kWebDiscoveryNativeEnabled); + } +#endif + UMA_HISTOGRAM_BOOLEAN(kWebDiscoveryEnabledMetric, enabled); + UMA_HISTOGRAM_BOOLEAN( + kWebDiscoveryAndAdsMetric, + enabled && profile_prefs_->GetBoolean( + brave_ads::prefs::kOptedInToNotificationAds)); } #endif diff --git a/browser/search_engines/search_engine_tracker.h b/browser/search_engines/search_engine_tracker.h index c7c61d700889..a3eb2e2c4f93 100644 --- a/browser/search_engines/search_engine_tracker.h +++ b/browser/search_engines/search_engine_tracker.h @@ -9,6 +9,7 @@ #include "base/memory/raw_ptr.h" #include "base/scoped_observation.h" #include "brave/components/time_period_storage/weekly_event_storage.h" +#include "brave/components/web_discovery/common/buildflags/buildflags.h" #include "components/keyed_service/content/browser_context_keyed_service_factory.h" #include "components/keyed_service/core/keyed_service.h" #include "components/prefs/pref_change_registrar.h" @@ -110,7 +111,7 @@ class SearchEngineTracker : public KeyedService, // TemplateURLServiceObserver overrides: void OnTemplateURLServiceChanged() override; -#if BUILDFLAG(ENABLE_EXTENSIONS) +#if BUILDFLAG(ENABLE_EXTENSIONS) || BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) void RecordWebDiscoveryEnabledP3A(); #endif @@ -130,7 +131,7 @@ class SearchEngineTracker : public KeyedService, raw_ptr template_url_service_ = nullptr; -#if BUILDFLAG(ENABLE_EXTENSIONS) +#if BUILDFLAG(ENABLE_EXTENSIONS) || BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) PrefChangeRegistrar pref_change_registrar_; #endif }; diff --git a/browser/search_engines/search_engine_tracker_browsertest.cc b/browser/search_engines/search_engine_tracker_browsertest.cc index 7cb32e0000cd..b651468e8d42 100644 --- a/browser/search_engines/search_engine_tracker_browsertest.cc +++ b/browser/search_engines/search_engine_tracker_browsertest.cc @@ -163,7 +163,7 @@ IN_PROC_BROWSER_TEST_F(SearchEngineProviderP3ATest, WebDiscoveryEnabledP3A) { histogram_tester_->ExpectBucketCount(kWebDiscoveryEnabledMetric, 0, 1); PrefService* prefs = browser()->profile()->GetPrefs(); - prefs->SetBoolean(kWebDiscoveryEnabled, true); + prefs->SetBoolean(kWebDiscoveryExtensionEnabled, true); histogram_tester_->ExpectBucketCount(kWebDiscoveryEnabledMetric, 1, 1); @@ -171,7 +171,7 @@ IN_PROC_BROWSER_TEST_F(SearchEngineProviderP3ATest, WebDiscoveryEnabledP3A) { prefs->SetBoolean(brave_ads::prefs::kOptedInToNotificationAds, true); histogram_tester_->ExpectBucketCount(kWebDiscoveryAndAdsMetric, 1, 1); - prefs->SetBoolean(kWebDiscoveryEnabled, false); + prefs->SetBoolean(kWebDiscoveryExtensionEnabled, false); histogram_tester_->ExpectBucketCount(kWebDiscoveryEnabledMetric, 0, 2); histogram_tester_->ExpectBucketCount(kWebDiscoveryAndAdsMetric, 0, 3); diff --git a/browser/sources.gni b/browser/sources.gni index 06565def9695..92dc904384de 100644 --- a/browser/sources.gni +++ b/browser/sources.gni @@ -55,6 +55,7 @@ import("//brave/components/brave_wayback_machine/buildflags/buildflags.gni") import("//brave/components/brave_webtorrent/browser/buildflags/buildflags.gni") import("//brave/components/commander/common/buildflags/buildflags.gni") import("//brave/components/tor/buildflags/buildflags.gni") +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") import("//extensions/buildflags/buildflags.gni") brave_chrome_browser_visibility = [ @@ -204,6 +205,8 @@ brave_chrome_browser_deps = [ "//brave/components/speedreader/common/buildflags", "//brave/components/tor/buildflags", "//brave/components/version_info", + "//brave/components/web_discovery/common", + "//brave/components/web_discovery/common/buildflags", "//brave/components/webcompat:core", "//brave/services/network/public/cpp", "//brave/third_party/blink/renderer", @@ -324,6 +327,13 @@ if (enable_ai_rewriter) { brave_chrome_browser_deps += [ "//brave/components/ai_rewriter/common" ] } +if (enable_web_discovery_native) { + brave_chrome_browser_deps += [ + "//brave/browser/web_discovery", + "//brave/components/web_discovery/browser", + ] +} + if (enable_brave_vpn) { brave_chrome_browser_deps += [ "//brave/components/brave_vpn/browser", diff --git a/browser/ui/BUILD.gn b/browser/ui/BUILD.gn index ed5b29ccfd95..3fdfea684adb 100644 --- a/browser/ui/BUILD.gn +++ b/browser/ui/BUILD.gn @@ -19,6 +19,7 @@ import("//brave/components/request_otr/common/buildflags/buildflags.gni") import("//brave/components/speedreader/common/buildflags/buildflags.gni") import("//brave/components/text_recognition/common/buildflags/buildflags.gni") import("//brave/components/tor/buildflags/buildflags.gni") +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") import("//build/config/features.gni") import("//chrome/common/features.gni") import("//components/gcm_driver/config.gni") @@ -843,6 +844,13 @@ source_set("ui") { } } + if (enable_web_discovery_native) { + deps += [ + "//brave/components/web_discovery/common", + "//brave/components/web_discovery/common/buildflags", + ] + } + if (is_linux) { sources += [ "views/brave_views_delegate_linux.cc", diff --git a/browser/ui/android/strings/android_brave_strings.grd b/browser/ui/android/strings/android_brave_strings.grd index fb34224fdd9d..a1cc87f068ef 100644 --- a/browser/ui/android/strings/android_brave_strings.grd +++ b/browser/ui/android/strings/android_brave_strings.grd @@ -746,6 +746,12 @@ This file contains all "about" strings. It is set to NOT be translated, in tran Anonymized P3A info helps Brave estimate overall usage, and ensure we’re improving popular features. + + Web Discovery Project + + + Contribute some anonymous search & browsing data to refine Brave Search. + Automatically send diagnostic reports diff --git a/browser/ui/webui/brave_settings_ui.cc b/browser/ui/webui/brave_settings_ui.cc index b91727e792f9..812e59af6895 100644 --- a/browser/ui/webui/brave_settings_ui.cc +++ b/browser/ui/webui/brave_settings_ui.cc @@ -38,6 +38,8 @@ #include "brave/components/speedreader/common/buildflags/buildflags.h" #include "brave/components/tor/buildflags/buildflags.h" #include "brave/components/version_info/version_info.h" +#include "brave/components/web_discovery/common/buildflags/buildflags.h" +#include "brave/components/web_discovery/common/features.h" #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/settings/metrics_reporting_handler.h" @@ -187,6 +189,11 @@ void BraveSettingsUI::AddResources(content::WebUIDataSource* html_source, ShouldExposeElementsForTesting()); html_source->AddBoolean("enable_extensions", BUILDFLAG(ENABLE_EXTENSIONS)); +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) + html_source->AddBoolean("isWebDiscoveryNativeEnabled", + base::FeatureList::IsEnabled( + web_discovery::features::kWebDiscoveryNative)); +#endif html_source->AddBoolean("extensionsManifestV2Feature", base::FeatureList::IsEnabled(kExtensionsManifestV2)); diff --git a/browser/ui/webui/welcome_page/welcome_dom_handler.cc b/browser/ui/webui/welcome_page/welcome_dom_handler.cc index 11f13039ef92..0478aaaaab8a 100644 --- a/browser/ui/webui/welcome_page/welcome_dom_handler.cc +++ b/browser/ui/webui/welcome_page/welcome_dom_handler.cc @@ -170,7 +170,7 @@ void WelcomeDOMHandler::HandleSetMetricsReportingEnabled( void WelcomeDOMHandler::HandleEnableWebDiscovery( const base::Value::List& args) { DCHECK(profile_); - profile_->GetPrefs()->SetBoolean(kWebDiscoveryEnabled, true); + profile_->GetPrefs()->SetBoolean(kWebDiscoveryExtensionEnabled, true); } void WelcomeDOMHandler::SetLocalStateBooleanEnabled( diff --git a/browser/web_discovery/BUILD.gn b/browser/web_discovery/BUILD.gn index ba369e8cb8e3..77a9cd9de33a 100644 --- a/browser/web_discovery/BUILD.gn +++ b/browser/web_discovery/BUILD.gn @@ -3,13 +3,35 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this file, # You can obtain one at http://mozilla.org/MPL/2.0/. +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") import("//extensions/buildflags/buildflags.gni") +if (enable_web_discovery_native) { + source_set("web_discovery") { + sources = [ + "wdp_service_factory.cc", + "wdp_service_factory.h", + ] + deps = [ + "//base", + "//brave/components/web_discovery/browser", + "//brave/components/web_discovery/common", + "//chrome/browser:browser_process", + "//chrome/common:constants", + "//components/keyed_service/content", + "//components/user_prefs", + "//content/public/browser", + "//third_party/re2", + ] + } +} + source_set("unit_tests") { if (enable_extensions) { testonly = true sources = [ "web_discovery_unittest.cc" ] + deps = [ "//brave/browser/profiles", "//brave/components/constants", @@ -23,6 +45,14 @@ source_set("unit_tests") { "//content/test:test_support", "//testing/gtest", ] + + if (enable_web_discovery_native) { + sources += [ "wdp_service_factory_unittest.cc" ] + deps += [ + ":web_discovery", + "//brave/components/web_discovery/common", + ] + } } } diff --git a/browser/web_discovery/wdp_service_factory.cc b/browser/web_discovery/wdp_service_factory.cc new file mode 100644 index 000000000000..e0b7fb36a3e5 --- /dev/null +++ b/browser/web_discovery/wdp_service_factory.cc @@ -0,0 +1,63 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/web_discovery/wdp_service_factory.h" + +#include "base/path_service.h" +#include "brave/components/web_discovery/browser/wdp_service.h" +#include "brave/components/web_discovery/common/features.h" +#include "chrome/browser/browser_process.h" +#include "chrome/common/chrome_paths.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "components/user_prefs/user_prefs.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/storage_partition.h" + +namespace web_discovery { + +WDPService* WDPServiceFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +WDPServiceFactory* WDPServiceFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +WDPServiceFactory::WDPServiceFactory() + : BrowserContextKeyedServiceFactory( + "WDPService", + BrowserContextDependencyManager::GetInstance()) {} + +WDPServiceFactory::~WDPServiceFactory() = default; + +KeyedService* WDPServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + auto* default_storage_partition = context->GetDefaultStoragePartition(); + auto shared_url_loader_factory = + default_storage_partition->GetURLLoaderFactoryForBrowserProcess(); + base::FilePath user_data_dir = + base::PathService::CheckedGet(chrome::DIR_USER_DATA); + return new WDPService(g_browser_process->local_state(), + user_prefs::UserPrefs::Get(context), user_data_dir, + shared_url_loader_factory); +} + +content::BrowserContext* WDPServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + if (!base::FeatureList::IsEnabled(features::kWebDiscoveryNative)) { + return nullptr; + } + // Prevents creation of service instance for incognito/OTR profiles + return context->IsOffTheRecord() ? nullptr : context; +} + +bool WDPServiceFactory::ServiceIsCreatedWithBrowserContext() const { + return true; +} + +} // namespace web_discovery diff --git a/browser/web_discovery/wdp_service_factory.h b/browser/web_discovery/wdp_service_factory.h new file mode 100644 index 000000000000..ca7773b19e81 --- /dev/null +++ b/browser/web_discovery/wdp_service_factory.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_WEB_DISCOVERY_WDP_SERVICE_FACTORY_H_ +#define BRAVE_BROWSER_WEB_DISCOVERY_WDP_SERVICE_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace web_discovery { + +class WDPService; + +class WDPServiceFactory : public BrowserContextKeyedServiceFactory { + public: + static WDPServiceFactory* GetInstance(); + static WDPService* GetForBrowserContext(content::BrowserContext* context); + + private: + friend base::NoDestructor; + + WDPServiceFactory(); + ~WDPServiceFactory() override; + + WDPServiceFactory(const WDPServiceFactory&) = delete; + WDPServiceFactory& operator=(const WDPServiceFactory&) = delete; + + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; +}; + +} // namespace web_discovery + +#endif // BRAVE_BROWSER_WEB_DISCOVERY_WDP_SERVICE_FACTORY_H_ diff --git a/browser/web_discovery/wdp_service_factory_unittest.cc b/browser/web_discovery/wdp_service_factory_unittest.cc new file mode 100644 index 000000000000..00e5a30e55f3 --- /dev/null +++ b/browser/web_discovery/wdp_service_factory_unittest.cc @@ -0,0 +1,32 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/web_discovery/wdp_service_factory.h" + +#include "base/test/scoped_feature_list.h" +#include "brave/components/web_discovery/common/features.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace web_discovery { + +TEST(WDPServiceFactoryTest, PrivateNotCreated) { + content::BrowserTaskEnvironment task_environment; + base::test::ScopedFeatureList scoped_features(features::kWebDiscoveryNative); + auto* browser_process = TestingBrowserProcess::GetGlobal(); + TestingProfileManager profile_manager(browser_process); + ASSERT_TRUE(profile_manager.SetUp()); + + auto* profile = profile_manager.CreateTestingProfile("test"); + + EXPECT_TRUE(WDPServiceFactory::GetForBrowserContext(profile)); + EXPECT_FALSE( + WDPServiceFactory::GetForBrowserContext(profile->GetOffTheRecordProfile( + Profile::OTRProfileID::CreateUniqueForTesting(), true))); +} + +} // namespace web_discovery diff --git a/browser/web_discovery/web_discovery_cta_util.cc b/browser/web_discovery/web_discovery_cta_util.cc index 9251daac6c70..0a053023faeb 100644 --- a/browser/web_discovery/web_discovery_cta_util.cc +++ b/browser/web_discovery/web_discovery_cta_util.cc @@ -14,6 +14,7 @@ #include "brave/components/constants/pref_names.h" #include "brave/components/constants/url_constants.h" #include "brave/components/search_engines/brave_prepopulated_engines.h" +#include "brave/components/web_discovery/common/buildflags/buildflags.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/search_engines/template_url.h" @@ -21,6 +22,11 @@ #include "net/base/url_util.h" #include "url/gurl.h" +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) +#include "brave/components/web_discovery/browser/pref_names.h" +#include "brave/components/web_discovery/common/features.h" +#endif + namespace { constexpr int kMaxDisplayCount = 5; @@ -87,8 +93,17 @@ bool ShouldShowWebDiscoveryInfoBar(TemplateURLService* service, PrefService* prefs, const WebDiscoveryCTAState& state, base::Clock* test_clock) { - if (prefs->GetBoolean(kWebDiscoveryEnabled)) + const char* enabled_pref_name = kWebDiscoveryExtensionEnabled; +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) + if (base::FeatureList::IsEnabled( + web_discovery::features::kWebDiscoveryNative)) { + enabled_pref_name = web_discovery::kWebDiscoveryNativeEnabled; + } +#endif + + if (prefs->GetBoolean(enabled_pref_name)) { return false; + } if (!service || !IsBraveSearchDefault(service)) return false; diff --git a/browser/web_discovery/web_discovery_infobar_delegate.cc b/browser/web_discovery/web_discovery_infobar_delegate.cc index c9db24eaa35b..b604262e05b1 100644 --- a/browser/web_discovery/web_discovery_infobar_delegate.cc +++ b/browser/web_discovery/web_discovery_infobar_delegate.cc @@ -7,9 +7,15 @@ #include "brave/browser/web_discovery/web_discovery_cta_util.h" #include "brave/components/constants/pref_names.h" +#include "brave/components/web_discovery/common/buildflags/buildflags.h" #include "components/infobars/core/infobar.h" #include "components/prefs/pref_service.h" +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) +#include "brave/components/web_discovery/browser/pref_names.h" +#include "brave/components/web_discovery/common/features.h" +#endif + WebDiscoveryInfoBarDelegate::WebDiscoveryInfoBarDelegate(PrefService* prefs) : prefs_(prefs) {} @@ -42,6 +48,13 @@ void WebDiscoveryInfoBarDelegate::Close(bool dismiss) { } void WebDiscoveryInfoBarDelegate::EnableWebDiscovery() { - prefs_->SetBoolean(kWebDiscoveryEnabled, true); + const char* pref_name = kWebDiscoveryExtensionEnabled; +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) + if (base::FeatureList::IsEnabled( + web_discovery::features::kWebDiscoveryNative)) { + pref_name = web_discovery::kWebDiscoveryNativeEnabled; + } +#endif + prefs_->SetBoolean(pref_name, true); infobar()->RemoveSelf(); } diff --git a/browser/web_discovery/web_discovery_unittest.cc b/browser/web_discovery/web_discovery_unittest.cc index 8591bee29ff4..f04051a236e3 100644 --- a/browser/web_discovery/web_discovery_unittest.cc +++ b/browser/web_discovery/web_discovery_unittest.cc @@ -108,7 +108,7 @@ class WebDiscoveryCTATest : public testing::Test { }; TEST_F(WebDiscoveryCTATest, InitialDataTest) { - EXPECT_FALSE(prefs()->GetBoolean(kWebDiscoveryEnabled)); + EXPECT_FALSE(prefs()->GetBoolean(kWebDiscoveryExtensionEnabled)); const auto& info_value = prefs()->GetDict(kWebDiscoveryCTAState); EXPECT_TRUE(info_value.empty()); @@ -141,10 +141,10 @@ TEST_F(WebDiscoveryCTATest, ShouldShowInfoBarTest) { EXPECT_TRUE(ShouldShowWebDiscoveryInfoBar()); // Don't show if already enabled. - prefs()->SetBoolean(kWebDiscoveryEnabled, true); + prefs()->SetBoolean(kWebDiscoveryExtensionEnabled, true); EXPECT_FALSE(ShouldShowWebDiscoveryInfoBar()); - prefs()->SetBoolean(kWebDiscoveryEnabled, false); + prefs()->SetBoolean(kWebDiscoveryExtensionEnabled, false); EXPECT_TRUE(ShouldShowWebDiscoveryInfoBar()); WebDiscoveryCTAState state = GetCurrentCTAState(); diff --git a/build/android/java/templates/BraveConfig.template b/build/android/java/templates/BraveConfig.template index fdbfe6008bfe..e29815a59180 100644 --- a/build/android/java/templates/BraveConfig.template +++ b/build/android/java/templates/BraveConfig.template @@ -14,4 +14,5 @@ public class BraveConfig { public static final boolean IPFS_ENABLED = BRAVE_ANDROID_ENABLE_IPFS; public static final boolean AI_CHAT_ENABLED = BRAVE_ANDROID_AI_CHAT_ENABLED; public static final String WEBCOMPAT_REPORT_ENDPOINT = BRAVE_ANDROID_WEBCOMPAT_REPORT_ENDPOINT; + public static final boolean WEB_DISCOVERY_ENABLED = BRAVE_ANDROID_WEB_DISCOVERY_ENABLED; } diff --git a/chromium_src/chrome/browser/DEPS b/chromium_src/chrome/browser/DEPS index 60fc416c8e7f..a8285992aa86 100644 --- a/chromium_src/chrome/browser/DEPS +++ b/chromium_src/chrome/browser/DEPS @@ -48,6 +48,7 @@ include_rules = [ "+brave/components/url_sanitizer", "+brave/components/vector_icons", "+brave/components/version_info", + "+brave/components/web_discovery/common", "+brave/components/webcompat", "+brave/net", "+brave/services/network/public", diff --git a/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc b/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc index 44bc0f023b42..f31b1a84e4eb 100644 --- a/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc @@ -18,6 +18,7 @@ #include "brave/components/playlist/common/features.h" #include "brave/components/request_otr/common/features.h" #include "brave/components/speedreader/common/features.h" +#include "brave/components/web_discovery/common/features.h" #include "brave/components/webcompat/features.h" #include "net/base/features.h" #include "third_party/blink/public/common/features.h" @@ -48,7 +49,8 @@ &google_sign_in_permission::features::kBraveGoogleSignInPermission, \ &net::features::kBraveForgetFirstPartyStorage, \ &brave_shields::features::kBraveShowStrictFingerprintingMode, \ - &brave_shields::features::kBraveLocalhostAccessPermission + &brave_shields::features::kBraveLocalhostAccessPermission, \ + &web_discovery::features::kWebDiscoveryNative // clang-format on diff --git a/chromium_src/chrome/renderer/DEPS b/chromium_src/chrome/renderer/DEPS index 24268d37bd82..2eace837fd59 100644 --- a/chromium_src/chrome/renderer/DEPS +++ b/chromium_src/chrome/renderer/DEPS @@ -8,6 +8,8 @@ include_rules = [ "+brave/components/content_settings/renderer", "+brave/components/tor/buildflags", "+brave/components/tor/renderer", + "+brave/components/web_discovery/common", + "+brave/components/web_discovery/renderer", "+brave/renderer", "+media/base/key_system_info.h", "+services/network/public/cpp", diff --git a/chromium_src/chrome/renderer/chrome_content_renderer_client.cc b/chromium_src/chrome/renderer/chrome_content_renderer_client.cc index b07798ed5f30..0c3d8e8717af 100644 --- a/chromium_src/chrome/renderer/chrome_content_renderer_client.cc +++ b/chromium_src/chrome/renderer/chrome_content_renderer_client.cc @@ -3,9 +3,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at https://mozilla.org/MPL/2.0/. */ +#include "base/feature_list.h" #include "brave/components/ai_chat/core/common/buildflags/buildflags.h" #include "brave/components/ai_rewriter/common/buildflags/buildflags.h" #include "brave/components/content_settings/renderer/brave_content_settings_agent_impl.h" +#include "brave/components/web_discovery/common/buildflags/buildflags.h" #include "chrome/common/chrome_isolated_world_ids.h" #include "chrome/renderer/chrome_render_thread_observer.h" #include "components/dom_distiller/content/renderer/distillability_agent.h" @@ -22,6 +24,11 @@ #include "brave/components/ai_rewriter/renderer/ai_rewriter_agent.h" #endif +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) +#include "brave/components/web_discovery/common/features.h" +#include "brave/components/web_discovery/renderer/blink_document_extractor.h" +#endif + namespace { void RenderFrameWithBinderRegistryCreated( @@ -42,6 +49,13 @@ void RenderFrameWithBinderRegistryCreated( new ai_rewriter::AIRewriterAgent(render_frame, registry); } #endif + +#if BUILDFLAG(ENABLE_WEB_DISCOVERY_NATIVE) + if (base::FeatureList::IsEnabled( + web_discovery::features::kWebDiscoveryNative)) { + new web_discovery::BlinkDocumentExtractor(render_frame, registry); + } +#endif } } // namespace diff --git a/components/constants/pref_names.h b/components/constants/pref_names.h index fde64ba8fb14..83c7842c44ee 100644 --- a/components/constants/pref_names.h +++ b/components/constants/pref_names.h @@ -85,7 +85,8 @@ inline constexpr char kBraveShieldsSettingsVersion[] = inline constexpr char kDefaultBrowserPromptEnabled[] = "brave.default_browser_prompt_enabled"; -inline constexpr char kWebDiscoveryEnabled[] = "brave.web_discovery_enabled"; +inline constexpr char kWebDiscoveryExtensionEnabled[] = + "brave.web_discovery_enabled"; inline constexpr char kWebDiscoveryCTAState[] = "brave.web_discovery.cta_state"; inline constexpr char kDontAskEnableWebDiscovery[] = "brave.dont_ask_enable_web_discovery"; diff --git a/components/web_discovery/browser/BUILD.gn b/components/web_discovery/browser/BUILD.gn new file mode 100644 index 000000000000..ab2c6b354eb5 --- /dev/null +++ b/components/web_discovery/browser/BUILD.gn @@ -0,0 +1,113 @@ +# Copyright (c) 2024 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") + +assert(enable_web_discovery_native) + +static_library("browser") { + sources = [ + "content_scraper.cc", + "content_scraper.h", + "credential_manager.cc", + "credential_manager.h", + "credential_signer.cc", + "credential_signer.h", + "double_fetcher.cc", + "double_fetcher.h", + "ecdh_aes.cc", + "ecdh_aes.h", + "hash_detection.cc", + "hash_detection.h", + "patterns.cc", + "patterns.h", + "payload_generator.cc", + "payload_generator.h", + "pref_names.h", + "privacy_guard.cc", + "privacy_guard.h", + "regex_util.cc", + "regex_util.h", + "reporter.cc", + "reporter.h", + "request_queue.cc", + "request_queue.h", + "rsa.cc", + "rsa.h", + "server_config_loader.cc", + "server_config_loader.h", + "signature_basename.cc", + "signature_basename.h", + "util.cc", + "util.h", + "wdp_service.cc", + "wdp_service.h", + "web_discovery_tab_helper.cc", + "web_discovery_tab_helper.h", + ] + deps = [ + "anonymous_credentials/rs/cxx:rust_lib", + "document_extractor/rs:rust_lib", + "//base", + "//brave/brave_domains", + "//brave/components/constants", + "//brave/components/web_discovery/common", + "//brave/components/web_discovery/common:mojom", + "//components/keyed_service/core", + "//components/prefs", + "//content/public/browser", + "//crypto", + "//extensions/buildflags", + "//net", + "//services/network/public/cpp", + "//third_party/boringssl", + "//third_party/re2", + "//third_party/zlib/google:compression_utils", + "//url", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "credential_manager_unittest.cc", + "double_fetcher_unittest.cc", + "hash_detection_unittest.cc", + "patterns_unittest.cc", + "payload_generator_unittest.cc", + "privacy_guard_unittest.cc", + "reporter_unittest.cc", + "server_config_loader_unittest.cc", + "signature_basename_unittest.cc", + ] + deps = [ + ":browser", + "//base/test:test_support", + "//brave/components/constants", + "//components/prefs:test_support", + "//crypto", + "//services/network:test_support", + "//services/network/public/cpp", + "//testing/gtest", + ] +} + +source_set("browser_tests") { + testonly = true + sources = [ "content_scraper_browsertest.cc" ] + deps = [ + ":browser", + "//base/test:test_support", + "//brave/components/constants", + "//brave/components/web_discovery/common", + "//brave/components/web_discovery/common:mojom", + "//chrome/test:test_support", + "//content/test:test_support", + "//net:test_support", + "//services/service_manager/public/cpp", + "//testing/gtest", + ] + defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] +} diff --git a/components/web_discovery/browser/DEPS b/components/web_discovery/browser/DEPS new file mode 100644 index 000000000000..972312b69aa4 --- /dev/null +++ b/components/web_discovery/browser/DEPS @@ -0,0 +1,9 @@ +include_rules = [ + "+services/network/public", + "+content/public/browser", + "+extensions/buildflags/buildflags.h", + "+services/service_manager/public/cpp", + "+third_party/boringssl/src/include", + "+third_party/re2", + "+third_party/zlib", +] diff --git a/components/web_discovery/browser/anonymous_credentials/rs/.gitignore b/components/web_discovery/browser/anonymous_credentials/rs/.gitignore new file mode 100644 index 000000000000..d9510d9f6795 --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/.gitignore @@ -0,0 +1,3 @@ +target +example/main +Cargo.lock diff --git a/components/web_discovery/browser/anonymous_credentials/rs/cxx/BUILD.gn b/components/web_discovery/browser/anonymous_credentials/rs/cxx/BUILD.gn new file mode 100644 index 000000000000..03dbf10acfda --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/cxx/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright (c) 2024 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +import("//build/rust/rust_static_library.gni") + +rust_static_library("rust_lib") { + crate_name = "anonymous_credentials_cxx" + crate_root = "src/lib.rs" + allow_unsafe = true + + edition = "2021" + sources = [ "src/lib.rs" ] + + visibility = [ "//brave/components/web_discovery/browser:*" ] + + cxx_bindings = [ "src/lib.rs" ] + + deps = [ "//brave/components/web_discovery/browser/anonymous_credentials/rs/lib:rust_lib" ] +} diff --git a/components/web_discovery/browser/anonymous_credentials/rs/cxx/Cargo.toml b/components/web_discovery/browser/anonymous_credentials/rs/cxx/Cargo.toml new file mode 100644 index 000000000000..83a371436ccd --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/cxx/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "anonymous-credentials-cxx" +version = "0.1.0" +edition = "2021" +license = "MPL-2.0" + +[dependencies] +anonymous-credentials = { version = "0.1", path = "../lib" } +cxx = "1" + +[lib] +name = "anonymous_credentials_cxx" +crate-type = ["rlib"] diff --git a/components/web_discovery/browser/anonymous_credentials/rs/cxx/src/lib.rs b/components/web_discovery/browser/anonymous_credentials/rs/cxx/src/lib.rs new file mode 100644 index 000000000000..27ee45a287f0 --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/cxx/src/lib.rs @@ -0,0 +1,166 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use anonymous_credentials::{ + CredentialBIG as InternalCredentialBIG, CredentialManager as InternalCredentialManager, + GroupPublicKey as InternalGroupPublicKey, JoinResponse as InternalJoinResponse, Result, + UserCredentials as InternalUserCredentials, +}; + +#[cxx::bridge(namespace = "anonymous_credentials")] +mod ffi { + struct StartJoinResult { + gsk: Vec, + join_request: Vec, + } + struct VecU8Result { + data: Vec, + error_message: String, + } + struct GroupPublicKeyResult { + value: Box, + error_message: String, + } + struct CredentialBIGResult { + value: Box, + error_message: String, + } + struct JoinResponseResult { + value: Box, + error_message: String, + } + struct UserCredentialsResult { + value: Box, + error_message: String, + } + + extern "Rust" { + type CredentialManager; + type GroupPublicKey; + type CredentialBIG; + type JoinResponse; + type UserCredentials; + + fn load_group_public_key(data: &[u8]) -> GroupPublicKeyResult; + fn load_credential_big(data: &[u8]) -> CredentialBIGResult; + fn load_join_response(data: &[u8]) -> JoinResponseResult; + fn load_user_credentials(data: &[u8]) -> UserCredentialsResult; + + fn new_credential_manager() -> Box; + fn new_credential_manager_with_fixed_seed() -> Box; + fn start_join(self: &mut CredentialManager, challenge: &[u8]) -> StartJoinResult; + fn finish_join( + self: &mut CredentialManager, + public_key: &GroupPublicKey, + gsk: &CredentialBIG, + join_resp: Box, + ) -> VecU8Result; + fn set_gsk_and_credentials( + self: &mut CredentialManager, + gsk: Box, + credentials: Box, + ); + fn sign(self: &mut CredentialManager, msg: &[u8], basename: &[u8]) -> VecU8Result; + } +} + +use ffi::*; + +macro_rules! wrapper_with_loader { + ($internal_type:ident, $wrapped_type:ident, $result_type:ident, $loader_func_name:ident) => { + #[derive(Default)] + struct $wrapped_type($internal_type); + + fn $loader_func_name(data: &[u8]) -> $result_type { + match $internal_type::try_from(data) { + Ok(value) => $result_type { + value: Box::new($wrapped_type(value)), + error_message: String::new(), + }, + Err(e) => $result_type { + value: Box::new(Default::default()), + error_message: e.to_string(), + }, + } + } + }; +} + +wrapper_with_loader!( + InternalGroupPublicKey, + GroupPublicKey, + GroupPublicKeyResult, + load_group_public_key +); +wrapper_with_loader!( + InternalCredentialBIG, + CredentialBIG, + CredentialBIGResult, + load_credential_big +); +wrapper_with_loader!(InternalJoinResponse, JoinResponse, JoinResponseResult, load_join_response); +wrapper_with_loader!( + InternalUserCredentials, + UserCredentials, + UserCredentialsResult, + load_user_credentials +); + +#[allow(dead_code)] +struct CredentialManager(InternalCredentialManager); + +fn new_credential_manager() -> Box { + Box::new(CredentialManager(InternalCredentialManager::new())) +} + +fn new_credential_manager_with_fixed_seed() -> Box { + Box::new(CredentialManager(InternalCredentialManager::new_with_seed(&[0u8; 1]))) +} + +impl CredentialManager { + fn start_join(&mut self, challenge: &[u8]) -> StartJoinResult { + let result = self.0.start_join(challenge); + StartJoinResult { + gsk: result.gsk.to_bytes().to_vec(), + join_request: result.join_msg.to_bytes().to_vec(), + } + } + + /// Processes response and returns user credentials + fn finish_join( + &mut self, + public_key: &GroupPublicKey, + gsk: &CredentialBIG, + join_resp: Box, + ) -> VecU8Result { + || -> Result> { + self.0 + .finish_join(&public_key.0, &gsk.0, join_resp.0) + .map(|creds| creds.to_bytes().to_vec()) + }() + .into() + } + + fn set_gsk_and_credentials( + &mut self, + gsk: Box, + credentials: Box, + ) { + self.0.set_gsk_and_credentials(gsk.0, credentials.0); + } + + fn sign(&mut self, msg: &[u8], basename: &[u8]) -> VecU8Result { + self.0.sign(msg, basename).map(|sig| sig.to_bytes().to_vec()).into() + } +} + +impl From>> for VecU8Result { + fn from(value: Result>) -> Self { + match value { + Ok(data) => VecU8Result { data, error_message: String::new() }, + Err(e) => VecU8Result { data: Vec::new(), error_message: e.to_string() }, + } + } +} diff --git a/components/web_discovery/browser/anonymous_credentials/rs/lib/BUILD.gn b/components/web_discovery/browser/anonymous_credentials/rs/lib/BUILD.gn new file mode 100644 index 000000000000..03e10c4e4a16 --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/lib/BUILD.gn @@ -0,0 +1,29 @@ +# Copyright (c) 2024 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +import("//build/rust/rust_static_library.gni") + +rust_static_library("rust_lib") { + crate_name = "anonymous_credentials" + crate_root = "src/lib.rs" + sources = [ + "src/data.rs", + "src/join.rs", + "src/lib.rs", + "src/sign.rs", + "src/util.rs", + ] + + visibility = [ + "//brave/components/web_discovery/browser/anonymous_credentials/rs/cxx:*", + ] + + deps = [ + "//brave/third_party/rust/brave_miracl/v0_1:lib", + "//brave/third_party/rust/lazy_static/v1:lib", + "//brave/third_party/rust/rand/v0_8:lib", + "//brave/third_party/rust/thiserror/v1:lib", + ] +} diff --git a/components/web_discovery/browser/anonymous_credentials/rs/lib/Cargo.toml b/components/web_discovery/browser/anonymous_credentials/rs/lib/Cargo.toml new file mode 100644 index 000000000000..1efc85f2de2f --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/lib/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "anonymous-credentials" +version = "0.1.4" +edition = "2021" +authors = ["Darnell Andries "] +description = "Implementation of Direct Anonymous Attestation for the Web Discovery Project" +license = "MPL-2.0" +repository = "https://github.com/brave-experiments/anonymous-credentials-rs" +keywords = ["daa", "credentials", "bn254"] +categories = ["cryptography"] + +[dependencies] +lazy_static = "1.4" +rand = "0.8" +brave-miracl = { version = "0.1", features = ["std"] } +thiserror = "1.0" + +[dev-dependencies] +hex = "0.4" diff --git a/components/web_discovery/browser/anonymous_credentials/rs/lib/README.md b/components/web_discovery/browser/anonymous_credentials/rs/lib/README.md new file mode 100644 index 000000000000..da0fa655154f --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/lib/README.md @@ -0,0 +1,11 @@ +# anonymous-credentials + +[![Crates.io](https://img.shields.io/crates/v/anonymous-credentials?style=for-the-badge)](https://crates.io/crates/anonymous-credentials) + +Partial implementation of Direct Anonymous Attestation (DAA) for the Web Discovery Project. Only signer functions are available. Performs the same elliptic curve operations as the [original C library](https://github.com/whotracksme/anonymous-credentials). + +bn254 is the only supported curve for this library. + +## License + +This project is licensed under the terms of the [Mozilla Public License 2.0](LICENSE). diff --git a/components/web_discovery/browser/anonymous_credentials/rs/lib/src/data.rs b/components/web_discovery/browser/anonymous_credentials/rs/lib/src/data.rs new file mode 100644 index 000000000000..fd87748c6249 --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/lib/src/data.rs @@ -0,0 +1,293 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +use brave_miracl::bn254::{ + big::{self, BIG}, + ecp::ECP, + ecp2::ECP2, + fp2::FP2, +}; + +use super::CredentialError; + +pub const BIG_SIZE: usize = big::MODBYTES; +pub const ECP_SIZE: usize = BIG_SIZE * 2 + 1; +pub const ECP2_COMPAT_SIZE: usize = BIG_SIZE * 4; + +pub const ECP_PROOF_SIZE: usize = BIG_SIZE * 2; +pub const JOIN_REQUEST_SIZE: usize = ECP_SIZE + ECP_PROOF_SIZE; +pub const USER_CREDENTIALS_SIZE: usize = ECP_SIZE * 4; +pub const JOIN_RESPONSE_SIZE: usize = USER_CREDENTIALS_SIZE + ECP_PROOF_SIZE; +pub const GROUP_PUBLIC_KEY_SIZE: usize = ECP2_COMPAT_SIZE * 2 + BIG_SIZE * 4; +pub const SIGNATURE_SIZE: usize = ECP_SIZE * 5 + ECP_PROOF_SIZE; + +/// A "join" request to be sent to the issuer. +pub struct JoinRequest { + pub(crate) q: ECP, // G1 ** gsk + + pub(crate) proof: ECPProof, +} + +/// A "join" response from the issuer, to be used +/// to generate DAA credentials. +pub struct JoinResponse { + pub(crate) cred: UserCredentials, + pub(crate) proof: ECPProof, +} + +/// DAA credentials to be used for signing messages. +pub struct UserCredentials { + pub(crate) a: ECP, + pub(crate) b: ECP, + pub(crate) c: ECP, + pub(crate) d: ECP, +} + +/// A DAA signature to be sent to the verifier. +pub struct Signature { + pub(crate) a: ECP, + pub(crate) b: ECP, + pub(crate) c: ECP, + pub(crate) d: ECP, + pub(crate) nym: ECP, + + pub(crate) proof: ECPProof, +} + +/// A group public key published by the issuer. +/// This is required to finish the "join" process +/// and acquire credentials. +pub struct GroupPublicKey { + pub(crate) x: ECP2, // G2 ** x + pub(crate) y: ECP2, // G2 ** y + + // ZK of discrete-log knowledge for X and Y + pub(crate) cx: BIG, + pub(crate) sx: BIG, + pub(crate) cy: BIG, + pub(crate) sy: BIG, +} + +pub(crate) struct ECPProof { + pub c: BIG, + pub s: BIG, +} + +/// Wrapper for a big number. +pub struct CredentialBIG(pub(crate) BIG); + +/// A result of starting the "join" process to acquire credentials. +pub struct StartJoinResult { + /// Private key which should be persisted for finishing the "join" + /// process and future signing requests. + pub gsk: CredentialBIG, + /// The join request to be sent to the issuer. + pub join_msg: JoinRequest, +} + +pub(crate) fn ecp_from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != ECP_SIZE { + return Err(CredentialError::BadECP); + } + Ok(ECP::frombytes(bytes)) +} + +pub(crate) fn ecp2_from_compat_bytes(bytes: &[u8]) -> Result { + if bytes.len() != ECP2_COMPAT_SIZE { + return Err(CredentialError::BadECP2); + } + let x = FP2::new_bigs( + &big_from_bytes(&bytes[..BIG_SIZE])?, + &big_from_bytes(&bytes[BIG_SIZE..BIG_SIZE * 2])?, + ); + let y = FP2::new_bigs( + &big_from_bytes(&bytes[BIG_SIZE * 2..BIG_SIZE * 3])?, + &big_from_bytes(&bytes[BIG_SIZE * 3..BIG_SIZE * 4])?, + ); + Ok(ECP2::new_fp2s(&x, &y)) +} + +pub(crate) fn ecp2_to_compat_bytes(point: &ECP2) -> [u8; ECP2_COMPAT_SIZE] { + let mut result = [0u8; ECP2_COMPAT_SIZE]; + + let mut x = point.getx(); + let mut y = point.gety(); + + x.geta().tobytes(&mut result[..BIG_SIZE]); + x.getb().tobytes(&mut result[BIG_SIZE..BIG_SIZE * 2]); + y.geta().tobytes(&mut result[BIG_SIZE * 2..BIG_SIZE * 3]); + y.getb().tobytes(&mut result[BIG_SIZE * 3..]); + + result +} + +pub(crate) fn big_from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != BIG_SIZE { + return Err(CredentialError::BadBIG); + } + Ok(BIG::frombytes(bytes)) +} + +impl TryFrom<&[u8]> for JoinResponse { + type Error = CredentialError; + + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() != JOIN_RESPONSE_SIZE { + return Err(CredentialError::BadJoinResponse); + } + + Ok(JoinResponse { + cred: bytes[..USER_CREDENTIALS_SIZE].try_into()?, + proof: bytes[USER_CREDENTIALS_SIZE..].try_into()?, + }) + } +} + +impl TryFrom<&[u8]> for UserCredentials { + type Error = CredentialError; + + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() != USER_CREDENTIALS_SIZE { + return Err(CredentialError::BadUserCredentials); + } + Ok(UserCredentials { + a: ecp_from_bytes(&bytes[..ECP_SIZE])?, + b: ecp_from_bytes(&bytes[ECP_SIZE..ECP_SIZE * 2])?, + c: ecp_from_bytes(&bytes[ECP_SIZE * 2..ECP_SIZE * 3])?, + d: ecp_from_bytes(&bytes[ECP_SIZE * 3..ECP_SIZE * 4])?, + }) + } +} + +impl TryFrom<&[u8]> for GroupPublicKey { + type Error = CredentialError; + + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() != GROUP_PUBLIC_KEY_SIZE { + return Err(CredentialError::GroupPublicKeyLength); + } + + let big_start = ECP2_COMPAT_SIZE * 2; + + Ok(GroupPublicKey { + x: ecp2_from_compat_bytes(&bytes[..ECP2_COMPAT_SIZE])?, + y: ecp2_from_compat_bytes(&bytes[ECP2_COMPAT_SIZE..ECP2_COMPAT_SIZE * 2])?, + cx: big_from_bytes(&bytes[big_start..big_start + BIG_SIZE])?, + sx: big_from_bytes(&bytes[big_start + BIG_SIZE..big_start + BIG_SIZE * 2])?, + cy: big_from_bytes(&bytes[big_start + BIG_SIZE * 2..big_start + BIG_SIZE * 3])?, + sy: big_from_bytes(&bytes[big_start + BIG_SIZE * 3..big_start + BIG_SIZE * 4])?, + }) + } +} + +impl CredentialBIG { + pub fn to_bytes(&self) -> [u8; BIG_SIZE] { + let mut bytes = [0u8; BIG_SIZE]; + self.0.tobytes(&mut bytes); + bytes + } +} + +impl TryFrom<&[u8]> for CredentialBIG { + type Error = CredentialError; + + fn try_from(bytes: &[u8]) -> Result { + Ok(Self(big_from_bytes(bytes)?)) + } +} + +impl JoinRequest { + pub fn to_bytes(&self) -> [u8; JOIN_REQUEST_SIZE] { + let mut result = [0u8; JOIN_REQUEST_SIZE]; + self.q.tobytes(&mut result, false); + result[ECP_SIZE..].copy_from_slice(&self.proof.to_bytes()); + result + } +} + +impl UserCredentials { + pub fn to_bytes(&self) -> [u8; USER_CREDENTIALS_SIZE] { + let mut result = [0u8; USER_CREDENTIALS_SIZE]; + self.a.tobytes(&mut result[..ECP_SIZE], false); + self.b.tobytes(&mut result[ECP_SIZE..ECP_SIZE * 2], false); + self.c.tobytes(&mut result[ECP_SIZE * 2..ECP_SIZE * 3], false); + self.d.tobytes(&mut result[ECP_SIZE * 3..ECP_SIZE * 4], false); + result + } +} + +impl ECPProof { + pub fn to_bytes(&self) -> [u8; ECP_PROOF_SIZE] { + let mut result = [0u8; ECP_PROOF_SIZE]; + self.c.tobytes(&mut result[..BIG_SIZE]); + self.s.tobytes(&mut result[BIG_SIZE..]); + result + } +} + +impl TryFrom<&[u8]> for ECPProof { + type Error = CredentialError; + + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() != ECP_PROOF_SIZE { + return Err(CredentialError::BadECPProof); + } + + Ok(ECPProof { + c: big_from_bytes(&bytes[0..BIG_SIZE])?, + s: big_from_bytes(&bytes[BIG_SIZE..])?, + }) + } +} + +impl Signature { + pub fn to_bytes(&self) -> [u8; SIGNATURE_SIZE] { + let mut result = [0u8; SIGNATURE_SIZE]; + self.a.tobytes(&mut result[..ECP_SIZE], false); + self.b.tobytes(&mut result[ECP_SIZE..ECP_SIZE * 2], false); + self.c.tobytes(&mut result[ECP_SIZE * 2..ECP_SIZE * 3], false); + self.d.tobytes(&mut result[ECP_SIZE * 3..ECP_SIZE * 4], false); + self.nym.tobytes(&mut result[ECP_SIZE * 4..ECP_SIZE * 5], false); + result[ECP_SIZE * 5..].copy_from_slice(&self.proof.to_bytes()); + result + } +} + +impl Default for CredentialBIG { + fn default() -> Self { + Self(BIG::new()) + } +} + +impl Default for ECPProof { + fn default() -> Self { + Self { c: BIG::new(), s: BIG::new() } + } +} + +impl Default for JoinResponse { + fn default() -> Self { + Self { cred: UserCredentials::default(), proof: ECPProof::default() } + } +} + +impl Default for UserCredentials { + fn default() -> Self { + UserCredentials { a: ECP::new(), b: ECP::new(), c: ECP::new(), d: ECP::new() } + } +} + +impl Default for GroupPublicKey { + fn default() -> Self { + GroupPublicKey { + x: ECP2::new(), + y: ECP2::new(), + cx: BIG::new(), + sx: BIG::new(), + cy: BIG::new(), + sy: BIG::new(), + } + } +} diff --git a/components/web_discovery/browser/anonymous_credentials/rs/lib/src/join.rs b/components/web_discovery/browser/anonymous_credentials/rs/lib/src/join.rs new file mode 100644 index 000000000000..96ceb816c63f --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/lib/src/join.rs @@ -0,0 +1,182 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +use brave_miracl::{ + bn254::{ + big::BIG, + ecp::ECP, + ecp2::ECP2, + fp12::FP12, + pair::{g1mul, g2mul}, + }, + rand::RAND, +}; + +use super::data::{ + ecp2_to_compat_bytes, CredentialBIG, ECPProof, GroupPublicKey, JoinRequest, JoinResponse, + StartJoinResult, UserCredentials, BIG_SIZE, ECP2_COMPAT_SIZE, ECP_SIZE, +}; +use super::util::{ + ecp_challenge_equals, hash256, pair_normalized_triple_ate, random_mod_curve_order, + CURVE_ORDER_BIG, G1_ECP, G2_ECP, +}; +use super::{CredentialError, Result}; + +fn ecp_challenge(message: &[u8; BIG_SIZE], y: &ECP, g: &ECP, gr: &ECP) -> BIG { + let mut all_bytes = [0u8; ECP_SIZE * 3 + BIG_SIZE]; + + all_bytes[..BIG_SIZE].copy_from_slice(message); + + y.tobytes(&mut all_bytes[BIG_SIZE..], false); + g.tobytes(&mut all_bytes[ECP_SIZE + BIG_SIZE..], false); + gr.tobytes(&mut all_bytes[ECP_SIZE * 2 + BIG_SIZE..], false); + + let hash = hash256(&all_bytes); + + let mut c = BIG::frombytes(&hash); + c.rmod(&CURVE_ORDER_BIG); + c +} + +fn make_ecp_proof(rng: &mut RAND, y: &ECP, x: &BIG, message: &[u8; BIG_SIZE]) -> ECPProof { + let r = random_mod_curve_order(rng); + + let g = G1_ECP.clone(); + let gr = g1mul(&g, &r); + let c = ecp_challenge(message, y, &g, &gr); + let mut s = BIG::modmul(&c, x, &CURVE_ORDER_BIG); + s.add(&r); + s.rmod(&CURVE_ORDER_BIG); + ECPProof { c, s } +} + +fn verify_ecp_proof_equals(a: &ECP, b: &ECP, y: &ECP, z: &ECP, proof: &ECPProof) -> bool { + let cn = BIG::modneg(&proof.c, &CURVE_ORDER_BIG); + + let mut r#as = g1mul(a, &proof.s); + let yc = g1mul(y, &cn); + let mut bs = g1mul(b, &proof.s); + let zc = g1mul(z, &cn); + + r#as.add(&yc); + bs.add(&zc); + + let cc = ecp_challenge_equals(None, &y, &z, &a, &b, &r#as, &bs); + + BIG::comp(&proof.c, &cc) == 0 +} + +fn verify_aux_fast(a: &ECP, b: &ECP, c: &ECP, d: &ECP, x: &ECP2, y: &ECP2, rng: &mut RAND) -> bool { + if a.is_infinity() { + return false; + } + + let e1 = random_mod_curve_order(rng); + let e2 = random_mod_curve_order(rng); + let ne1 = BIG::modneg(&e1, &CURVE_ORDER_BIG); + let ne2 = BIG::modneg(&e2, &CURVE_ORDER_BIG); + + // AA = e1 * A + let aa = g1mul(a, &e1); + + // BB = -e1 * B + let mut bb = g1mul(b, &ne1); + + // CC = -e2 * C + let mut cc = g1mul(c, &ne2); + + // BB = (-e1 * B) + (-e2 * C) + bb.add(&cc); + + // CC = e2 * (A + D) + cc.copy(a); + cc.add(d); + cc = g1mul(&cc, &e2); + + // w = e(e1·A, Y)·e((-e1·B) + (-e2·C), G2)·e(e2·(A + D), X) + let w = pair_normalized_triple_ate(y, &aa, &G2_ECP, &bb, x, &cc); + + let mut fp12_one = FP12::new(); + fp12_one.one(); + + w.equals(&fp12_one) +} + +pub fn start_join(rng: &mut RAND, challenge: &[u8]) -> StartJoinResult { + let gsk = random_mod_curve_order(rng); + let q = g1mul(&G1_ECP, &gsk); + + let challenge_hash = hash256(challenge); + + let proof = make_ecp_proof(rng, &q, &gsk, &challenge_hash); + + StartJoinResult { gsk: CredentialBIG(gsk), join_msg: JoinRequest { q, proof } } +} + +pub fn finish_join( + pub_key: &GroupPublicKey, + gsk: &CredentialBIG, + resp: JoinResponse, +) -> Result { + verify_group_public_key(pub_key)?; + + let q = g1mul(&G1_ECP, &gsk.0); + + let mut rng = RAND::new(); + rng.seed(BIG_SIZE, &gsk.to_bytes()); + + if !verify_ecp_proof_equals(&G1_ECP, &q, &resp.cred.b, &resp.cred.d, &resp.proof) { + return Err(CredentialError::JoinResponseValidation); + } + + if !verify_aux_fast( + &resp.cred.a, + &resp.cred.b, + &resp.cred.c, + &resp.cred.d, + &pub_key.x, + &pub_key.y, + &mut rng, + ) { + return Err(CredentialError::JoinResponseValidation); + } + + Ok(resp.cred) +} + +fn ecp2_challenge(y: &ECP2, g: &ECP2, gr: &ECP2) -> BIG { + let mut all_bytes = [0u8; ECP2_COMPAT_SIZE * 3]; + + all_bytes[..ECP2_COMPAT_SIZE].copy_from_slice(&ecp2_to_compat_bytes(y)); + all_bytes[ECP2_COMPAT_SIZE..ECP2_COMPAT_SIZE * 2].copy_from_slice(&ecp2_to_compat_bytes(g)); + all_bytes[ECP2_COMPAT_SIZE * 2..].copy_from_slice(&ecp2_to_compat_bytes(gr)); + + let hash = hash256(&all_bytes); + + let mut c = BIG::frombytes(&hash); + c.rmod(&CURVE_ORDER_BIG); + c +} + +fn verify_ecp2_proof(y: &ECP2, c: &BIG, s: &BIG) -> bool { + let cn = BIG::modneg(c, &CURVE_ORDER_BIG); + + let mut gs = g2mul(&G2_ECP, s); + let yc = g2mul(y, &cn); + + gs.add(&yc); + + let cc = ecp2_challenge(y, &G2_ECP, &gs); + + BIG::comp(c, &cc) == 0 +} + +fn verify_group_public_key(key: &GroupPublicKey) -> Result<()> { + match verify_ecp2_proof(&key.x, &key.cx, &key.sx) && verify_ecp2_proof(&key.y, &key.cy, &key.sy) + { + true => Ok(()), + false => Err(CredentialError::BadGroupPublicKey), + } +} diff --git a/components/web_discovery/browser/anonymous_credentials/rs/lib/src/lib.rs b/components/web_discovery/browser/anonymous_credentials/rs/lib/src/lib.rs new file mode 100644 index 000000000000..15effca276dd --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/lib/src/lib.rs @@ -0,0 +1,243 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Partial implementation of Direct Anonymous Attestation (DAA) for the Web Discovery Project. +//! Only signer functions are available. Performs the same elliptic curve operations as the [original C library](https://github.com/whotracksme/anonymous-credentials). +//! +//! bn254 is the only supported curve for this library. + +mod data; +mod join; +mod sign; +mod util; + +use brave_miracl::rand::RAND; +use rand::{rngs::OsRng, RngCore}; +use thiserror::Error; + +pub use self::data::*; +use self::join::{finish_join, start_join}; +use self::sign::sign; + +#[derive(Error, Debug)] +pub enum CredentialError { + #[error("ECP should be {0} bytes", ECP_SIZE)] + BadECP, + #[error("ECP2 should be {0} bytes", ECP2_COMPAT_SIZE)] + BadECP2, + #[error("BIG should be {0} bytes", BIG_SIZE)] + BadBIG, + #[error("ECP proof should be {0} bytes", ECP_PROOF_SIZE)] + BadECPProof, + #[error("User credentials should be {0} bytes", USER_CREDENTIALS_SIZE)] + BadUserCredentials, + #[error("Join response should be {0} bytes", JOIN_RESPONSE_SIZE)] + BadJoinResponse, + #[error("Group public key should be {0} bytes", GROUP_PUBLIC_KEY_SIZE)] + GroupPublicKeyLength, + #[error("Join response validation failed")] + JoinResponseValidation, + #[error("Private key and/or credentials not set")] + CredentialsNotSet, + #[error("Group public key verification failed")] + BadGroupPublicKey, +} + +pub type Result = std::result::Result; + +/// Creates and manages Direct Anonymous Attestation credentials +/// using the bn254 curve. +pub struct CredentialManager { + rng: RAND, + gsk_and_credentials: Option<(CredentialBIG, UserCredentials)>, +} + +impl CredentialManager { + /// Creates new manager with random seed. + pub fn new() -> Self { + let mut entropy = [0u8; 128]; + OsRng::default().fill_bytes(&mut entropy); + Self::new_with_seed(&entropy) + } + + /// Creates new manager with fixed seed. Should only be used for testing. + pub fn new_with_seed(entropy: &[u8]) -> Self { + let mut rng = RAND::new(); + + rng.seed(entropy.len(), entropy); + + Self { rng, gsk_and_credentials: None } + } + + /// Creates a "join" requests to be sent to the credential issuer, + /// for a given challenge. + pub fn start_join(&mut self, challenge: &[u8]) -> StartJoinResult { + start_join(&mut self.rng, challenge) + } + + /// Processes a "join" response from the issuer, and returns anonymous + /// credentials. + pub fn finish_join( + &mut self, + public_key: &GroupPublicKey, + gsk: &CredentialBIG, + join_resp: JoinResponse, + ) -> Result { + finish_join(public_key, gsk, join_resp) + } + + /// Sets the key and credentials to be used for signing requests. + pub fn set_gsk_and_credentials(&mut self, gsk: CredentialBIG, credentials: UserCredentials) { + self.gsk_and_credentials = Some((gsk, credentials)); + } + + /// Signs a message using the pre-set credentials and a given basename. + /// Returns a signature to be sent to the verifier. + pub fn sign(&mut self, msg: &[u8], basename: &[u8]) -> Result { + match &self.gsk_and_credentials { + Some((gsk, credentials)) => Ok(sign(&mut self.rng, gsk, credentials, msg, basename)), + None => Err(CredentialError::CredentialsNotSet), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use lazy_static::lazy_static; + + lazy_static! { + static ref EXPECTED_GSK: Vec = + hex::decode("0198c86f99ede0ca2ed30b8e4ae6cac831c9b398445422a41d95abb4d3f03499") + .unwrap(); + static ref GROUP_PUB_KEY: Vec = hex::decode( + "0477ce930400ab04a6e1caa46601dbd1b1ba5d24f0577834a960285a0512e7ed\ + 0174121707ea5d80e083d2e992236864608998a4d08cb3a41dde1fc6b7eaad5b2\ + 125310b44ca712bf63f62c39cb44917de0772fefd876e170729428142c21d4f17\ + 9f72fcdc1c1ff5f13e272449ac9ff01a74e95bb011045b12bdac942b46168d051\ + 1ecbb4651d9ddd6491437a8d6b6e6e6877038ea4317a5de863e237ff6472014d2\ + 2c88863b6d8de3eb1b73bb46ab12553c2765bcde905487c518936887ba831dc42\ + ca4862bf60b7cccf08ae579f14699fcff5ec8366af5562a2117095dc066105c17\ + 714dadae0b2110b91d0f19f062e9ab410f59e4515cb027e268435502cf12d4a2d\ + de1c5b711619507485e54e6e6bb1b279e7f42067c47b124e7b1e044de0345f28c\ + ea642eef79e0da60dad085b9bec8b73c61a4eee59ec4f024fc83366e1efc63762\ + b2c1c214ad151dd01f1a5f16d5a238187f1afdab361dfea2e0956be24b1bcdba7\ + c9a6a5e0296377bd1cf1b722bc4d375ae8aa4761b7aac5a50e9871" + ) + .unwrap(); + static ref EXPECTED_CREDENTIALS: Vec = hex::decode( + "04246220e5a9d48d359178c9e0994cc10f7288b50\ + cd059c24c5a26fc5919682e8017b66ca6185d62bf2\ + bed7cf02503157ab93ff79d8d34ab3c48669954b7e\ + 2b69c041d98fde59abcd8c0f22790e8d40e253c124\ + 0f3697c161d18a9d04ca24ba2b01f0d100b28b3d52\ + 9939ec717f4f39e114337878f03c9066afc2250332\ + 76f162b4904248822cb548ccb8167480e23f019813\ + 4d1547b005ac84c2a7101a4d39c924ee50298022d7\ + dd7c9f0006eab2576635a36af81e0f781437c4ee35\ + b8672511089830401074ad73c4e9e9aed541bdc5a2\ + df2ee815a3ac4f6297b73da35db2a646e19720475c\ + fe50eb2465833b50758f6c8f09fdf645643a4b3ef5\ + bd494be6a551768c8", + ) + .unwrap(); + } + + const CHALLENGE: &[u8] = b"challenge"; + + fn manager_with_fixed_seed() -> CredentialManager { + let entropy = [0u8; 1]; + CredentialManager::new_with_seed(&entropy) + } + + #[test] + fn test_gsk_and_join_msg() { + let mut cm = manager_with_fixed_seed(); + let result = cm.start_join(CHALLENGE); + + let expected_join_msg = hex::decode( + "04185d9e3a0f0e590928568a951a70749c5f3e\ + 969b3c335f109f0e95f4c0cbabe70ddd27e6751\ + df37fa70b906d3d246b388a2a9fb67c3972ea1b\ + 822dc80653454e0de86a209418b4953191caa980\ + 462463ec21b07da451e7becc2d7917ef34f5150c\ + 1c0a4187182af0a43a28868c7b17b2c73704bc26\ + 8071b414d99f48999014b2", + ) + .unwrap(); + + assert_eq!(result.gsk.to_bytes().as_slice(), EXPECTED_GSK.as_slice()); + assert_eq!(result.join_msg.to_bytes().as_slice(), &expected_join_msg); + } + + #[test] + fn test_finish_join_credentials() { + let mut cm = manager_with_fixed_seed(); + + let group_pub_key: GroupPublicKey = GROUP_PUB_KEY.as_slice().try_into().unwrap(); + let gsk: CredentialBIG = EXPECTED_GSK.as_slice().try_into().unwrap(); + let join_response: JoinResponse = hex::decode( + "04246220e5a9d48d359178c9e0994cc10f7288b50\ + cd059c24c5a26fc5919682e8017b66ca6185d62bf2\ + bed7cf02503157ab93ff79d8d34ab3c48669954b7e\ + 2b69c041d98fde59abcd8c0f22790e8d40e253c124\ + 0f3697c161d18a9d04ca24ba2b01f0d100b28b3d52\ + 9939ec717f4f39e114337878f03c9066afc2250332\ + 76f162b4904248822cb548ccb8167480e23f019813\ + 4d1547b005ac84c2a7101a4d39c924ee50298022d7\ + dd7c9f0006eab2576635a36af81e0f781437c4ee35\ + b8672511089830401074ad73c4e9e9aed541bdc5a2\ + df2ee815a3ac4f6297b73da35db2a646e19720475c\ + fe50eb2465833b50758f6c8f09fdf645643a4b3ef5\ + bd494be6a551768c81677932196184249f179d319f\ + eba43b32da42501daa355d3cde30615a08ac687188\ + a8c6e3b8a330f76c233e900acd6ef31c50796b9192\ + 9cfc16b4fcad40b5309", + ) + .unwrap() + .as_slice() + .try_into() + .unwrap(); + + let credentials = cm.finish_join(&group_pub_key, &gsk, join_response).unwrap().to_bytes(); + + assert_eq!(credentials.as_slice(), EXPECTED_CREDENTIALS.as_slice()); + } + + #[test] + fn test_signature() { + let mut cm = manager_with_fixed_seed(); + + let gsk = EXPECTED_GSK.as_slice().try_into().unwrap(); + let credentials = EXPECTED_CREDENTIALS.as_slice().try_into().unwrap(); + cm.set_gsk_and_credentials(gsk, credentials); + + let expected_signature = hex::decode( + "0406cb022fcc3dcaef1e4c62dad349bfd263581126c\ + f17b293d1a41e4d96f840da00ad85e4a97aad1247a19\ + a425da6f96978fdac180136f0f486bad0fce0a9ada20\ + 401074ad73c4e9e9aed541bdc5a2df2ee815a3ac4f62\ + 97b73da35db2a646e19720475cfe50eb2465833b5075\ + 8f6c8f09fdf645643a4b3ef5bd494be6a551768c8042\ + 4a006154937bcd3b8f94f12a4672d9a9411928846adc\ + 9132737600089a65915121160cbd4e417435e4acfe66\ + 57840c50584bc8dca420544879fe7fe9c03bc0f0418e\ + e65a71c262c5301d782b20e7f3f252e938282b98a2f8\ + 6a7447e2aa424005819835a0a954d4f6dc53ae4c8bad\ + 2d192a70fcb8883403f69989e43ff66caad0104208cd\ + 8eb5e25486eb754e44f4e2f3b6f5153f5aa73d7eab8c\ + 2f5c867a157276b0463ced7f409f86ef91a4548cf4bb\ + 519392cd657505475e585a3ea0348b6266b1fd3d4ab1\ + 253392386bf2f08afe36072abe575e07865272c3014a\ + b067f5051181fe574571f34d278e2c9359294ca44aa3\ + 3568c546082e4e8d921541e5ccc6c81", + ) + .unwrap(); + + let signature_bytes = cm.sign(b"message", b"basename").unwrap().to_bytes(); + + assert_eq!(signature_bytes, expected_signature.as_slice()); + } +} diff --git a/components/web_discovery/browser/anonymous_credentials/rs/lib/src/sign.rs b/components/web_discovery/browser/anonymous_credentials/rs/lib/src/sign.rs new file mode 100644 index 000000000000..4012347312e7 --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/lib/src/sign.rs @@ -0,0 +1,66 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +use brave_miracl::{ + bn254::{big::BIG, ecp::ECP, pair::g1mul}, + rand::RAND, +}; + +use super::{ + data::{CredentialBIG, ECPProof, Signature, UserCredentials, BIG_SIZE}, + util::{ecp_challenge_equals, hash256, random_mod_curve_order, CURVE_ORDER_BIG}, +}; + +fn make_ecp_proof_equals( + rng: &mut RAND, + message: &[u8; BIG_SIZE], + a: &ECP, + b: &ECP, + y: &ECP, + z: &ECP, + x: &BIG, +) -> ECPProof { + let r = random_mod_curve_order(rng); + + let ar = g1mul(&a, &r); + let br = g1mul(&b, &r); + + let c = ecp_challenge_equals(Some(message), y, z, a, b, &ar, &br); + let mut s = BIG::modmul(&c, x, &CURVE_ORDER_BIG); + s.add(&r); + s.rmod(&CURVE_ORDER_BIG); + + ECPProof { c, s } +} + +pub fn sign( + rng: &mut RAND, + gsk: &CredentialBIG, + credentials: &UserCredentials, + msg: &[u8], + bsn: &[u8], +) -> Signature { + // Randomize credentials for signature + let r = random_mod_curve_order(rng); + + let a = g1mul(&credentials.a, &r); + let b = g1mul(&credentials.b, &r); + let c = g1mul(&credentials.c, &r); + let d = g1mul(&credentials.d, &r); + + // Map basename to point in G1 + let bsn_hash = hash256(bsn); + let bsn_point = ECP::mapit(&bsn_hash); + let nym = g1mul(&bsn_point, &gsk.0); + + // Compute H(H(msg) || H(bsn)) to be used in proof of equality + let mut msg_bsn_hash_data = [0u8; BIG_SIZE * 2]; + msg_bsn_hash_data[..BIG_SIZE].copy_from_slice(&hash256(msg)); + msg_bsn_hash_data[BIG_SIZE..].copy_from_slice(&hash256(bsn)); + let msg_bsn_hash = hash256(&msg_bsn_hash_data); + + let proof = make_ecp_proof_equals(rng, &msg_bsn_hash, &b, &bsn_point, &d, &nym, &gsk.0); + Signature { a, b, c, d, nym, proof } +} diff --git a/components/web_discovery/browser/anonymous_credentials/rs/lib/src/util.rs b/components/web_discovery/browser/anonymous_credentials/rs/lib/src/util.rs new file mode 100644 index 000000000000..a249020edd0e --- /dev/null +++ b/components/web_discovery/browser/anonymous_credentials/rs/lib/src/util.rs @@ -0,0 +1,90 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +use brave_miracl::{ + bn254::{ + big::BIG, + ecp::ECP, + ecp2::ECP2, + fp12::FP12, + fp2::FP2, + pair::{another, fexp, initmp, miller}, + rom::{CURVE_GX, CURVE_GY, CURVE_ORDER, CURVE_PXA, CURVE_PXB, CURVE_PYA, CURVE_PYB}, + }, + hash256::HASH256, + rand::RAND, +}; +use lazy_static::lazy_static; + +use super::data::{BIG_SIZE, ECP_SIZE}; + +lazy_static! { + pub static ref G1_ECP: ECP = { + let gx = BIG::new_ints(&CURVE_GX); + let gy = BIG::new_ints(&CURVE_GY); + ECP::new_bigs(&gx, &gy) + }; + pub static ref G2_ECP: ECP2 = { + let pxa = BIG::new_ints(&CURVE_PXA); + let pxb = BIG::new_ints(&CURVE_PXB); + let pya = BIG::new_ints(&CURVE_PYA); + let pyb = BIG::new_ints(&CURVE_PYB); + let wx = FP2::new_bigs(&pxa, &pxb); + let wy = FP2::new_bigs(&pya, &pyb); + ECP2::new_fp2s(&wx, &wy) + }; + pub static ref CURVE_ORDER_BIG: BIG = BIG::new_ints(&CURVE_ORDER); +} + +pub fn random_mod_curve_order(rng: &mut RAND) -> BIG { + BIG::randomnum(&CURVE_ORDER_BIG, rng) +} + +pub fn hash256(data: &[u8]) -> [u8; 32] { + let mut hash = HASH256::new(); + hash.process_array(data); + hash.hash() +} + +pub fn pair_normalized_triple_ate(p: &ECP2, q: &ECP, r: &ECP2, s: &ECP, t: &ECP2, u: &ECP) -> FP12 { + let mut rr = initmp(); + another(&mut rr, p, q); + another(&mut rr, r, s); + another(&mut rr, t, u); + let r = miller(&mut rr); + fexp(&r) +} + +pub fn ecp_challenge_equals( + message: Option<&[u8; BIG_SIZE]>, + y: &ECP, + z: &ECP, + a: &ECP, + b: &ECP, + ar: &ECP, + br: &ECP, +) -> BIG { + let (mut all_bytes, msg_len) = match message { + Some(message) => { + let mut all_bytes = vec![0u8; ECP_SIZE * 6 + BIG_SIZE]; + all_bytes[..BIG_SIZE].copy_from_slice(message); + (all_bytes, message.len()) + } + None => (vec![0u8; ECP_SIZE * 6], 0), + }; + + y.tobytes(&mut all_bytes[msg_len..], false); + z.tobytes(&mut all_bytes[msg_len + ECP_SIZE..], false); + a.tobytes(&mut all_bytes[msg_len + ECP_SIZE * 2..], false); + b.tobytes(&mut all_bytes[msg_len + ECP_SIZE * 3..], false); + ar.tobytes(&mut all_bytes[msg_len + ECP_SIZE * 4..], false); + br.tobytes(&mut all_bytes[msg_len + ECP_SIZE * 5..], false); + + let hash = hash256(&all_bytes); + + let mut c = BIG::frombytes(&hash); + c.rmod(&CURVE_ORDER_BIG); + c +} diff --git a/components/web_discovery/browser/content_scraper.cc b/components/web_discovery/browser/content_scraper.cc new file mode 100644 index 000000000000..ad8310ec4e6d --- /dev/null +++ b/components/web_discovery/browser/content_scraper.cc @@ -0,0 +1,382 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/content_scraper.h" + +#include + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/ranges/algorithm.h" +#include "base/strings/string_split.h" +#include "base/task/thread_pool.h" +#include "brave/components/web_discovery/browser/patterns.h" +#include "brave/components/web_discovery/browser/privacy_guard.h" +#include "brave/components/web_discovery/browser/util.h" + +namespace web_discovery { + +namespace { + +constexpr char kUrlAttrId[] = "url"; +constexpr char kCountryCodeAttrId[] = "ctry"; +constexpr char kFieldsValueKey[] = "fields"; +constexpr char kIdValueKey[] = "id"; +constexpr char kUrlValueKey[] = "url"; + +constexpr char kRefineSplitFuncId[] = "splitF"; +constexpr char kRefineMaskURLFuncId[] = "maskU"; +constexpr char kRefineParseURLFuncId[] = "parseU"; +constexpr char kRefineJsonExtractFuncId[] = "json"; + +constexpr char kParseURLQueryExtractType[] = "qs"; + +std::string RefineSplit(const std::string& value, + const std::string& delimiter, + int index) { + auto split = base::SplitStringUsingSubstr( + value, delimiter, base::WhitespaceHandling::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL); + std::string encoded_result; + if (index < 0 || static_cast(index) >= split.size()) { + encoded_result = value; + } else { + encoded_result = split[index]; + } + return DecodeURLComponent(encoded_result); +} + +std::optional RefineParseURL(const std::string& value, + const std::string& extract_type, + const std::string& key) { + if (extract_type != kParseURLQueryExtractType) { + return std::nullopt; + } + GURL url(value); + if (!url.is_valid() || !url.has_query()) { + return std::nullopt; + } + auto query_value = ExtractValueFromQueryString(url.query_piece(), key); + return query_value; +} + +std::optional RefineJsonExtract(const std::string& value, + const std::string& path, + bool extract_objects) { + auto parsed = base::JSONReader::Read(value); + if (!parsed || !parsed->is_dict()) { + return std::nullopt; + } + const auto* found_value = parsed->GetDict().FindByDottedPath(path); + if (!found_value) { + return std::nullopt; + } + if (found_value->is_string()) { + return found_value->GetString(); + } + if ((found_value->is_dict() || found_value->is_list()) && !extract_objects) { + return std::nullopt; + } + std::string encoded_value; + if (!base::JSONWriter::Write(*found_value, &encoded_value)) { + return std::nullopt; + } + return encoded_value; +} + +} // namespace + +PageScrapeResult::PageScrapeResult(GURL url, std::string id) + : url(url), id(id) {} +PageScrapeResult::~PageScrapeResult() = default; + +ContentScraper::ContentScraper(const ServerConfigLoader* server_config_loader, + RegexUtil* regex_util) + : pool_sequenced_task_runner_( + base::ThreadPool::CreateSequencedTaskRunner({})), + server_config_loader_(server_config_loader), + regex_util_(regex_util) {} + +ContentScraper::~ContentScraper() = default; + +base::Value PageScrapeResult::SerializeToValue() { + base::Value::Dict result; + base::Value::Dict fields_dict; + + for (const auto& [root_selector, inner_fields] : fields) { + base::Value::List list; + for (const auto& values : inner_fields) { + list.Append(values.Clone()); + } + fields_dict.Set(root_selector, std::move(list)); + } + + result.Set(kFieldsValueKey, std::move(fields_dict)); + result.Set(kIdValueKey, id); + result.Set(kUrlValueKey, url.spec()); + return base::Value(std::move(result)); +} + +std::unique_ptr PageScrapeResult::FromValue( + const base::Value& value) { + if (!value.is_dict()) { + return nullptr; + } + const auto& dict = value.GetDict(); + const auto* fields_dict = dict.FindDict(kFieldsValueKey); + const auto* id = dict.FindString(kIdValueKey); + const auto* url = dict.FindString(kUrlValueKey); + + if (!fields_dict || !id || !url) { + return nullptr; + } + + auto result = std::make_unique(GURL(*url), *id); + for (const auto [root_selector, inner_fields_val] : *fields_dict) { + const auto* inner_fields_list = inner_fields_val.GetIfList(); + if (!inner_fields_list) { + continue; + } + for (const auto& values : *inner_fields_list) { + const auto* values_dict = values.GetIfDict(); + if (!values_dict) { + continue; + } + result->fields[root_selector].push_back(values_dict->Clone()); + } + } + + return result; +} + +void ContentScraper::ScrapePage(const GURL& url, + bool is_strict_scrape, + mojom::DocumentExtractor* document_extractor, + PageScrapeResultCallback callback) { + const auto* url_details = + server_config_loader_->GetLastPatterns().GetMatchingURLPattern( + url, is_strict_scrape); + if (!url_details) { + return; + } + auto interim_result = + std::make_unique(url, url_details->id); + + std::vector select_requests; + for (const auto& [selector, group] : url_details->scrape_rule_groups) { + auto select_request = mojom::SelectRequest::New(); + select_request->root_selector = selector; + for (const auto& [report_key, rule] : group) { + if (rule->rule_type == ScrapeRuleType::kStandard) { + ProcessStandardRule(report_key, *rule, selector, url, + interim_result.get()); + continue; + } + auto attribute_request = mojom::SelectAttributeRequest::New(); + attribute_request->sub_selector = rule->sub_selector; + attribute_request->attribute = rule->attribute; + attribute_request->key = report_key; + + select_request->attribute_requests.push_back( + std::move(attribute_request)); + } + select_requests.push_back(std::move(select_request)); + } + + document_extractor->QueryElementAttributes( + std::move(select_requests), + base::BindOnce(&ContentScraper::OnScrapedElementAttributes, + base::Unretained(this), is_strict_scrape, + std::move(interim_result), std::move(callback))); +} + +void ContentScraper::ParseAndScrapePage( + const GURL& url, + bool is_strict_scrape, + std::unique_ptr prev_result, + std::string html, + PageScrapeResultCallback callback) { + const auto* url_details = + server_config_loader_->GetLastPatterns().GetMatchingURLPattern( + url, is_strict_scrape); + if (!url_details) { + return; + } + auto interim_result = std::move(prev_result); + + std::vector select_requests; + for (const auto& [selector, group] : url_details->scrape_rule_groups) { + rust_document_extractor::SelectRequest select_request; + select_request.root_selector = selector; + for (const auto& [report_key, rule] : group) { + if (rule->rule_type == ScrapeRuleType::kStandard) { + ProcessStandardRule(report_key, *rule, selector, url, + interim_result.get()); + continue; + } + rust_document_extractor::SelectAttributeRequest attribute_request{ + .sub_selector = rule->sub_selector.value_or(""), + .key = report_key, + .attribute = rule->attribute, + }; + select_request.attribute_requests.push_back(std::move(attribute_request)); + } + select_requests.push_back(std::move(select_request)); + } + + pool_sequenced_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&rust_document_extractor::query_element_attributes, html, + select_requests), + base::BindOnce(&ContentScraper::OnRustElementAttributes, + weak_ptr_factory_.GetWeakPtr(), is_strict_scrape, + std::move(interim_result), std::move(callback))); +} + +void ContentScraper::ProcessStandardRule(const std::string& report_key, + const ScrapeRule& rule, + const std::string& root_selector, + const GURL& url, + PageScrapeResult* scrape_result) { + std::string value; + if (rule.attribute == kUrlAttrId) { + value = url.spec(); + } else if (rule.attribute == kCountryCodeAttrId) { + value = server_config_loader_->GetLastServerConfig().location; + } + auto refined_value = ExecuteRefineFunctions(rule.functions_applied, value); + if (!refined_value) { + return; + } + auto& fields = scrape_result->fields[root_selector]; + if (fields.empty()) { + fields.emplace_back(); + } + fields[0].Set(report_key, *refined_value); +} + +void ContentScraper::OnScrapedElementAttributes( + bool is_strict_scrape, + std::unique_ptr scrape_result, + PageScrapeResultCallback callback, + std::vector attribute_results) { + const auto* url_details = + server_config_loader_->GetLastPatterns().GetMatchingURLPattern( + scrape_result->url, is_strict_scrape); + if (!url_details) { + return; + } + for (const auto& attribute_result : attribute_results) { + const auto rule_group = + url_details->scrape_rule_groups.find(attribute_result->root_selector); + if (rule_group == url_details->scrape_rule_groups.end()) { + continue; + } + base::Value::Dict attribute_values; + for (const auto& [key, value_str] : attribute_result->attribute_values) { + ProcessAttributeValue(rule_group->second, *scrape_result, key, value_str, + attribute_values); + } + scrape_result->fields[attribute_result->root_selector].push_back( + std::move(attribute_values)); + } + std::move(callback).Run(std::move(scrape_result)); +} + +void ContentScraper::OnRustElementAttributes( + bool is_strict_scrape, + std::unique_ptr scrape_result, + PageScrapeResultCallback callback, + rust::Vec attribute_results) { + const auto* url_details = + server_config_loader_->GetLastPatterns().GetMatchingURLPattern( + scrape_result->url, is_strict_scrape); + if (!url_details) { + return; + } + for (const auto& attribute_result : attribute_results) { + const auto root_selector = std::string(attribute_result.root_selector); + const auto rule_group = url_details->scrape_rule_groups.find(root_selector); + if (rule_group == url_details->scrape_rule_groups.end()) { + continue; + } + base::Value::Dict attribute_values; + for (const auto& pair : attribute_result.attribute_pairs) { + ProcessAttributeValue( + rule_group->second, *scrape_result, std::string(pair.key), + pair.value.empty() ? std::nullopt + : std::make_optional(pair.value), + attribute_values); + } + scrape_result->fields[root_selector].push_back(std::move(attribute_values)); + } + std::move(callback).Run(std::move(scrape_result)); +} + +std::optional ContentScraper::ExecuteRefineFunctions( + const RefineFunctionList& function_list, + std::string value) { + std::optional result = value; + for (const auto& function_args : function_list) { + if (function_args.empty()) { + continue; + } + const auto& func_name = function_args[0]; + if (func_name == kRefineSplitFuncId) { + if (function_args.size() >= 3 && function_args[1].is_string() && + function_args[2].is_int()) { + result = RefineSplit(*result, function_args[1].GetString(), + function_args[2].GetInt()); + } + } else if (func_name == kRefineMaskURLFuncId) { + result = MaskURL(*regex_util_, GURL(value)); + } else if (func_name == kRefineParseURLFuncId) { + if (function_args.size() >= 3 && function_args[1].is_string() && + function_args[2].is_string()) { + result = RefineParseURL(*result, function_args[1].GetString(), + function_args[2].GetString()); + } + } else if (func_name == kRefineJsonExtractFuncId) { + if (function_args.size() >= 3 && function_args[1].is_string() && + function_args[2].is_bool()) { + result = RefineJsonExtract(*result, function_args[1].GetString(), + function_args[2].GetBool()); + } + } + if (!result) { + break; + } + } + return result; +} + +void ContentScraper::ProcessAttributeValue( + const ScrapeRuleGroup& rule_group, + PageScrapeResult& scrape_result, + std::string key, + std::optional value_str, + base::Value::Dict& attribute_values) { + const auto rule = rule_group.find(key); + if (rule == rule_group.end()) { + return; + } + base::Value value; + if (value_str) { + value_str = + ExecuteRefineFunctions(rule->second->functions_applied, *value_str); + + if (value_str) { + if (rule->second->rule_type == ScrapeRuleType::kSearchQuery || + rule->second->rule_type == ScrapeRuleType::kWidgetTitle) { + scrape_result.query = value_str; + } + + value = base::Value(*value_str); + } + } + attribute_values.Set(key, std::move(value)); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/content_scraper.h b/components/web_discovery/browser/content_scraper.h new file mode 100644 index 000000000000..68d6cb60178a --- /dev/null +++ b/components/web_discovery/browser/content_scraper.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_CONTENT_SCRAPER_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_CONTENT_SCRAPER_H_ + +#include +#include +#include + +#include "base/containers/flat_map.h" +#include "base/functional/callback.h" +#include "base/values.h" +#include "brave/components/web_discovery/browser/document_extractor/rs/src/lib.rs.h" +#include "brave/components/web_discovery/browser/patterns.h" +#include "brave/components/web_discovery/browser/regex_util.h" +#include "brave/components/web_discovery/browser/server_config_loader.h" +#include "brave/components/web_discovery/common/web_discovery.mojom.h" +#include "url/gurl.h" + +namespace web_discovery { + +struct PageScrapeResult { + PageScrapeResult(GURL url, std::string id); + ~PageScrapeResult(); + + PageScrapeResult(const PageScrapeResult&) = delete; + PageScrapeResult& operator=(const PageScrapeResult&) = delete; + + base::Value SerializeToValue(); + static std::unique_ptr FromValue(const base::Value& dict); + + GURL url; + // A map of DOM selectors to list of scraped values embedded in a Dict. + // Each dict contains arbitrary keys (defined in the patterns) to scraped + // values. + base::flat_map> fields; + std::string id; + + // Only available for non-strict scrapes with "searchQuery"/"widgetTitle" + // scrape rules + std::optional query; +}; + +// Extracts attribute values from the page DOM for reporting purposes. +// ContentScraper utilizes the following techniques: +// +// a) Extraction within the current page in the renderer (via `ScrapePage`). +// The `mojom::DocumentExtractor` is used to request attribute values +// from the current DOM in the view. Typically, this is used to exact a +// search query, and decide whether the page is worthy of investigation +// and reporting. +// b) Parsing and extracting HTML from a double fetch. This follows +// the extraction in a). Used to extract all other needed details +// from the page i.e. search results. Uses a Rust library for DOM +// operations, in respect of Rule of Two. +class ContentScraper { + public: + using PageScrapeResultCallback = + base::OnceCallback)>; + + ContentScraper(const ServerConfigLoader* server_config_loader, + RegexUtil* regex_util); + ~ContentScraper(); + + ContentScraper(const ContentScraper&) = delete; + ContentScraper& operator=(const ContentScraper&) = delete; + + // For initial page scrape in renderer + void ScrapePage(const GURL& url, + bool is_strict_scrape, + mojom::DocumentExtractor* document_extractor, + PageScrapeResultCallback callback); + // For subsequent double fetches after initial scrape + void ParseAndScrapePage(const GURL& url, + bool is_strict_scrape, + std::unique_ptr prev_result, + std::string html, + PageScrapeResultCallback callback); + + private: + void ProcessStandardRule(const std::string& report_key, + const ScrapeRule& rule, + const std::string& root_selector, + const GURL& url, + PageScrapeResult* scrape_result); + void OnScrapedElementAttributes( + bool is_strict_scrape, + std::unique_ptr scrape_result, + PageScrapeResultCallback callback, + std::vector attribute_results); + void OnRustElementAttributes( + bool is_strict_scrape, + std::unique_ptr scrape_result, + PageScrapeResultCallback callback, + rust::Vec attribute_results); + + std::optional ExecuteRefineFunctions( + const RefineFunctionList& function_list, + std::string value); + void ProcessAttributeValue(const ScrapeRuleGroup& rule_group, + PageScrapeResult& scrape_result, + std::string key, + std::optional value_str, + base::Value::Dict& attribute_values); + + scoped_refptr pool_sequenced_task_runner_; + + raw_ptr server_config_loader_; + raw_ptr regex_util_; + + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_CONTENT_SCRAPER_H_ diff --git a/components/web_discovery/browser/content_scraper_browsertest.cc b/components/web_discovery/browser/content_scraper_browsertest.cc new file mode 100644 index 000000000000..2597918e8f9b --- /dev/null +++ b/components/web_discovery/browser/content_scraper_browsertest.cc @@ -0,0 +1,283 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/content_scraper.h" + +#include +#include + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "base/test/bind.h" +#include "base/test/scoped_feature_list.h" +#include "brave/components/constants/brave_paths.h" +#include "brave/components/web_discovery/browser/patterns.h" +#include "brave/components/web_discovery/browser/server_config_loader.h" +#include "brave/components/web_discovery/common/features.h" +#include "brave/components/web_discovery/common/web_discovery.mojom.h" +#include "chrome/test/base/chrome_test_utils.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_base.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_mock_cert_verifier.h" +#include "net/dns/mock_host_resolver.h" +#include "services/service_manager/public/cpp/interface_provider.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/re2/src/re2/re2.h" + +#if BUILDFLAG(IS_ANDROID) +#include "chrome/test/base/android/android_browser_test.h" +#else +#include "chrome/test/base/in_process_browser_test.h" +#endif + +namespace web_discovery { + +class WebDiscoveryContentScraperTest : public PlatformBrowserTest { + public: + WebDiscoveryContentScraperTest() + : scoped_features_(features::kWebDiscoveryNative) {} + + // PlatformBrowserTest: + void SetUpOnMainThread() override { + PlatformBrowserTest::SetUpOnMainThread(); + base::FilePath data_path = + base::PathService::CheckedGet(brave::DIR_TEST_DATA); + data_path = data_path.AppendASCII("web_discovery"); + + host_resolver()->AddRule("*", "127.0.0.1"); + test_server_.ServeFilesFromDirectory(data_path); + mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); + ASSERT_TRUE(test_server_.Start()); + + InitScraper(); + run_loop_ = std::make_unique(); + + ASSERT_TRUE(base::ReadFileToString(data_path.AppendASCII("page.html"), + &page_content_)); + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + PlatformBrowserTest::SetUpCommandLine(command_line); + mock_cert_verifier_.SetUpCommandLine(command_line); + } + + void SetUpInProcessBrowserTestFixture() override { + PlatformBrowserTest::SetUpInProcessBrowserTestFixture(); + mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); + } + + void TearDownInProcessBrowserTestFixture() override { + mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); + PlatformBrowserTest::TearDownInProcessBrowserTestFixture(); + } + + protected: + mojo::Remote LoadTestPageAndGetExtractor() { + mojo::Remote remote; + + auto url = test_server_.GetURL("example.com", "/page.html"); + auto* contents = chrome_test_utils::GetActiveWebContents(this); + EXPECT_TRUE(content::NavigateToURL(contents, url)); + auto* render_frame_host = contents->GetPrimaryMainFrame(); + + if (render_frame_host) { + render_frame_host->GetRemoteInterfaces()->GetInterface( + remote.BindNewPipeAndPassReceiver()); + } + return remote; + } + + std::string page_content_; + std::unique_ptr scraper_; + std::unique_ptr run_loop_; + + private: + void InitScraper() { + server_config_loader_ = std::make_unique( + nullptr, base::FilePath(), nullptr, base::DoNothing(), + base::DoNothing()); + auto server_config = std::make_unique(); + server_config->location = "us"; + server_config_loader_->SetLastServerConfigForTesting( + std::move(server_config)); + + auto patterns_group = std::make_unique(); + std::vector normal_patterns(1); + std::vector strict_patterns(1); + normal_patterns[0].url_regex = + std::make_unique("^https:\\/\\/example\\.com"); + normal_patterns[0].id = "ex1"; + strict_patterns[0].url_regex = + std::make_unique("^https:\\/\\/example\\.com"); + strict_patterns[0].id = "ex1"; + + auto* normal_rule_group = + &normal_patterns[0].scrape_rule_groups["#b .result1"]; + auto scrape_rule1 = std::make_unique(); + scrape_rule1->sub_selector = "a"; + scrape_rule1->rule_type = ScrapeRuleType::kOther; + scrape_rule1->attribute = "href"; + normal_rule_group->insert_or_assign("href", std::move(scrape_rule1)); + auto scrape_rule2 = std::make_unique(); + scrape_rule2->sub_selector = "a"; + scrape_rule2->rule_type = ScrapeRuleType::kOther; + scrape_rule2->attribute = "textContent"; + normal_rule_group->insert_or_assign("text", std::move(scrape_rule2)); + auto scrape_rule3 = std::make_unique(); + scrape_rule3->sub_selector = "#query"; + scrape_rule3->rule_type = ScrapeRuleType::kSearchQuery; + scrape_rule3->attribute = "textContent"; + normal_rule_group->insert_or_assign("q", std::move(scrape_rule3)); + normal_rule_group = &normal_patterns[0].scrape_rule_groups["dont>match"]; + auto scrape_rule4 = std::make_unique(); + scrape_rule4->rule_type = ScrapeRuleType::kStandard; + scrape_rule4->attribute = "url"; + scrape_rule4->functions_applied.emplace_back(); + scrape_rule4->functions_applied[0].push_back(base::Value("parseU")); + scrape_rule4->functions_applied[0].push_back(base::Value("qs")); + scrape_rule4->functions_applied[0].push_back(base::Value("q")); + + normal_rule_group->insert_or_assign("q2", std::move(scrape_rule4)); + + patterns_group->normal_patterns = std::move(normal_patterns); + + auto* strict_rule_group = + &strict_patterns[0].scrape_rule_groups["#b #result2"]; + scrape_rule1 = std::make_unique(); + scrape_rule1->sub_selector = "a"; + scrape_rule1->rule_type = ScrapeRuleType::kOther; + scrape_rule1->attribute = "textContent"; + strict_rule_group->insert_or_assign("text", std::move(scrape_rule1)); + scrape_rule2 = std::make_unique(); + scrape_rule2->sub_selector = "#input1"; + scrape_rule2->rule_type = ScrapeRuleType::kOther; + scrape_rule2->attribute = "value"; + strict_rule_group->insert_or_assign("input", std::move(scrape_rule2)); + strict_rule_group = &strict_patterns[0].scrape_rule_groups["dont>match"]; + scrape_rule3 = std::make_unique(); + scrape_rule3->rule_type = ScrapeRuleType::kStandard; + scrape_rule3->attribute = "ctry"; + strict_rule_group->insert_or_assign("ctry", std::move(scrape_rule3)); + + patterns_group->strict_patterns = std::move(strict_patterns); + + server_config_loader_->SetLastPatternsForTesting(std::move(patterns_group)); + + scraper_ = std::make_unique(server_config_loader_.get(), + ®ex_util_); + } + + content::ContentMockCertVerifier mock_cert_verifier_; + net::EmbeddedTestServer test_server_{net::EmbeddedTestServer::TYPE_HTTPS}; + base::test::ScopedFeatureList scoped_features_; + RegexUtil regex_util_; + std::unique_ptr server_config_loader_; +}; + +IN_PROC_BROWSER_TEST_F(WebDiscoveryContentScraperTest, RendererScrape) { + auto extractor = LoadTestPageAndGetExtractor(); + ASSERT_TRUE(extractor.is_bound() && extractor.is_connected()); + + GURL url("https://example.com/page?q=testquery"); + scraper_->ScrapePage( + url, false, extractor.get(), + base::BindLambdaForTesting( + [&](std::unique_ptr scrape_result) { + [&] { + ASSERT_TRUE(scrape_result); + EXPECT_EQ(scrape_result->url, url); + EXPECT_EQ(scrape_result->fields.size(), 2u); + EXPECT_EQ(scrape_result->id, "ex1"); + + EXPECT_EQ(scrape_result->query, "A query"); + + auto field_map_it = scrape_result->fields.find("#b .result1"); + ASSERT_TRUE(field_map_it != scrape_result->fields.end()); + const auto* fields = &field_map_it->second; + + ASSERT_EQ(fields->size(), 2u); + + const auto* href_value = (*fields)[0].FindString("href"); + const auto* text_value = (*fields)[0].FindString("text"); + const auto* query_value_str = (*fields)[0].FindString("q"); + ASSERT_TRUE(href_value); + ASSERT_TRUE(text_value); + ASSERT_TRUE(query_value_str); + EXPECT_EQ(*href_value, "https://example.com/foo1"); + EXPECT_EQ(*text_value, "Foo1"); + EXPECT_EQ(*query_value_str, "A query"); + + href_value = (*fields)[1].FindString("href"); + text_value = (*fields)[1].FindString("text"); + const auto* query_value = (*fields)[1].Find("q"); + ASSERT_TRUE(href_value); + ASSERT_TRUE(text_value); + ASSERT_TRUE(query_value); + EXPECT_EQ(*href_value, "https://example.com/foo2"); + EXPECT_EQ(*text_value, "Foo2"); + EXPECT_TRUE(query_value->is_none()); + + field_map_it = scrape_result->fields.find("dont>match"); + ASSERT_TRUE(field_map_it != scrape_result->fields.end()); + fields = &field_map_it->second; + + ASSERT_EQ(fields->size(), 1u); + const auto* url_query_value = (*fields)[0].FindString("q2"); + ASSERT_TRUE(url_query_value); + EXPECT_EQ(*url_query_value, "testquery"); + }(); + run_loop_->Quit(); + })); + run_loop_->Run(); +} + +IN_PROC_BROWSER_TEST_F(WebDiscoveryContentScraperTest, RustParseAndScrape) { + GURL url("https://example.com/page.html"); + + auto prev_scrape_result = std::make_unique(url, "ex1"); + scraper_->ParseAndScrapePage( + url, true, std::move(prev_scrape_result), page_content_, + base::BindLambdaForTesting( + [&](std::unique_ptr scrape_result) { + [&] { + ASSERT_TRUE(scrape_result); + EXPECT_EQ(scrape_result->url, url); + EXPECT_EQ(scrape_result->fields.size(), 2u); + EXPECT_EQ(scrape_result->id, "ex1"); + + EXPECT_FALSE(scrape_result->query); + + auto field_map_it = scrape_result->fields.find("#b #result2"); + ASSERT_TRUE(field_map_it != scrape_result->fields.end()); + const auto* fields = &field_map_it->second; + + ASSERT_EQ(fields->size(), 1u); + + const auto* text_value = (*fields)[0].FindString("text"); + const auto* input_value = (*fields)[0].FindString("input"); + ASSERT_TRUE(text_value); + ASSERT_TRUE(input_value); + EXPECT_EQ(*text_value, "Foo3"); + EXPECT_EQ(*input_value, "Foo4"); + + field_map_it = scrape_result->fields.find("dont>match"); + ASSERT_TRUE(field_map_it != scrape_result->fields.end()); + fields = &field_map_it->second; + + ASSERT_EQ(fields->size(), 1u); + + const auto* ctry_value = (*fields)[0].FindString("ctry"); + ASSERT_TRUE(ctry_value); + EXPECT_EQ(*ctry_value, "us"); + }(); + run_loop_->Quit(); + })); + run_loop_->Run(); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/credential_manager.cc b/components/web_discovery/browser/credential_manager.cc new file mode 100644 index 000000000000..e4877039237c --- /dev/null +++ b/components/web_discovery/browser/credential_manager.cc @@ -0,0 +1,461 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/credential_manager.h" + +#include + +#include "base/base64.h" +#include "base/functional/bind.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/task/sequenced_task_runner.h" +#include "base/task/thread_pool.h" +#include "brave/components/web_discovery/browser/anonymous_credentials/rs/cxx/src/lib.rs.h" +#include "brave/components/web_discovery/browser/pref_names.h" +#include "brave/components/web_discovery/browser/util.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "crypto/sha2.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/public/mojom/url_response_head.mojom.h" + +namespace web_discovery { + +namespace { + +constexpr char kJoinPath[] = "/join"; +constexpr char kJoinContentType[] = "application/json"; + +constexpr char kJoinDateField[] = "ts"; +constexpr char kJoinMessageField[] = "joinMsg"; +constexpr char kJoinRSAPublicKeyField[] = "pk"; +constexpr char kJoinRSASignatureField[] = "sig"; +constexpr char kJoinResponseField[] = "joinResponse"; + +constexpr char kGSKDictKey[] = "gsk"; +constexpr char kCredentialDictKey[] = "credential"; + +constexpr net::NetworkTrafficAnnotationTag kJoinNetworkTrafficAnnotation = + net::DefineNetworkTrafficAnnotation("wdp_join", R"( + semantics { + sender: "Brave Web Discovery HPNv2 Join" + description: + "Retrieves anonymous credentials in order to sign Web Discovery + measurements sent via the HumanWeb Proxy Network." + trigger: + "Requests are automatically sent on daily intervals " + "while Brave is running." + data: "Configuration attributes" + destination: WEBSITE + } + policy { + cookies_allowed: NO + setting: + "Users can opt-in or out via brave://settings/search" + })"); + +std::optional GenerateJoinRequest( + anonymous_credentials::CredentialManager* anonymous_credential_manager, + EVPKeyPtr* rsa_private_key, + std::string pre_challenge) { + base::span pre_challenge_span( + reinterpret_cast(pre_challenge.data()), pre_challenge.size()); + auto challenge = crypto::SHA256Hash(pre_challenge_span); + + auto join_request = anonymous_credential_manager->start_join( + rust::Slice(challenge.data(), challenge.size())); + + auto signature = RSASign(*rsa_private_key, join_request.join_request); + + if (!signature) { + VLOG(1) << "RSA signature failed"; + return std::nullopt; + } + + return GenerateJoinRequestResult{.start_join_result = join_request, + .signature = *signature}; +} + +std::optional FinishJoin( + anonymous_credentials::CredentialManager* anonymous_credential_manager, + std::string date, + std::vector group_pub_key, + std::vector gsk, + std::vector join_resp_bytes) { + auto pub_key_result = anonymous_credentials::load_group_public_key( + rust::Slice(group_pub_key.data(), group_pub_key.size())); + auto gsk_result = anonymous_credentials::load_credential_big( + rust::Slice(gsk.data(), gsk.size())); + auto join_resp_result = anonymous_credentials::load_join_response( + rust::Slice(join_resp_bytes.data(), join_resp_bytes.size())); + if (!pub_key_result.error_message.empty() || + !gsk_result.error_message.empty() || + !join_resp_result.error_message.empty()) { + VLOG(1) << "Failed to finish credential join due to deserialization error " + "with group pub key, gsk, or join response: " + << pub_key_result.error_message.c_str() + << gsk_result.error_message.c_str() + << join_resp_result.error_message.c_str(); + return std::nullopt; + } + auto finish_res = anonymous_credential_manager->finish_join( + *pub_key_result.value, *gsk_result.value, + std::move(join_resp_result.value)); + if (!finish_res.error_message.empty()) { + VLOG(1) << "Failed to finish credential join for " << date << ": " + << finish_res.error_message.c_str(); + return std::nullopt; + } + return base::Base64Encode(finish_res.data); +} + +std::optional> PerformSign( + anonymous_credentials::CredentialManager* anonymous_credential_manager, + std::vector msg, + std::vector basename, + std::optional> gsk_bytes, + std::optional> credential_bytes) { + if (gsk_bytes && credential_bytes) { + auto gsk_result = anonymous_credentials::load_credential_big( + rust::Slice(reinterpret_cast(gsk_bytes->data()), + gsk_bytes->size())); + auto credential_result = anonymous_credentials::load_user_credentials( + rust::Slice(reinterpret_cast(credential_bytes->data()), + credential_bytes->size())); + if (!gsk_result.error_message.empty() || + !credential_result.error_message.empty()) { + VLOG(1) << "Failed to sign due to deserialization error with gsk, or " + "user credential: " + << gsk_result.error_message.c_str() + << credential_result.error_message.c_str(); + return std::nullopt; + } + anonymous_credential_manager->set_gsk_and_credentials( + std::move(gsk_result.value), std::move(credential_result.value)); + } + auto sig_res = anonymous_credential_manager->sign( + rust::Slice(msg.data(), msg.size()), + rust::Slice(basename.data(), basename.size())); + if (!sig_res.error_message.empty()) { + VLOG(1) << "Failed to sign: " << sig_res.error_message.c_str(); + return std::nullopt; + } + return std::vector(sig_res.data.begin(), sig_res.data.end()); +} + +} // namespace + +CredentialManager::CredentialManager( + PrefService* profile_prefs, + network::SharedURLLoaderFactory* shared_url_loader_factory, + const ServerConfigLoader* server_config_loader) + : profile_prefs_(profile_prefs), + shared_url_loader_factory_(shared_url_loader_factory), + server_config_loader_(server_config_loader), + join_url_(GetDirectHPNHost() + kJoinPath), + backoff_entry_(&kBackoffPolicy), + pool_sequenced_task_runner_( + base::ThreadPool::CreateSequencedTaskRunner({})), + anonymous_credential_manager_( + new rust::Box(anonymous_credentials::new_credential_manager()), + base::OnTaskRunnerDeleter(pool_sequenced_task_runner_)), + rsa_private_key_(new EVPKeyPtr(), + base::OnTaskRunnerDeleter(pool_sequenced_task_runner_)) { +} + +CredentialManager::~CredentialManager() = default; + +bool CredentialManager::LoadRSAKey() { + std::string private_key_b64 = + profile_prefs_->GetString(kCredentialRSAPrivateKey); + rsa_public_key_b64_ = profile_prefs_->GetString(kCredentialRSAPublicKey); + + if (private_key_b64.empty() || rsa_public_key_b64_->empty()) { + rsa_public_key_b64_ = std::nullopt; + return true; + } + + *rsa_private_key_ = ImportRSAKeyPair(private_key_b64); + + if (!rsa_private_key_) { + VLOG(1) << "Failed to decode stored RSA key"; + return false; + } + + return true; +} + +void CredentialManager::OnNewRSAKey(std::unique_ptr key_info) { + if (!key_info) { + VLOG(1) << "RSA key generation failed"; + return; + } + + *rsa_private_key_ = std::move(key_info->key_pair); + rsa_public_key_b64_ = key_info->public_key_b64; + + profile_prefs_->SetString(kCredentialRSAPrivateKey, + key_info->private_key_b64); + profile_prefs_->SetString(kCredentialRSAPublicKey, *rsa_public_key_b64_); + + JoinGroups(); +} + +void CredentialManager::JoinGroups() { + const auto& server_config = server_config_loader_->GetLastServerConfig(); + auto today_date = FormatServerDate(base::Time::Now().UTCMidnight()); + const auto& anon_creds_dict = + profile_prefs_->GetDict(kAnonymousCredentialsDict); + for (const auto& [date, group_pub_key_b64] : server_config.group_pub_keys) { + if (date < today_date || join_url_loaders_.contains(date) || + anon_creds_dict.contains(date)) { + continue; + } + + if (*rsa_private_key_ == nullptr) { + if (!LoadRSAKey()) { + return; + } + if (*rsa_private_key_ == nullptr) { + pool_sequenced_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce(&GenerateRSAKeyPair), + base::BindOnce(&CredentialManager::OnNewRSAKey, + weak_ptr_factory_.GetWeakPtr())); + return; + } + } + + StartJoinGroup(date, group_pub_key_b64); + } +} + +void CredentialManager::StartJoinGroup(const std::string& date, + const std::string& group_pub_key_b64) { + auto group_pub_key = base::Base64Decode(group_pub_key_b64); + if (!group_pub_key) { + VLOG(1) << "Failed to decode group public key for " << date; + return; + } + std::vector group_pub_key_const(group_pub_key->begin(), + group_pub_key->end()); + + auto challenge_elements = base::Value::List::with_capacity(2); + challenge_elements.Append(*rsa_public_key_b64_); + challenge_elements.Append(group_pub_key_b64); + + std::string pre_challenge; + base::JSONWriter::Write(challenge_elements, &pre_challenge); + + pool_sequenced_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&GenerateJoinRequest, &**anonymous_credential_manager_, + rsa_private_key_.get(), pre_challenge), + base::BindOnce(&CredentialManager::OnJoinRequestReady, + weak_ptr_factory_.GetWeakPtr(), date, + group_pub_key_const)); +} + +void CredentialManager::OnJoinRequestReady( + std::string date, + std::vector group_pub_key, + std::optional generate_join_result) { + if (!generate_join_result) { + return; + } + base::Value::Dict body_fields; + + body_fields.Set(kJoinDateField, date); + body_fields.Set( + kJoinMessageField, + base::Base64Encode(generate_join_result->start_join_result.join_request)); + body_fields.Set(kJoinRSAPublicKeyField, *rsa_public_key_b64_); + body_fields.Set(kJoinRSASignatureField, generate_join_result->signature); + + std::string json_body; + if (!base::JSONWriter::Write(body_fields, &json_body)) { + VLOG(1) << "Join body serialization failed"; + return; + } + + auto gsk = std::vector( + generate_join_result->start_join_result.gsk.begin(), + generate_join_result->start_join_result.gsk.end()); + + auto resource_request = CreateResourceRequest(join_url_); + resource_request->headers.SetHeader(kVersionHeader, + base::NumberToString(kCurrentVersion)); + resource_request->method = net::HttpRequestHeaders::kPostMethod; + + join_url_loaders_[date] = network::SimpleURLLoader::Create( + std::move(resource_request), kJoinNetworkTrafficAnnotation); + auto& url_loader = join_url_loaders_[date]; + + url_loader->AttachStringForUpload(json_body, kJoinContentType); + + url_loader->DownloadToString( + shared_url_loader_factory_.get(), + base::BindOnce(&CredentialManager::OnJoinResponse, base::Unretained(this), + date, group_pub_key, gsk), + kMaxResponseSize); +} + +void CredentialManager::OnJoinResponse( + std::string date, + std::vector group_pub_key, + std::vector gsk, + std::optional response_body) { + bool result = ProcessJoinResponse(date, group_pub_key, gsk, response_body); + if (!result) { + HandleJoinResponseStatus(date, result); + } +} + +void CredentialManager::HandleJoinResponseStatus(const std::string& date, + bool result) { + join_url_loaders_.erase(date); + // TODO(djandries): what if the last request succeeds and the other requests + // fail? fix + if (join_url_loaders_.empty()) { + backoff_entry_.InformOfRequest(result); + + if (!result) { + retry_timer_.Start( + FROM_HERE, base::Time::Now() + backoff_entry_.GetTimeUntilRelease(), + base::BindOnce(&CredentialManager::JoinGroups, + base::Unretained(this))); + } + } +} + +bool CredentialManager::ProcessJoinResponse( + const std::string& date, + const std::vector& group_pub_key, + const std::vector& gsk, + const std::optional& response_body) { + CHECK(join_url_loaders_[date]); + auto& url_loader = join_url_loaders_[date]; + auto* response_info = url_loader->ResponseInfo(); + if (!response_body || !response_info || + response_info->headers->response_code() != 200) { + VLOG(1) << "Failed to fetch credentials for " << date; + return false; + } + + auto parsed_json = base::JSONReader::ReadAndReturnValueWithError( + *response_body, base::JSON_PARSE_RFC); + + if (!parsed_json.has_value()) { + VLOG(1) << "Failed to parse join response json"; + return false; + } + + const auto* root = parsed_json.value().GetIfDict(); + if (!root) { + VLOG(1) << "Failed to parse join response json: not a dict"; + return false; + } + + const auto* join_resp = root->FindString(kJoinResponseField); + if (!join_resp) { + VLOG(1) << "Failed to find content in join response json"; + return false; + } + + auto join_resp_bytes = base::Base64Decode(*join_resp); + if (!join_resp_bytes) { + VLOG(1) << "Failed to decode join response base64"; + return false; + } + std::vector join_resp_bytes_const(join_resp_bytes->begin(), + join_resp_bytes->end()); + + pool_sequenced_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&FinishJoin, &**anonymous_credential_manager_, date, + group_pub_key, gsk, join_resp_bytes_const), + base::BindOnce(&CredentialManager::OnCredentialsReady, + weak_ptr_factory_.GetWeakPtr(), date, gsk)); + return true; +} + +void CredentialManager::OnCredentialsReady( + std::string date, + std::vector gsk, + std::optional credentials) { + if (!credentials) { + HandleJoinResponseStatus(date, false); + return; + } + ScopedDictPrefUpdate update(profile_prefs_, kAnonymousCredentialsDict); + auto* date_dict = update->EnsureDict(date); + date_dict->Set(kGSKDictKey, base::Base64Encode(gsk)); + date_dict->Set(kCredentialDictKey, *credentials); + HandleJoinResponseStatus(date, true); +} + +bool CredentialManager::CredentialExistsForToday() { + return profile_prefs_->GetDict(kAnonymousCredentialsDict) + .contains(FormatServerDate(base::Time::Now())); +} + +bool CredentialManager::Sign(std::vector msg, + std::vector basename, + SignCallback callback) { + auto today_date = FormatServerDate(base::Time::Now().UTCMidnight()); + const auto& anon_creds_dict = + profile_prefs_->GetDict(kAnonymousCredentialsDict); + std::optional> gsk_bytes; + std::optional> credential_bytes; + if (!loaded_credential_date_ || loaded_credential_date_ != today_date) { + auto* today_cred_dict = anon_creds_dict.FindDict(today_date); + if (!today_cred_dict) { + VLOG(1) << "Failed to sign due to unavailability of credentials"; + return false; + } + auto* gsk_b64 = today_cred_dict->FindString(kGSKDictKey); + auto* credential_b64 = today_cred_dict->FindString(kCredentialDictKey); + if (!gsk_b64 || !credential_b64) { + VLOG(1) << "Failed to sign due to unavailability of gsk/credential"; + return false; + } + gsk_bytes = base::Base64Decode(*gsk_b64); + credential_bytes = base::Base64Decode(*credential_b64); + if (!gsk_bytes || !credential_bytes) { + VLOG(1) << "Failed to sign due to bad gsk/credential base64"; + return false; + } + } + + pool_sequenced_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&PerformSign, &**anonymous_credential_manager_, msg, + basename, gsk_bytes, credential_bytes), + base::BindOnce(&CredentialManager::OnSignResult, + weak_ptr_factory_.GetWeakPtr(), today_date, + std::move(callback))); + return true; +} + +void CredentialManager::OnSignResult( + std::string credential_date, + SignCallback callback, + std::optional> signed_message) { + loaded_credential_date_ = credential_date; + std::move(callback).Run(signed_message); +} + +void CredentialManager::UseFixedSeedForTesting() { + anonymous_credential_manager_ = + std::unique_ptr, + base::OnTaskRunnerDeleter>( + new rust::Box( + anonymous_credentials::new_credential_manager_with_fixed_seed()), + base::OnTaskRunnerDeleter(pool_sequenced_task_runner_)); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/credential_manager.h b/components/web_discovery/browser/credential_manager.h new file mode 100644 index 000000000000..21d9ceb35258 --- /dev/null +++ b/components/web_discovery/browser/credential_manager.h @@ -0,0 +1,130 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_CREDENTIAL_MANAGER_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_CREDENTIAL_MANAGER_H_ + +#include +#include +#include +#include + +#include "base/functional/callback.h" +#include "base/memory/raw_ptr.h" +#include "base/task/sequenced_task_runner.h" +#include "base/timer/wall_clock_timer.h" +#include "brave/components/web_discovery/browser/anonymous_credentials/rs/cxx/src/lib.rs.h" +#include "brave/components/web_discovery/browser/credential_signer.h" +#include "brave/components/web_discovery/browser/rsa.h" +#include "brave/components/web_discovery/browser/server_config_loader.h" +#include "net/base/backoff_entry.h" + +class PrefService; + +namespace network { +class SharedURLLoaderFactory; +class SimpleURLLoader; +} // namespace network + +namespace web_discovery { + +struct GenerateJoinRequestResult { + anonymous_credentials::StartJoinResult start_join_result; + std::string signature; +}; + +// Manages and utilizes anonymous credentials used for communicating +// with Web Discovery servers. These Direct Anonymous Attestation credentials +// are used to prevent Sybil attacks on the servers. +// The manager provides two key functions: +// +// a) "joining": acquires credentials from the Web Discovery server. Join +// requests +// are signed with a random RSA key that is persisted with the profile. +// b) "signing": uses the previously acquired credentials to sign submissions +// which is required in order for the servers to accept the request. +class CredentialManager : public CredentialSigner { + public: + CredentialManager(PrefService* profile_prefs, + network::SharedURLLoaderFactory* shared_url_loader_factory, + const ServerConfigLoader* server_config_loader); + ~CredentialManager() override; + + CredentialManager(const CredentialManager&) = delete; + CredentialManager& operator=(const CredentialManager&) = delete; + + // Acquires credentials for all dates/"group public keys" published in + // the server config, if not stored already. + void JoinGroups(); + + // CredentialSigner: + bool CredentialExistsForToday() override; + + bool Sign(std::vector msg, + std::vector basename, + SignCallback callback) override; + + // Uses a fixed seed in the anonymous credential manager + // to provide deterministic credentials & signatures which + // are useful for testing. + void UseFixedSeedForTesting(); + + private: + bool LoadRSAKey(); + bool GenerateRSAKey(); + void OnNewRSAKey(std::unique_ptr key_info); + + void StartJoinGroup(const std::string& date, + const std::string& group_pub_key_b64); + + void OnJoinRequestReady( + std::string date, + std::vector group_pub_key, + std::optional generate_join_result); + + void OnJoinResponse(std::string date, + std::vector group_pub_key, + std::vector gsk, + std::optional response_body); + void HandleJoinResponseStatus(const std::string& date, bool result); + bool ProcessJoinResponse(const std::string& date, + const std::vector& group_pub_key, + const std::vector& gsk, + const std::optional& response_body); + void OnCredentialsReady(std::string date, + std::vector gsk, + std::optional credentials); + + void OnSignResult(std::string credential_date, + SignCallback callback, + std::optional> signed_message); + + raw_ptr profile_prefs_; + raw_ptr shared_url_loader_factory_; + raw_ptr server_config_loader_; + + GURL join_url_; + base::flat_map> + join_url_loaders_; + net::BackoffEntry backoff_entry_; + base::WallClockTimer retry_timer_; + + scoped_refptr pool_sequenced_task_runner_; + + std::unique_ptr, + base::OnTaskRunnerDeleter> + anonymous_credential_manager_; + + std::unique_ptr rsa_private_key_; + std::optional rsa_public_key_b64_; + + std::optional loaded_credential_date_; + + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_CREDENTIAL_MANAGER_H_ diff --git a/components/web_discovery/browser/credential_manager_unittest.cc b/components/web_discovery/browser/credential_manager_unittest.cc new file mode 100644 index 000000000000..81ad1c85e34a --- /dev/null +++ b/components/web_discovery/browser/credential_manager_unittest.cc @@ -0,0 +1,196 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/credential_manager.h" + +#include +#include +#include + +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/path_service.h" +#include "base/test/bind.h" +#include "base/test/task_environment.h" +#include "brave/components/constants/brave_paths.h" +#include "brave/components/web_discovery/browser/pref_names.h" +#include "brave/components/web_discovery/browser/util.h" +#include "brave/components/web_discovery/browser/wdp_service.h" +#include "components/prefs/testing_pref_service.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace web_discovery { + +class WebDiscoveryCredentialManagerTest : public testing::Test { + public: + WebDiscoveryCredentialManagerTest() + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), + shared_url_loader_factory_( + base::MakeRefCounted( + &url_loader_factory_)) {} + ~WebDiscoveryCredentialManagerTest() override = default; + + // testing::Test: + void SetUp() override { + base::Time set_time; + ASSERT_TRUE(base::Time::FromUTCString("2024-06-22", &set_time)); + task_environment_.AdvanceClock(set_time - base::Time::Now()); + + WDPService::RegisterProfilePrefs(profile_prefs_.registry()); + + base::FilePath data_path = + base::PathService::CheckedGet(brave::DIR_TEST_DATA); + std::string test_data_json; + ASSERT_TRUE(base::ReadFileToString( + data_path.AppendASCII( + "web_discovery/credential_keys_and_responses.json"), + &test_data_json)); + auto test_data_value = base::JSONReader::Read(test_data_json); + ASSERT_TRUE(test_data_value); + const auto& test_data_dict = test_data_value->GetDict(); + const auto* rsa_priv_key = test_data_dict.FindString("rsa_priv_key"); + const auto* rsa_pub_key = test_data_dict.FindString("rsa_pub_key"); + const auto* group_pub_key = test_data_dict.FindString("group_pub_key"); + const auto* join_responses = test_data_dict.FindDict("join_responses"); + ASSERT_TRUE(rsa_priv_key && rsa_pub_key && group_pub_key && join_responses); + + profile_prefs_.SetString(kCredentialRSAPublicKey, *rsa_pub_key); + profile_prefs_.SetString(kCredentialRSAPrivateKey, *rsa_priv_key); + + server_config_loader_ = std::make_unique( + nullptr, base::FilePath(), nullptr, base::DoNothing(), + base::DoNothing()); + + auto server_config = std::make_unique(); + for (const auto [date, join_response] : *join_responses) { + server_config->group_pub_keys[date] = *group_pub_key; + join_responses_[date] = join_response.GetString(); + } + server_config_loader_->SetLastServerConfigForTesting( + std::move(server_config)); + + url_loader_factory_.SetInterceptor( + base::BindRepeating(&WebDiscoveryCredentialManagerTest::HandleRequest, + base::Unretained(this))); + + SetUpCredentialManager(); + } + + protected: + void SetUpCredentialManager() { + credential_manager_ = std::make_unique( + &profile_prefs_, shared_url_loader_factory_.get(), + server_config_loader_.get()); + credential_manager_->UseFixedSeedForTesting(); + } + + base::test::TaskEnvironment task_environment_; + std::unique_ptr credential_manager_; + TestingPrefServiceSimple profile_prefs_; + size_t join_requests_made_ = 0; + + private: + void HandleRequest(const network::ResourceRequest& request) { + url_loader_factory_.ClearResponses(); + std::string response; + const auto* elements = request.request_body->elements(); + ASSERT_EQ(elements->size(), 1u); + ASSERT_EQ(elements->at(0).type(), network::DataElement::Tag::kBytes); + auto body_json = + elements->at(0).As().AsStringPiece(); + + auto body_value = base::JSONReader::Read(body_json); + ASSERT_TRUE(body_value && body_value->is_dict()); + const auto* ts = body_value->GetDict().FindString("ts"); + ASSERT_TRUE(ts); + ASSERT_TRUE(join_responses_.contains(*ts)); + ASSERT_EQ(request.url.spec(), GetDirectHPNHost() + "/join"); + + base::Value::Dict dict; + dict.Set("joinResponse", join_responses_.at(*ts)); + ASSERT_TRUE( + base::JSONWriter::Write(base::Value(std::move(dict)), &response)); + url_loader_factory_.AddResponse(request.url.spec(), response); + join_requests_made_++; + } + + base::flat_map join_responses_; + std::unique_ptr server_config_loader_; + network::TestURLLoaderFactory url_loader_factory_; + scoped_refptr shared_url_loader_factory_; +}; + +TEST_F(WebDiscoveryCredentialManagerTest, JoinGroups) { + credential_manager_->JoinGroups(); + task_environment_.RunUntilIdle(); + + EXPECT_EQ(join_requests_made_, 3u); + join_requests_made_ = 0; + + for (size_t i = 0; i < 3; i++) { + EXPECT_TRUE(credential_manager_->CredentialExistsForToday()); + task_environment_.FastForwardBy(base::Days(1)); + } + EXPECT_FALSE(credential_manager_->CredentialExistsForToday()); + EXPECT_EQ(join_requests_made_, 0u); +} + +TEST_F(WebDiscoveryCredentialManagerTest, LoadKeysFromStorage) { + credential_manager_->JoinGroups(); + task_environment_.RunUntilIdle(); + + EXPECT_EQ(join_requests_made_, 3u); + join_requests_made_ = 0; + + SetUpCredentialManager(); + for (size_t i = 0; i < 3; i++) { + ASSERT_TRUE(credential_manager_->CredentialExistsForToday()); + task_environment_.FastForwardBy(base::Days(1)); + } + ASSERT_FALSE(credential_manager_->CredentialExistsForToday()); + EXPECT_EQ(join_requests_made_, 0u); +} + +TEST_F(WebDiscoveryCredentialManagerTest, Sign) { + std::vector message({0, 1, 2, 3, 4}); + std::vector basename({5, 6, 7, 8, 9}); + credential_manager_->Sign( + message, basename, + base::BindLambdaForTesting( + [&](const std::optional> signature) { + EXPECT_FALSE(signature); + })); + task_environment_.RunUntilIdle(); + + credential_manager_->JoinGroups(); + task_environment_.RunUntilIdle(); + + base::flat_set> signatures; + for (size_t i = 0; i < 3; i++) { + credential_manager_->Sign( + message, basename, + base::BindLambdaForTesting( + [&](const std::optional> signature) { + ASSERT_TRUE(signature); + EXPECT_FALSE(signature->empty()); + EXPECT_FALSE(signatures.contains(*signature)); + signatures.insert(*signature); + })); + task_environment_.FastForwardBy(base::Days(1)); + } + credential_manager_->Sign( + message, basename, + base::BindLambdaForTesting( + [&](const std::optional> signature) { + EXPECT_FALSE(signature); + })); + task_environment_.RunUntilIdle(); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/credential_signer.cc b/components/web_discovery/browser/credential_signer.cc new file mode 100644 index 000000000000..ca8fce0c3b51 --- /dev/null +++ b/components/web_discovery/browser/credential_signer.cc @@ -0,0 +1,12 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/credential_signer.h" + +namespace web_discovery { + +CredentialSigner::~CredentialSigner() = default; + +} // namespace web_discovery diff --git a/components/web_discovery/browser/credential_signer.h b/components/web_discovery/browser/credential_signer.h new file mode 100644 index 000000000000..b7e167cf1e49 --- /dev/null +++ b/components/web_discovery/browser/credential_signer.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_CREDENTIAL_SIGNER_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_CREDENTIAL_SIGNER_H_ + +#include +#include + +#include "base/functional/callback.h" + +namespace web_discovery { + +class CredentialSigner { + public: + using SignCallback = + base::OnceCallback>)>; + virtual ~CredentialSigner(); + + // Returns true is a credential is available for the current date. + // The caller can expect future calls to `Sign` to succeed, if made today. + virtual bool CredentialExistsForToday() = 0; + + // Signs a message for a given basename. The server has the ability + // to check whether two messages with the same basename were signed + // with the same credential without revealing the credential used, + // preventing Sybil attacks. + // See signature_basename.h/cc for more information on how the basename + // should be generated. + virtual bool Sign(std::vector msg, + std::vector basename, + SignCallback callback) = 0; +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_CREDENTIAL_SIGNER_H_ diff --git a/components/web_discovery/browser/document_extractor/rs/.gitignore b/components/web_discovery/browser/document_extractor/rs/.gitignore new file mode 100644 index 000000000000..436cbd991cdd --- /dev/null +++ b/components/web_discovery/browser/document_extractor/rs/.gitignore @@ -0,0 +1,2 @@ +target +example/main diff --git a/components/web_discovery/browser/document_extractor/rs/BUILD.gn b/components/web_discovery/browser/document_extractor/rs/BUILD.gn new file mode 100644 index 000000000000..976310b3cdb3 --- /dev/null +++ b/components/web_discovery/browser/document_extractor/rs/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright (c) 2024 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +import("//build/rust/rust_static_library.gni") + +rust_static_library("rust_lib") { + crate_name = "document_extractor_cxx" + crate_root = "src/lib.rs" + allow_unsafe = true + + edition = "2021" + sources = [ "src/lib.rs" ] + + visibility = [ "//brave/components/web_discovery/browser:*" ] + + cxx_bindings = [ "src/lib.rs" ] + + deps = [ + "//brave/third_party/rust/html5ever/v0_25:lib", + "//brave/third_party/rust/kuchikiki/v0_8:lib", + ] +} diff --git a/components/web_discovery/browser/document_extractor/rs/Cargo.lock b/components/web_discovery/browser/document_extractor/rs/Cargo.lock new file mode 100644 index 000000000000..dd4a2a1e7410 --- /dev/null +++ b/components/web_discovery/browser/document_extractor/rs/Cargo.lock @@ -0,0 +1,767 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "matches", + "phf", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.66", +] + +[[package]] +name = "cxx" +version = "1.0.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb497fad022245b29c2a0351df572e2d67c1046bcef2260ebc022aec81efea82" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c799a4a846f1c0acb9f36bb9c6272d9b3d9457f3633c7753c6057270df13c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bc249a7e3cd554fd2e8e08a426e9670c50bbfc9a621653cfa9accc9641783" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "document-extractor-cxx" +version = "0.1.0" +dependencies = [ + "cxx", + "html5ever", + "kuchikiki", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "html5ever" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "kuchikiki" +version = "0.8.4-speedreader" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af921794306a6885fc7510012ce12015d2cf0bfdba82fb217b9c2caf324a4618" +dependencies = [ + "cssparser", + "html5ever", + "indexmap", + "matches", + "selectors", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "link-cplusplus" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +dependencies = [ + "cc", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "markup5ever" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" +dependencies = [ + "log", + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf", + "phf_codegen", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/components/web_discovery/browser/document_extractor/rs/Cargo.toml b/components/web_discovery/browser/document_extractor/rs/Cargo.toml new file mode 100644 index 000000000000..cde2a9183587 --- /dev/null +++ b/components/web_discovery/browser/document_extractor/rs/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "document-extractor-cxx" +version = "0.1.0" +authors = ["Darnell Andries "] +edition = "2021" +license = "MPL-2.0" + +[dependencies] +cxx = "1" +kuchikiki = "0.8.4-speedreader" +html5ever = "0.25" + +[lib] +name = "document_extractor_cxx" +crate-type = ["rlib"] diff --git a/components/web_discovery/browser/document_extractor/rs/src/lib.rs b/components/web_discovery/browser/document_extractor/rs/src/lib.rs new file mode 100644 index 000000000000..4db51614a395 --- /dev/null +++ b/components/web_discovery/browser/document_extractor/rs/src/lib.rs @@ -0,0 +1,124 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::collections::HashMap; + +use cxx::{CxxString, CxxVector}; +use html5ever::tree_builder::TreeSink; +use kuchikiki::{ + iter::{Descendants, Elements, Select}, + parse_html, + traits::TendrilSink, +}; + +#[cxx::bridge(namespace = "rust_document_extractor")] +mod ffi { + pub struct SelectAttributeRequest { + /// An optional selector for an element within the current selected element. + /// The attribute will be retrieved from the embedded element. + /// If not needed, an empty string should be provided. + pub sub_selector: String, + /// Arbitrary ID used for storing the scraped result. + pub key: String, + /// Name of the attribute to scrape. + pub attribute: String, + } + + pub struct SelectRequest { + /// The DOM selector for the element to scrape. + pub root_selector: String, + /// Scrape requests for the selected element. + pub attribute_requests: Vec, + } + + pub struct AttributePair { + /// Arbitrary ID for the scraped result. + pub key: String, + /// The scraped value. Will be empty if attribute is not available. + pub value: String, + } + + pub struct AttributeResult { + /// The DOM selector for the scraped element. + pub root_selector: String, + /// A list of arbitrary IDs and scraped value pairs. + pub attribute_pairs: Vec, + } + + extern "Rust" { + /// Extracts DOM attributes from the result of a double fetch. + fn query_element_attributes( + html: &CxxString, + requests: &CxxVector, + ) -> Vec; + } +} + +use ffi::*; + +const TEXT_CONTENT_ATTRIBUTE_NAME: &str = "textContent"; + +fn extract_attributes_from_nodes( + root_selector: &str, + attribute_requests: &[SelectAttributeRequest], + nodes: Select>, + results: &mut Vec, +) { + for node in nodes { + let mut attribute_map = HashMap::new(); + for attribute_request in attribute_requests { + let sub_node = match attribute_request.sub_selector.is_empty() { + false => match node.as_node().select_first(&attribute_request.sub_selector) { + Ok(e) => Some(e), + Err(_) => { + attribute_map.insert(attribute_request.key.clone(), String::new()); + continue; + } + }, + true => None, + }; + let node_to_query = sub_node.as_ref().unwrap_or(&node).as_node(); + + let attribute_value = match attribute_request.attribute == TEXT_CONTENT_ATTRIBUTE_NAME { + true => node_to_query.text_contents(), + false => node_to_query + .as_element() + .and_then(|element| { + let attributes = element.attributes.borrow(); + attributes.get(attribute_request.attribute.as_str()).map(|v| v.to_string()) + }) + .unwrap_or_default(), + }; + attribute_map.insert(attribute_request.key.clone(), attribute_value); + } + results.push(AttributeResult { + root_selector: root_selector.to_string(), + attribute_pairs: attribute_map + .into_iter() + .map(|(key, value)| AttributePair { key, value }) + .collect(), + }); + } +} + +pub fn query_element_attributes( + html: &CxxString, + requests: &CxxVector, +) -> Vec { + let mut sink = parse_html().one(html.to_str().unwrap_or_default()); + let mut results = Vec::new(); + let document = sink.get_document(); + for request in requests { + if let Ok(nodes) = document.select(&request.root_selector) { + extract_attributes_from_nodes( + &request.root_selector, + &request.attribute_requests, + nodes, + &mut results, + ); + } + } + results +} diff --git a/components/web_discovery/browser/double_fetcher.cc b/components/web_discovery/browser/double_fetcher.cc new file mode 100644 index 000000000000..fbf8697b0010 --- /dev/null +++ b/components/web_discovery/browser/double_fetcher.cc @@ -0,0 +1,132 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/double_fetcher.h" + +#include + +#include "brave/components/web_discovery/browser/pref_names.h" +#include "brave/components/web_discovery/browser/request_queue.h" +#include "brave/components/web_discovery/browser/util.h" +#include "components/prefs/pref_service.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/public/mojom/url_response_head.mojom.h" + +namespace web_discovery { + +namespace { +constexpr char kUrlKey[] = "url"; +constexpr char kAssociatedDataKey[] = "assoc_data"; + +constexpr base::TimeDelta kRequestMaxAge = base::Hours(1); +constexpr base::TimeDelta kMinRequestInterval = + base::Minutes(1) - base::Seconds(5); +constexpr base::TimeDelta kMaxRequestInterval = + base::Minutes(1) + base::Seconds(5); +constexpr size_t kMaxRetries = 3; +constexpr size_t kMaxDoubleFetchResponseSize = 2 * 1024 * 1024; + +constexpr net::NetworkTrafficAnnotationTag kFetchNetworkTrafficAnnotation = + net::DefineNetworkTrafficAnnotation("wdp_doublefetch", R"( + semantics { + sender: "Brave Web Discovery Double Fetch" + description: + "Retrieves a page of interest without cookies for + scraping and reporting via Web Discovery." + trigger: + "Requests are sent minutes after the original + page request is made by the user." + data: "Page data" + destination: WEBSITE + } + policy { + cookies_allowed: NO + setting: + "Users can opt-in or out via brave://settings/search" + })"); + +} // namespace + +DoubleFetcher::DoubleFetcher( + PrefService* profile_prefs, + network::SharedURLLoaderFactory* shared_url_loader_factory, + FetchedCallback callback) + : profile_prefs_(profile_prefs), + shared_url_loader_factory_(shared_url_loader_factory), + request_queue_(profile_prefs, + kScheduledDoubleFetches, + kRequestMaxAge, + kMinRequestInterval, + kMaxRequestInterval, + kMaxRetries, + base::BindRepeating(&DoubleFetcher::OnFetchTimer, + base::Unretained(this))), + callback_(callback) {} + +DoubleFetcher::~DoubleFetcher() = default; + +void DoubleFetcher::ScheduleDoubleFetch(const GURL& url, + base::Value associated_data) { + base::Value::Dict fetch_dict; + fetch_dict.Set(kUrlKey, url.spec()); + fetch_dict.Set(kAssociatedDataKey, std::move(associated_data)); + + request_queue_.ScheduleRequest(base::Value(std::move(fetch_dict))); +} + +void DoubleFetcher::OnFetchTimer(const base::Value& request_data) { + const auto* fetch_dict = request_data.GetIfDict(); + const auto* url_str = fetch_dict ? fetch_dict->FindString(kUrlKey) : nullptr; + if (!url_str) { + request_queue_.NotifyRequestComplete(true); + return; + } + + GURL url(*url_str); + auto resource_request = CreateResourceRequest(url); + url_loader_ = network::SimpleURLLoader::Create( + std::move(resource_request), kFetchNetworkTrafficAnnotation); + url_loader_->DownloadToString( + shared_url_loader_factory_.get(), + base::BindOnce(&DoubleFetcher::OnRequestComplete, base::Unretained(this), + url), + kMaxDoubleFetchResponseSize); +} + +void DoubleFetcher::OnRequestComplete( + GURL url, + std::optional response_body) { + auto result = ProcessCompletedRequest(&response_body); + + auto request_data = request_queue_.NotifyRequestComplete(result); + + if (request_data) { + const auto& request_dict = request_data->GetDict(); + const auto* assoc_data = request_dict.Find(kAssociatedDataKey); + if (assoc_data) { + callback_.Run(url, *assoc_data, response_body); + } + } +} + +bool DoubleFetcher::ProcessCompletedRequest( + std::optional* response_body) { + auto* response_info = url_loader_->ResponseInfo(); + if (!response_body || !response_info) { + return false; + } + auto response_code = response_info->headers->response_code(); + if (response_code < 200 || response_code >= 300) { + if (response_code >= 500) { + // Only retry failures due to server error + return false; + } + *response_body = std::nullopt; + } + return true; +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/double_fetcher.h b/components/web_discovery/browser/double_fetcher.h new file mode 100644 index 000000000000..e2b8c979a78d --- /dev/null +++ b/components/web_discovery/browser/double_fetcher.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_DOUBLE_FETCHER_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_DOUBLE_FETCHER_H_ + +#include +#include +#include + +#include "base/memory/raw_ptr.h" +#include "base/values.h" +#include "brave/components/web_discovery/browser/request_queue.h" +#include "url/gurl.h" + +class PrefService; + +namespace network { +class SharedURLLoaderFactory; +class SimpleURLLoader; +} // namespace network + +namespace web_discovery { + +// Makes anonymous requests to relevant page URLs, without involvement of the +// user's session. In the case of search engine result pages, the result of the +// double fetch will scraped for search engine results for a future submission. +// Uses `RequestQueue` to persist and schedule double fetches. Requests +// will be sent on somewhat random intervals averaging to a minute. +class DoubleFetcher { + public: + using FetchedCallback = + base::RepeatingCallback response_body)>; + DoubleFetcher(PrefService* profile_prefs, + network::SharedURLLoaderFactory* shared_url_loader_factory, + FetchedCallback callback); + ~DoubleFetcher(); + + DoubleFetcher(const DoubleFetcher&) = delete; + DoubleFetcher& operator=(const DoubleFetcher&) = delete; + + // Queues a double fetch for a given URL. The associated data will be stored + // beside the queue request, and will be passed to the `FetchedCallback` + // upon completion. + void ScheduleDoubleFetch(const GURL& url, base::Value associated_data); + + private: + void OnFetchTimer(const base::Value& request_data); + void OnRequestComplete(GURL url, std::optional response_body); + bool ProcessCompletedRequest(std::optional* response_body); + + raw_ptr profile_prefs_; + raw_ptr shared_url_loader_factory_; + std::unique_ptr url_loader_; + + RequestQueue request_queue_; + + FetchedCallback callback_; +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_DOUBLE_FETCHER_H_ diff --git a/components/web_discovery/browser/double_fetcher_unittest.cc b/components/web_discovery/browser/double_fetcher_unittest.cc new file mode 100644 index 000000000000..ea1241c2a8e3 --- /dev/null +++ b/components/web_discovery/browser/double_fetcher_unittest.cc @@ -0,0 +1,196 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/double_fetcher.h" + +#include +#include +#include +#include + +#include "base/functional/bind.h" +#include "base/memory/scoped_refptr.h" +#include "base/test/task_environment.h" +#include "brave/components/web_discovery/browser/wdp_service.h" +#include "components/prefs/testing_pref_service.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace web_discovery { + +namespace { +constexpr char kTestUrl[] = "https://example.com/test"; +constexpr char kTestResponseText[] = "test"; +} // namespace + +class WebDiscoveryDoubleFetcherTest : public testing::Test { + public: + WebDiscoveryDoubleFetcherTest() + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), + shared_url_loader_factory_( + base::MakeRefCounted( + &url_loader_factory_)) {} + ~WebDiscoveryDoubleFetcherTest() override = default; + + // testing::Test: + void SetUp() override { + WDPService::RegisterProfilePrefs(profile_prefs_.registry()); + + InitDoubleFetcher(); + SetUpResponse(net::HTTP_OK); + } + + protected: + void InitDoubleFetcher() { + double_fetcher_ = std::make_unique( + &profile_prefs_, shared_url_loader_factory_.get(), + base::BindRepeating(&WebDiscoveryDoubleFetcherTest::HandleDoubleFetch, + base::Unretained(this))); + } + + void SetUpResponse(net::HttpStatusCode status) { + url_loader_factory_.ClearResponses(); + url_loader_factory_.AddResponse(kTestUrl, kTestResponseText, status); + } + + base::test::TaskEnvironment task_environment_; + struct CompletedFetch { + GURL url; + base::Value associated_data; + std::optional response_body; + }; + std::unique_ptr double_fetcher_; + std::vector completed_fetches_; + network::TestURLLoaderFactory url_loader_factory_; + + private: + void HandleDoubleFetch(const GURL& url, + const base::Value& associated_data, + std::optional response_body) { + completed_fetches_.push_back( + CompletedFetch{.url = url, + .associated_data = associated_data.Clone(), + .response_body = response_body}); + } + + TestingPrefServiceSimple profile_prefs_; + scoped_refptr shared_url_loader_factory_; +}; + +TEST_F(WebDiscoveryDoubleFetcherTest, ScheduleAndFetch) { + GURL url(kTestUrl); + double_fetcher_->ScheduleDoubleFetch(url, base::Value("foo1 data")); + double_fetcher_->ScheduleDoubleFetch(url, base::Value("foo2 data")); + + task_environment_.FastForwardBy(base::Seconds(45)); + EXPECT_TRUE(completed_fetches_.empty()); + + task_environment_.FastForwardBy(base::Seconds(30)); + ASSERT_EQ(completed_fetches_.size(), 1u); + + EXPECT_EQ(completed_fetches_[0].url, url); + EXPECT_EQ(completed_fetches_[0].associated_data, base::Value("foo1 data")); + ASSERT_TRUE(completed_fetches_[0].response_body); + EXPECT_EQ(*completed_fetches_[0].response_body, kTestResponseText); + + completed_fetches_.clear(); + + task_environment_.FastForwardBy(base::Seconds(25)); + EXPECT_TRUE(completed_fetches_.empty()); + + task_environment_.FastForwardBy(base::Seconds(45)); + ASSERT_EQ(completed_fetches_.size(), 1u); + + EXPECT_EQ(completed_fetches_[0].url, url); + EXPECT_EQ(completed_fetches_[0].associated_data, base::Value("foo2 data")); + ASSERT_TRUE(completed_fetches_[0].response_body); + EXPECT_EQ(*completed_fetches_[0].response_body, kTestResponseText); + + completed_fetches_.clear(); + + task_environment_.FastForwardBy(base::Seconds(180)); + EXPECT_TRUE(completed_fetches_.empty()); +} + +TEST_F(WebDiscoveryDoubleFetcherTest, LoadScheduleFromStorageAndFetch) { + GURL url(kTestUrl); + double_fetcher_->ScheduleDoubleFetch(url, base::Value(1)); + double_fetcher_->ScheduleDoubleFetch(url, base::Value(2)); + + EXPECT_TRUE(completed_fetches_.empty()); + + InitDoubleFetcher(); + + task_environment_.FastForwardBy(base::Seconds(240)); + EXPECT_EQ(completed_fetches_.size(), 2u); +} + +TEST_F(WebDiscoveryDoubleFetcherTest, ScheduleRetry) { + GURL url(kTestUrl); + SetUpResponse(net::HTTP_INTERNAL_SERVER_ERROR); + double_fetcher_->ScheduleDoubleFetch(url, base::Value(true)); + + task_environment_.FastForwardBy(base::Seconds(75)); + EXPECT_TRUE(completed_fetches_.empty()); + + SetUpResponse(net::HTTP_OK); + task_environment_.FastForwardBy(base::Seconds(30)); + + ASSERT_EQ(completed_fetches_.size(), 1u); + + EXPECT_EQ(completed_fetches_[0].url, url); + EXPECT_EQ(completed_fetches_[0].associated_data, base::Value(true)); + ASSERT_TRUE(completed_fetches_[0].response_body); + EXPECT_EQ(*completed_fetches_[0].response_body, kTestResponseText); + + completed_fetches_.clear(); + + task_environment_.FastForwardBy(base::Seconds(180)); + EXPECT_TRUE(completed_fetches_.empty()); +} + +TEST_F(WebDiscoveryDoubleFetcherTest, ScheduleMaxRetries) { + GURL url(kTestUrl); + SetUpResponse(net::HTTP_INTERNAL_SERVER_ERROR); + double_fetcher_->ScheduleDoubleFetch(url, base::Value(true)); + + task_environment_.FastForwardBy(base::Seconds(70)); + EXPECT_TRUE(completed_fetches_.empty()); + + task_environment_.FastForwardBy(base::Seconds(120)); + ASSERT_EQ(completed_fetches_.size(), 1u); + + EXPECT_EQ(completed_fetches_[0].url, url); + EXPECT_EQ(completed_fetches_[0].associated_data, base::Value(true)); + ASSERT_FALSE(completed_fetches_[0].response_body); + + completed_fetches_.clear(); + + SetUpResponse(net::HTTP_OK); + task_environment_.FastForwardBy(base::Minutes(10)); + EXPECT_TRUE(completed_fetches_.empty()); +} + +TEST_F(WebDiscoveryDoubleFetcherTest, ScheduleNoRetry) { + GURL url(kTestUrl); + SetUpResponse(net::HTTP_NOT_FOUND); + double_fetcher_->ScheduleDoubleFetch(url, base::Value(123)); + + task_environment_.FastForwardBy(base::Seconds(70)); + ASSERT_EQ(completed_fetches_.size(), 1u); + + EXPECT_EQ(completed_fetches_[0].url, url); + EXPECT_EQ(completed_fetches_[0].associated_data, base::Value(123)); + ASSERT_FALSE(completed_fetches_[0].response_body); + + completed_fetches_.clear(); + + SetUpResponse(net::HTTP_OK); + task_environment_.FastForwardBy(base::Minutes(10)); + EXPECT_TRUE(completed_fetches_.empty()); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/ecdh_aes.cc b/components/web_discovery/browser/ecdh_aes.cc new file mode 100644 index 000000000000..4d0c59b38fec --- /dev/null +++ b/components/web_discovery/browser/ecdh_aes.cc @@ -0,0 +1,139 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/ecdh_aes.h" + +#include + +#include "base/base64.h" +#include "base/logging.h" +#include "base/ranges/algorithm.h" +#include "base/strings/string_number_conversions.h" +#include "crypto/random.h" +#include "crypto/sha2.h" +#include "third_party/boringssl/src/include/openssl/aead.h" +#include "third_party/boringssl/src/include/openssl/ec.h" +#include "third_party/boringssl/src/include/openssl/ec_key.h" +#include "third_party/boringssl/src/include/openssl/ecdh.h" +#include "third_party/boringssl/src/include/openssl/nid.h" + +namespace web_discovery { + +namespace { + +constexpr size_t kAesKeySize = 16; +constexpr size_t kAesTagLength = 16; +constexpr size_t kIvSize = 12; +constexpr size_t kKeyMaterialSize = 32; +// P-256 field size * 2 + type byte +constexpr size_t kComponentOctSize = 32 * 2 + 1; +// type byte + public component + initialization vector +constexpr size_t kEncodedPubKeyAndIv = 1 + kComponentOctSize + kIvSize; +constexpr uint8_t kP256TypeByte = 0xea; + +bssl::UniquePtr CreateECKey() { + return bssl::UniquePtr( + EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); +} + +} // namespace + +AESEncryptResult::AESEncryptResult(std::vector data, + std::string encoded_public_component_and_iv) + : data(data), + encoded_public_component_and_iv(encoded_public_component_and_iv) {} + +AESEncryptResult::~AESEncryptResult() = default; +AESEncryptResult::AESEncryptResult(const AESEncryptResult&) = default; + +std::optional DeriveAESKeyAndEncrypt( + const std::string& server_pub_key_b64, + const std::vector& data) { + auto server_pub_key_data = base::Base64Decode(server_pub_key_b64); + if (!server_pub_key_data) { + VLOG(1) << "ec p-256 public component not available or incorrect size"; + return std::nullopt; + } + + auto client_private_key = CreateECKey(); + + if (!client_private_key) { + VLOG(1) << "Failed to init P-256 curve"; + return std::nullopt; + } + + bssl::UniquePtr server_public_point(EC_POINT_new(EC_group_p256())); + if (!server_public_point) { + VLOG(1) << "Failed to init EC public point"; + return std::nullopt; + } + + if (!EC_POINT_oct2point(EC_group_p256(), server_public_point.get(), + server_pub_key_data->data(), + server_pub_key_data->size(), nullptr)) { + VLOG(1) << "Failed to load server public key data into EC point"; + return std::nullopt; + } + + if (!EC_KEY_generate_key(client_private_key.get())) { + VLOG(1) << "Failed to generate client EC key"; + return std::nullopt; + } + + uint8_t shared_key_material[kKeyMaterialSize]; + if (!ECDH_compute_key(shared_key_material, kKeyMaterialSize, + server_public_point.get(), client_private_key.get(), + nullptr)) { + VLOG(1) << "Failed to set derive key via ECDH"; + return std::nullopt; + } + + auto key_material_hash = crypto::SHA256Hash(shared_key_material); + + auto aes_key = std::vector(key_material_hash.begin(), + key_material_hash.begin() + kAesKeySize); + auto* algo = EVP_aead_aes_128_gcm(); + + bssl::ScopedEVP_AEAD_CTX ctx; + if (!EVP_AEAD_CTX_init(ctx.get(), algo, aes_key.data(), aes_key.size(), + kAesTagLength, nullptr)) { + VLOG(1) << "Failed to init AEAD context"; + return std::nullopt; + } + + size_t len; + std::array iv; + + crypto::RandBytes(iv); + + std::vector output(data.size() + EVP_AEAD_max_overhead(algo)); + if (!EVP_AEAD_CTX_seal(ctx.get(), output.data(), &len, output.size(), + iv.data(), iv.size(), data.data(), data.size(), + nullptr, 0)) { + VLOG(1) << "Failed to encrypt via AES"; + return std::nullopt; + } + + output.resize(len); + + std::array public_component_and_iv; + public_component_and_iv[0] = kP256TypeByte; + + if (!EC_POINT_point2oct( + EC_group_p256(), EC_KEY_get0_public_key(client_private_key.get()), + POINT_CONVERSION_UNCOMPRESSED, public_component_and_iv.data() + 1, + kComponentOctSize, nullptr)) { + VLOG(1) << "Failed to export EC public point/key"; + return std::nullopt; + } + + base::ranges::copy(iv.begin(), iv.end(), + public_component_and_iv.begin() + kComponentOctSize + 1); + + return std::make_optional( + output, base::Base64Encode(public_component_and_iv)); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/ecdh_aes.h b/components/web_discovery/browser/ecdh_aes.h new file mode 100644 index 000000000000..d9ce7604888f --- /dev/null +++ b/components/web_discovery/browser/ecdh_aes.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_ECDH_AES_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_ECDH_AES_H_ + +#include +#include +#include + +namespace web_discovery { + +struct AESEncryptResult { + AESEncryptResult(std::vector data, + std::string encoded_public_component_and_iv); + ~AESEncryptResult(); + + AESEncryptResult(const AESEncryptResult&); + + std::vector data; + std::string encoded_public_component_and_iv; +}; + +std::optional DeriveAESKeyAndEncrypt( + const std::string& server_pub_key_b64, + const std::vector& data); + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_ECDH_AES_H_ diff --git a/components/web_discovery/browser/hash_detection.cc b/components/web_discovery/browser/hash_detection.cc new file mode 100644 index 000000000000..d2970c2faf0b --- /dev/null +++ b/components/web_discovery/browser/hash_detection.cc @@ -0,0 +1,1489 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/hash_detection.h" + +#include + +#include "base/containers/fixed_flat_map.h" +#include "brave/components/web_discovery/browser/util.h" + +namespace web_discovery { + +constexpr size_t kClassifierTokenCount = 62; + +// Markov chain transition matrix for hash classifier +constexpr float + kClassifierTransitionMatrix[kClassifierTokenCount][kClassifierTokenCount] = + {{ + -1.839225984234144, -1.8009413231413045, -2.5864601561900273, + -2.779077348461893, -2.41154163187108, -2.8701669802592216, + -3.010853183897427, -2.9831997811803244, -3.0258844950086354, + -3.028549665472028, -4.922879838696041, -4.503195676571833, + -4.598154161855354, -5.027344698225922, -5.217723368153992, + -5.366375752342653, -5.195909278900248, -3.2940850310881387, + -5.181468249477294, -6.110371416838184, -5.766202229535159, + -5.393352786265537, -4.920024681004049, -6.049294408676102, + -6.399015490326156, -4.690163484884073, -7.459851804835359, + -5.825337077308845, -4.675000760327133, -5.3635239851672845, + -6.331713706782754, -6.169284763337202, -5.951151191317628, + -5.768078242705815, -7.332129653597336, -6.766704624275413, + -6.01222435569866, -5.268617743244878, -6.279416117827666, + -5.861972559492891, -5.838071468059843, -6.267250173459698, + -6.283981033030117, -6.454759516784266, -6.5195694451700765, + -6.83526729571271, -6.458157413359671, -6.359696067797872, + -6.250241538141395, -6.647172527007435, -6.973987597161365, + -6.4598607011160425, -7.685547265665763, -6.5912026847359835, + -5.794718807999361, -6.474629988987015, -7.135098972554303, + -6.880960523371609, -6.747429130747086, -7.733118660641065, + -8.398636456057481, -7.6323973914922005, + }, + { + -2.11332750230783, -2.3060786022344897, -2.3740965556720726, + -2.5398307619465608, -2.463417892707307, -2.130953109899374, + -2.8009573227909073, -2.9401508827086436, -2.748847204575824, + -2.8414237914951976, -5.010884149435608, -4.7716590964777925, + -4.868359687249472, -5.322380370933768, -5.037006822424893, + -5.279762905036231, -5.635270908532894, -2.9219817650555973, + -5.50179037155463, -6.5406369920023, -6.311742070591071, + -5.516975693542294, -5.7251712834206305, -6.083331008626942, + -6.409455776722731, -4.856277028595425, -7.356357334609669, + -6.255977591877104, -5.042089425488359, -5.723688703460408, + -6.9306288690141455, -5.879866127104103, -6.269949621534028, + -6.969512838475713, -7.475373123729266, -6.802062729887527, + -6.566109586768108, -6.661695002244713, -6.634070998648396, + -6.128265317507457, -6.0654489711795785, -6.79728322915323, + -6.99559673771516, -6.996654379273295, -7.671972036661277, + -7.840255377779173, -7.132455920432357, -7.233113706563278, + -6.737551430374816, -7.2706736012479585, -7.430290364348156, + -6.991903776514698, -8.559572276072503, -7.231105001502197, + -6.117005677632925, -6.669675284003577, -7.87022016648111, + -7.635233211015519, -7.18343028641663, -8.53463332772525, + -8.567176875457722, -7.573077285525099, + }, + { + -1.6313545940843748, -2.457209014039589, -2.565380346827546, + -2.767788786784649, -2.6386222432933284, -2.595661570703591, + -2.6893427324813977, -2.7198733792574403, -2.720533202987637, + -2.8467380805237736, -5.029918409868431, -4.178710463028381, + -5.07588590483658, -5.057436988529895, -4.899938336310833, + -5.271744381251988, -5.715419865537325, -3.1739071041721116, + -5.367102243680773, -6.224018730172038, -5.866973141306898, + -5.730015933050199, -5.360958031920617, -6.20201967617667, + -6.328452890969734, -4.837569916970598, -7.520833339090353, + -5.8325529174204185, -4.950768880962046, -5.339171801246006, + -6.500538943869661, -6.141626179853517, -5.826929659866057, + -6.371549418153106, -7.095602530626063, -6.863928261303842, + -6.26532998038965, -6.486382994639529, -6.568804701240378, + -5.928030195562504, -6.138501621140467, -6.375855129864318, + -6.672567424416514, -6.87690014131182, -7.500698430681298, + -7.486404619424152, -7.110483757622713, -6.948864783187929, + -6.7689909475117735, -7.44680608044408, -7.305175362028806, + -6.490004000406482, -8.278142463867583, -7.100043692626031, + -6.262757631704339, -6.785644569035485, -7.917859578552653, + -6.966883288690608, -6.7663306639529575, -7.827911341889713, + -7.709275282211631, -7.650980633730635, + }, + { + -2.2483023852308777, -2.2601431921520803, -2.543211818297523, + -2.6290975996017654, -2.597012293697696, -2.425038774903284, + -2.6464962853598264, -2.7268820909874645, -2.7170988469992086, + -2.6384412943652213, -4.8059842634060495, -4.801920790322523, + -4.881995682865955, -4.399130557719554, -4.977525552072196, + -5.254333597613871, -5.4861286120783905, -3.172333855510997, + -5.311009967578493, -6.3787775808955045, -6.060833750357407, + -5.179488625881995, -5.284849141539821, -6.185226477248653, + -6.342526765978808, -5.0477193482508715, -7.229508117457252, + -5.92913163950562, -4.781563610047119, -5.175693021409531, + -6.686598785853124, -5.953265540842049, -5.765108625614716, + -6.249636653394711, -7.053405713688212, -6.871084156894258, + -4.746700678954438, -4.435206614193364, -6.2492260600816145, + -5.437566316202071, -5.90294982336378, -6.5708663077474965, + -6.4774110336687505, -6.642451113295277, -7.490560332753359, + -7.638809897851904, -7.037026821604173, -6.792453878834064, + -6.557926605402683, -7.183535297458448, -7.556615154408894, + -6.675849393697125, -7.929241490870054, -6.920447880012594, + -5.937211886970303, -6.536908132533395, -7.266281062975223, + -7.138583909596181, -6.9407325511841, -7.611188320541975, + -8.063402408224212, -7.7539194100649205, + }, + { + -2.0350349956205984, -2.348602646771946, -2.41594411405956, + -2.651370395735765, -2.563410442434685, -2.706962611141542, + -2.7917112923152776, -2.7483543933766477, -2.6785026736567357, + -2.8111298756441037, -4.759837736546805, -4.20618015484438, + -4.787453629928326, -4.659538126267765, -5.015532806050122, + -5.056335259588051, -5.339468177159593, -3.040667184111712, + -5.203144447478613, -6.2264472230490675, -5.7097357671058155, + -5.585194192596145, -5.035187712987706, -5.542988699592872, + -6.206189420830567, -4.864552817623237, -7.387161020524848, + -5.130725109803191, -4.710070938057912, -5.015532806050122, + -5.563665939332496, -5.8412571088023135, -5.744609136156329, + -6.206575148897066, -6.842735398083401, -6.794361813799185, + -5.850672107736916, -6.088449236501511, -6.348519254409916, + -5.725588459198469, -6.064735388153122, -6.479956671206651, + -6.2209527037314265, -6.70094736915976, -7.3405268040104845, + -7.140183173146506, -6.467358872953416, -6.7233339772218645, + -6.184068000080206, -6.381516598393398, -7.406185056529545, + -6.368731036096427, -7.402351186018824, -6.6091509166536255, + -5.9623588984476195, -6.352529061520146, -7.133337180638517, + -7.24783614109371, -6.901266731308386, -7.234806640803376, + -7.432126848507545, -7.494477663675224, + }, + { + -2.0215812860615885, -2.455637406643175, -2.4330253990141046, + -2.6113902739466517, -2.6033787533338284, -2.6337646574813993, + -2.7111759743895725, -2.7471499108067454, -2.721672070749707, + -2.7644985821403028, -4.771735346669196, -4.967298931497775, + -4.806933772233218, -4.796223062219612, -4.702049659123834, + -4.615986584200591, -5.422222957790021, -3.024083897225106, + -5.341473161524181, -5.691932137893965, -5.870247680316981, + -5.6516120254419615, -4.85258639108815, -5.9684706438872475, + -6.205102036008844, -4.762283950640148, -7.106211026775939, + -5.754565801069223, -4.28552037163959, -5.083154204506742, + -6.514533306380853, -5.91282033178054, -5.801594696670316, + -6.211408552601299, -6.884100000075919, -6.5060032575028, + -6.051550266183317, -6.1528452495165125, -6.237040028392324, + -5.845664774991169, -5.83750622157883, -6.376088248894053, + -6.3936457603588925, -6.58430154569624, -7.071724850704769, + -7.213982427018076, -6.56730052905341, -6.608473779316545, + -6.403307671270629, -6.973970456584425, -7.485700648480843, + -6.5060032575028, -7.126013654072119, -6.592294210440276, + -5.525669597265563, -6.0726189713973024, -6.168884085714272, + -7.057863242618145, -6.702467385782517, -7.84259108644045, + -7.932742183434748, -6.889071012797939, + }, + { + -2.3515175517946965, -2.4895377785815813, -2.455794452354539, + -2.6177065611128625, -2.3916765698655924, -2.55893036105207, + -2.621684157393488, -2.6988517931658524, -2.630805311213068, + -2.6634082117951667, -4.639123440452939, -4.108116635400416, + -4.850674635561831, -4.879642999369406, -5.020506071184806, + -5.079702479761731, -5.3856932140647995, -2.922465393995544, + -5.272571266829349, -6.095451326503016, -5.703507123198923, + -5.5861660464475555, -5.144197286007818, -5.722031888813277, + -6.28586346876822, -4.872555039237073, -6.886991750607913, + -5.521389523166842, -4.712785087276782, -4.712557943607434, + -6.258849400356963, -5.781080159458192, -5.683518571573409, + -6.497978247391754, -6.801813554699944, -6.640350228026399, + -6.23670827447975, -6.480528140170159, -6.503409353749256, + -6.035279500709713, -6.187868456644626, -6.634900623258835, + -6.227883924459252, -6.665648019797556, -7.5457712876636505, + -7.422426740346121, -6.235666064901307, -6.769319721223498, + -6.343344078699495, -6.975730165249003, -7.353206363454935, + -6.635677322327007, -7.937813375439674, -6.762240045635436, + -6.147006740966892, -6.741296871790193, -7.715813334827543, + -7.183705495090042, -6.938232467377743, -7.593211012735211, + -8.021194984378726, -7.326496213127361, + }, + { + -2.404503840183417, -2.43761707422273, -2.4108252303609095, + -2.2999425986283435, -2.542843475674806, -2.6029429346724786, + -2.584141922741018, -2.5855127818195496, -2.5510561981552526, + -2.647681869737238, -4.782820501953572, -4.808208666935694, + -4.979563332219715, -5.008211525808261, -5.046432738628459, + -5.142457459370653, -5.485491564024772, -3.1144173778919617, + -5.417825855041213, -6.245547233803318, -5.938268453446564, + -5.963941744764968, -5.450843208258146, -6.059373833665894, + -6.223182084343739, -4.942745700375246, -7.208409268623897, + -6.038143491827719, -4.6277437642171355, -4.892513996642899, + -6.694766741586792, -6.117821568685912, -5.949820065666706, + -6.502648045547855, -6.835922059321975, -6.694766741586792, + -6.361312919572388, -6.403294392961231, -6.118769885494771, + -5.676056523505988, -6.406451957946552, -6.5244722133881785, + -6.6333977952105005, -6.6574953467895615, -7.573786078663716, + -7.446115322000925, -6.883692335590661, -6.903250344490318, + -6.3498910941692, -6.905331510694142, -7.588115904218541, + -6.879623153983935, -8.035692497676832, -6.939233062369824, + -5.959077527634608, -6.324513515108117, -7.549688527084656, + -7.120692921566297, -6.988939594552065, -7.891731808755324, + -7.727259481120012, -7.430143376434873, + }, + { + -2.2924692182301323, -2.450539739657926, -2.3769703926915984, + -2.532604294663703, -2.55393918066343, -2.609718771364289, + -2.6246425013179655, -2.661744915258029, -2.6011009513028984, + -2.6236780353341946, -4.669477044233787, -4.1521285006409165, + -4.955491059584871, -4.818890308190988, -5.030853379357302, + -5.002039294312803, -5.278250342600015, -3.076829272811893, + -5.307731406455737, -5.826171280206974, -6.022604681193609, + -5.8364861502494865, -5.222160875948972, -5.86734719439116, + -6.263453577434854, -5.1217643044016885, -7.32073988901422, + -5.758783848671988, -4.666476541899311, -5.259001445170042, + -6.374002568031498, -6.083417735921755, -5.695962812152638, + -6.490715185357981, -6.417147740477192, -6.55681384079998, + -5.431378631989581, -5.843302320137157, -6.369727752361356, + -5.638321273173884, -6.50456582229188, -6.275027142828877, + -6.210627212225794, -6.774279201200845, -7.54625217416398, + -7.377527245335997, -6.968174323388823, -6.961544464850153, + -6.5738576027632725, -6.808653231033201, -7.247759185607984, + -6.667823395181603, -8.06567857870257, -6.862038055489511, + -5.923075085846576, -6.772454383176216, -7.732534132174032, + -6.980444415980637, -6.935456028765855, -7.926350068393722, + -7.409760739910981, -7.357691884279851, + }, + { + -2.395140177772492, -2.3972905877762423, -2.4380054501613637, + -2.59514809923619, -2.5304957080424493, -2.553494441315067, + -2.592658670737186, -2.6234135177213647, -2.5068257242437872, + -2.493912396989218, -4.415128005729072, -4.8162395242315545, + -4.805681457561184, -4.959588437581088, -5.013259465745109, + -5.070148436368585, -5.778302005668334, -3.0289910877331776, + -5.527143864017425, -6.11105402601701, -6.123887370951674, + -5.975929983744964, -5.442482986164537, -6.151079378646925, + -6.120416006196215, -5.065655600056955, -7.071974876907828, + -5.957253719340739, -4.781773843113505, -5.354948164056644, + -6.520450343119245, -6.293473674919533, -5.817016517849025, + -6.85914069525248, -6.81356318675616, -6.777584585720929, + -5.97764525003172, -6.226877237430227, -6.02961643394924, + -5.9729353291005545, -6.418715298488868, -5.532354351604477, + -6.803710890313149, -6.889653320113874, -7.354541848692839, + -7.502757793000282, -6.938924369120657, -6.214291571987144, + -6.749334119435077, -7.237514956739779, -7.394443065445024, + -6.457482478746571, -7.947443614261728, -6.86121538479334, + -5.698583920623846, -6.845759126556648, -7.683896617545187, + -7.180188461548061, -6.757725777071325, -7.8704825731256, + -7.881944016644606, -7.107203568791572, + }, + { + -6.7847523397462, -4.956382101946025, -5.55168468117927, + -6.54694976738503, -6.679027997805541, -6.920449307304025, + -6.651631877885628, -7.028532297007316, -6.819873006155468, + -6.9668156836069475, -5.07824502580267, -3.6978375281608122, + -3.2158001218369128, -3.221520357616586, -3.5888259593254497, + -4.18118917224718, -3.0226572253435795, -3.971027081545084, + -3.562365839815367, -6.241860395850777, -3.738063429007306, + -2.286143322088719, -2.8154088123469325, -1.9366972011829824, + -6.402763696295053, -4.09673956284193, -6.7632686126933175, + -2.217315348390325, -2.7898081670861736, -2.2970370063901697, + -2.7943042308650328, -4.45525252224197, -5.6170558845712195, + -5.634999368405547, -4.204524838877218, -5.247540598697068, + -7.679731693977602, -7.496500850432476, -7.694782629326121, + -7.660949127015419, -8.444625153562612, -8.02242837580043, + -7.9107342568656245, -7.844229700045118, -8.496568431723452, + -8.90528981102539, -7.744710798608082, -7.796294560812227, + -7.4084137546993, -8.54315037261458, -8.96995880381818, + -7.702981796781846, -9.818649422090688, -7.982756374523151, + -7.087136791691802, -7.726484291615801, -9.302472289566692, + -8.264363329731667, -8.520253749464962, -9.573314897467998, + -10.041792973404897, -9.03352354731742, + }, + { + -5.530908158078372, -5.12660572425173, -5.285205739999349, + -5.724471900365845, -5.873312125055024, -6.16534879225732, + -6.148934708170209, -6.3610443530793574, -6.311033932504697, + -6.085846837289822, -1.9318371570117603, -3.9897678769234868, + -4.264721024425275, -4.508665668126993, -1.397989588361542, + -5.165043688633805, -5.1354703161064155, -4.710076673495516, + -2.2812710196631696, -5.744808587095395, -6.153180999051661, + -2.7741841902487896, -4.878049696777151, -5.0409257316634655, + -2.3898944653854226, -5.13595610674021, -7.841822106027579, + -2.8471437524600627, -3.4340095911141377, -4.619316513108799, + -2.3549200090866647, -5.921518406898233, -5.302848234969303, + -6.786138406296854, -4.207750888408638, -6.039012800612939, + -7.841822106027579, -7.704962923310382, -7.065002449743519, + -7.589296290560376, -8.260096172428325, -8.035112694192108, + -7.871344545293901, -7.140827013169332, -8.545938317958099, + -8.836893715754849, -7.993349965830133, -7.27788665694764, + -7.741565502287929, -7.888977243944857, -8.111485672976682, + -7.875096894912451, -10.091297373707675, -7.392485113590403, + -6.863268166852988, -7.610229500226842, -8.901213681542407, + -8.92241588919301, -8.313038591698524, -9.803615301255896, + -9.310181420201774, -9.00812940746704, + }, + { + -5.262771475279199, -4.784242541566078, -5.27970172852834, + -5.684918512820975, -5.945292208575153, -6.218979090786592, + -6.228233983716081, -6.354509212158639, -6.204315840281487, + -6.442281485719593, -2.526983467749325, -5.114296740462473, + -4.530006859098648, -5.422625873895182, -2.9651924820539826, + -5.615416770210166, -5.759915004501759, -0.738654569892248, + -3.9951800832331146, -7.496424744983864, -2.271649654828947, + -3.6688863968618732, -4.479075949205008, -6.406971077861438, + -2.7704548687771466, -5.064362048924221, -7.65209604855975, + -4.10990241837356, -4.3379100438872245, -3.144791234860585, + -4.257400003418622, -6.678114421469913, -6.866487433947222, + -7.697728752690109, -5.8045538215356425, -6.443871311173693, + -8.079096309219214, -8.153204281372934, -8.17767713789166, + -8.353264662504271, -8.864090286270262, -8.820319623214933, + -8.693771874337664, -8.686253041923635, -8.948055666387596, + -9.842684901480572, -8.909864867655205, -8.569719225667685, + -8.285794202093616, -9.08611589448313, -9.094531091408415, + -7.571541334321345, -10.622843459030147, -8.818180584966184, + -7.823031278179709, -8.265917771336019, -8.826764328657575, + -9.481851947393176, -9.0917181500318, -8.558120089824332, + -9.233307603986084, -10.138335143581529, + }, + { + -6.352576341887475, -4.375482811098582, -5.097247890129581, + -5.253867383491415, -5.300921501088017, -5.719261065235271, + -5.695077377144448, -4.664604980788103, -6.0509261540753565, + -6.1211148128546835, -2.6955840583648807, -4.0885439717448415, + -4.601612684872268, -4.3272866151564005, -0.8335121453767717, + -4.368698782518719, -4.841722524158204, -3.6648788033138118, + -2.5172646408008483, -5.8838060715240985, -5.03345350892465, + -4.275021730152804, -4.6405776530763765, -5.25643229403688, + -3.133213396702524, -3.881602832644344, -7.333792367288475, + -3.6375800962202542, -3.280343679384232, -4.168603006615407, + -3.0158762344692915, -4.957870530657444, -4.673357005846336, + -7.071279326505898, -4.732292586433726, -6.102987428262127, + -6.308187629826426, -6.692855118141101, -6.912844895031934, + -6.811275201992611, -7.387021770702892, -6.866694309094154, + -6.971677700053924, -7.120054614261141, -7.672123196561181, + -8.47105299909571, -7.06564216575394, -7.303313817631059, + -6.877175818825198, -7.588372692026248, -7.710844848156796, + -7.012073355807918, -9.487490679574803, -7.203069557208429, + -6.086293297912648, -6.450936411500557, -8.098699438256325, + -7.677018501414594, -7.313496820953744, -9.611543328244782, + -8.931691690166081, -8.112931574673333, + }, + { + -6.278777370845214, -4.701340011999448, -5.078865738235898, + -5.8830885176004895, -6.165050673343658, -6.274964098411205, + -6.212416394644692, -6.400778791567437, -6.741692863598321, + -6.816576094912807, -3.3928904712107735, -3.566247086795751, + -3.646180662937257, -3.523693462975528, -4.243767227003855, + -4.080529251966207, -3.795135190780714, -3.596123418433819, + -2.7581828008765474, -6.336206897605354, -4.412794119233608, + -2.7790238541384125, -3.7937492847610685, -1.8714655708089012, + -4.039189587014222, -3.917817104421266, -7.57147815542886, + -1.7211826197805813, -2.454732555101985, -3.258178620019967, + -3.831607640543702, -4.745787338078659, -3.8173867609921763, + -3.6132042458306612, -5.788388393117924, -5.078865738235898, + -6.509442541403006, -6.358094961721853, -6.982655840165303, + -5.955633264954192, -7.203135532877834, -6.519814112808369, + -6.508748303549079, -6.5805671825510945, -7.521390752829464, + -8.307422019239775, -6.987204748691672, -6.90870144071007, + -6.578332663638785, -7.263487211998134, -7.281252691733005, + -6.635300404511988, -9.747550691415777, -6.939413018359865, + -5.8716986926591, -6.717215468733563, -8.061948046761787, + -7.4055504084105825, -7.052394044113555, -9.471951436870118, + -9.440772718621666, -8.392393495069179, + }, + { + -6.019277337678054, -4.809610930457335, -5.353723551448762, + -5.676382755872694, -5.70371603264992, -5.986064759126338, + -5.8139317591377235, -5.978144349530662, -6.042881895447475, + -6.198360420377549, -2.2921047448703056, -4.34095023979577, + -4.345648109454055, -4.367598309292761, -2.0155741702111323, + -3.0132780221373907, -4.923111941566577, -4.563679111444075, + -2.223531060167143, -6.691102686207805, -5.506010794027101, + -3.0594865563826104, -4.849221407410736, -4.504665385487454, + -1.9294270482862954, -4.87704758753489, -7.8125932274537675, + -2.476262613634102, -3.9821539473427885, -2.8193399945915623, + -2.274864811699418, -5.85738068573614, -5.424700477670406, + -7.189965927648669, -6.389275800912233, -5.638934468614081, + -7.03745392086323, -7.341255375194895, -6.260863632640313, + -7.279063354938447, -7.679061834829245, -7.811259004440631, + -7.236144890792278, -7.5275849533863886, -7.494018129747305, + -9.1181262319292, -7.907769904821474, -7.576977708715964, + -7.061731490444577, -8.083943001314976, -8.105182737815888, + -7.709906510180343, -8.65367988774833, -7.560240916360441, + -6.676421938949557, -6.335428678009885, -9.006382193489584, + -8.143473650809202, -7.5376758563683515, -10.154666091954931, + -9.349927845986523, -8.656780665426579, + }, + { + -6.45948206141902, -4.541389017548509, -5.0469147172405115, + -5.714893702071352, -5.93765911006474, -6.159588168280474, + -6.484829040506417, -6.54643138634695, -6.777423978144398, + -6.472536180151478, -2.0992585177991696, -4.351360197286945, + -4.810779680772453, -4.478347221038041, -1.057979923268165, + -4.503803429976553, -4.369600328148692, -3.1761274441594316, + -2.5925706700973334, -6.132316774171815, -5.0527495258256305, + -3.548319221739584, -4.665298532220545, -4.239092161682817, + -2.8823289365699227, -3.9799558061803935, -7.613527691788449, + -2.9149052754992715, -3.0856952412535055, -3.988705873974733, + -3.519423658731669, -5.380012776918168, -5.236242140888087, + -7.294418265937878, -5.378236234528395, -5.653900468202214, + -6.843339306558724, -6.1506932535462235, -7.23734725322546, + -6.366553451469326, -7.364460455464448, -6.868708500684183, + -6.652923233094938, -6.285920796794185, -7.434940854111734, + -8.332992180665768, -7.045904469629325, -7.3935637933916185, + -6.8666551118723405, -7.771896007559911, -7.6868589648739984, + -6.982133113853507, -9.79784886428876, -7.098382406877942, + -6.05045863483477, -6.643853499354418, -7.8988076492249855, + -7.62952803313489, -7.2191872449632415, -9.030593711575094, + -9.78510983851133, -8.512650620040239, + }, + { + -7.312010848930209, -5.9704021836204655, -6.3878250976208735, + -6.964626238636961, -6.946983784483292, -7.4124339237241115, + -7.3181741966378775, -7.680384414313297, -7.712878247789743, + -7.554150900935073, -2.446039713635401, -5.2426041270797, + -5.947299500896444, -4.663409565765878, -1.9484553212571183, + -5.736061416021277, -5.712018325729332, -4.9933347157551395, + -3.124667578606162, -6.881965531986077, -5.837272445819192, + -3.4071965596735665, -4.466780007835062, -3.7439634287862087, + -2.4245394005655605, -2.7481043832730636, -7.96925132102691, + -3.001717011027925, -4.3613498567188325, -1.0557054893757776, + -3.70597885137566, -6.306264213716016, -4.571775536818785, + -8.174476930790583, -5.896948417633875, -6.225037976827956, + -7.408704473732679, -7.482057817244962, -7.278478028822127, + -7.6772808871048746, -8.27951403118039, -7.874318428531343, + -7.723948471543991, -7.700568997316141, -8.91379761462879, + -8.048158534515101, -7.944081716081054, -7.980577820654186, + -7.290733450482923, -8.159308709317411, -8.547715834521, + -7.821812931153412, -10.179602578494553, -7.600205085285631, + -6.935111692528849, -7.541317747952087, -8.80008790436004, + -8.364689804291782, -8.08874001915325, -10.681459532277783, + -10.11675467483489, -9.167510387022356, + }, + { + -7.888212115175222, -6.644858293408979, -6.899926832152373, + -7.644887076927795, -7.6845832447435445, -7.532598742461448, + -8.35541557609491, -8.047237610771814, -8.039272966898704, + -8.151069111852731, -3.668932909455333, -4.782855975285661, + -2.6659061839419613, -2.8967154778709228, -2.0223904901109755, + -4.446771913881909, -3.000219123980515, -4.8870757508472185, + -5.930891109758962, -6.823383785692898, -3.2455278628878323, + -2.6715253337166462, -3.4540042231373063, -1.613293110190054, + -3.32378892967727, -4.249589417392827, -7.22956773062339, + -3.479217249500404, -2.6394006861426296, -2.505331420610824, + -5.608431497999796, -3.9128781321838506, -6.817651617834854, + -5.899129031817539, -6.838124773860243, -4.798082958969038, + -6.707198867504101, -6.862144714309829, -6.946702102337892, + -6.119321735858142, -7.3106242225785305, -7.047703884629593, + -7.1735103563413185, -7.1142937780684905, -7.8727953781736435, + -7.684274173500164, -7.105593531784784, -7.043298950514618, + -6.705339482889372, -7.153500510194026, -7.545964351744817, + -6.429130146514012, -9.208169648866278, -7.146978747947639, + -6.381569697965211, -6.863775814304458, -8.274721978073412, + -7.87653370028425, -7.678419897035876, -9.611509357662127, + -9.195484489338961, -8.64028017837942, + }, + { + -5.479179331884548, -5.058052917950845, -5.462604366790335, + -6.042749073104725, -5.955329948912708, -5.958323963125313, + -6.182592584875092, -6.134964535885837, -5.987223312688662, + -6.092926822690444, -1.4103668710151773, -5.3350267357418115, + -4.970584606980225, -5.289457186272422, -1.770010358993832, + -4.901554513033236, -5.37688758694302, -5.089624713826659, + -3.3520314691389803, -5.392942008652894, -4.734460344193294, + -5.4364649785873, -5.373540303482963, -5.098468738623112, + -1.787587681664556, -3.2734283276295595, -6.018573842572738, + -5.287155689284143, -2.4508369623232906, -5.268931950327691, + -2.1042922431924556, -5.627005270227098, -5.60465202444635, + -5.974953220096848, -5.917202163476144, -5.89303609762896, + -7.4048682036926285, -7.92851451588768, -7.027392005486696, + -7.609083745121319, -7.38594019380711, -7.373517673808553, + -7.797674914928869, -7.69894007424318, -6.9721762828842495, + -7.92851451588768, -7.9072371174403955, -7.498269378781029, + -7.24617825143195, -7.836141195756666, -7.972477639308797, + -8.030297210197622, -7.424161406627308, -8.130380668754604, + -5.985681292336846, -7.548459123304884, -7.876146530370365, + -7.886403030537553, -8.383576565135217, -8.509870290459508, + -8.104405182351345, -7.3612475812167375, + }, + { + -4.615704248273826, -5.059874046144977, -5.605352624766998, + -6.125932548297868, -6.262849675716521, -6.79239877505316, + -6.990929985784472, -6.841047905444769, -7.1584533214794765, + -6.9886390340379165, -2.1061188583709898, -4.694960766195293, + -5.294767434713961, -4.877455498156362, -1.7372915500183557, + -4.54913156517679, -5.204152324138941, -4.129384627657894, + -2.037945671503165, -6.073264053143508, -4.589028985169175, + -2.990753289694604, -4.778969963958759, -4.689721987339014, + -2.425686266522293, -4.746481928875957, -7.836127606736178, + -3.351158263461062, -3.2418172438356554, -2.1018059989954563, + -2.7036646563447677, -4.616611132654885, -4.89196974360223, + -7.672674534246606, -4.594182160042578, -5.702759391924034, + -6.928687676907251, -6.888038111045066, -6.879281938695605, + -7.0247524516915965, -7.464610089759517, -7.314413547152238, + -7.29014621148154, -7.293244587314209, -7.623992355607218, + -8.372647679876952, -7.108856380340105, -7.38925322665568, + -6.931385277679899, -7.915280634635728, -8.074744053353632, + -6.836124212582984, -10.164407149105006, -7.372318020898097, + -6.283267109907934, -7.0071153102054895, -7.702596214111655, + -7.848216404055182, -7.332806708624255, -9.549591811814969, + -9.295811291038868, -8.128717117098297, + }, + { + -6.86596902684244, -5.052082122851932, -5.150933488599681, + -5.292208274141252, -6.214682697483703, -6.745331904339627, + -6.68552490120206, -6.760045968476806, -7.227305715576431, + -6.950863382929711, -2.1215717821722215, -3.9228106947364623, + -4.840908762978201, -3.0664686008437925, -1.6983460154315366, + -4.061987675375524, -4.329017422576037, -4.468146834761754, + -2.0559567932823986, -6.284656471404593, -4.495653917304999, + -2.239702601986903, -4.059167724800704, -4.675038779649125, + -2.554078005507218, -4.208324039380938, -7.846798279854166, + -4.952711862629422, -3.1510988852700317, -2.942373372199169, + -3.3895078376634635, -4.604176621456193, -5.1138376420493366, + -7.9144140567925305, -4.640693910453494, -5.102920313867999, + -6.555881088992486, -6.826369644359554, -6.799686147757116, + -6.863359893400418, -7.558312671802701, -7.067443904093005, + -7.193279183729173, -7.1899393531824245, -7.705592764618944, + -8.457679157012771, -7.256276285899237, -7.186610639983997, + -7.0503828029559665, -7.591798854832744, -8.06246715675167, + -7.178062400973902, -9.768878866076495, -6.917516203514602, + -5.665418470446385, -7.260143611855673, -6.937390775844807, + -7.687607497719584, -7.3485921408206325, -9.2384661090352, + -9.464847655623185, -7.897922355187247, + }, + { + -6.774920173861653, -5.204290861824316, -5.43566671911157, + -6.296930641076284, -6.565626027570306, -6.7373590105822805, + -6.991252066666452, -7.063233360250443, -7.2676834795551315, + -7.1966044322333405, -2.083909994196951, -4.067585265323549, + -5.182321460673428, -4.835732283228186, -1.980079058156674, + -5.0283663440075825, -5.072641431371989, -4.6392056435133515, + -2.7086094993416667, -6.770304135451181, -5.008181972697398, + -1.1019554288598505, -3.2594369265598493, -5.6765883311043766, + -2.736784393019736, -3.364824539297133, -7.918041582638827, + -5.789527217909832, -3.4282422713486094, -4.498435097687583, + -3.6322465613937016, -5.355734234640129, -5.0804532195051415, + -7.17597860187687, -4.9759313787901425, -6.718299350460867, + -6.452973839496266, -6.266241964550636, -6.756169624516277, + -6.43275589071587, -7.149632742116933, -6.722481373881418, + -6.632646871978412, -6.5830057991087605, -7.441299494170494, + -7.5325638154552745, -7.295254585596671, -7.196283868037615, + -6.683920964952739, -7.4835386813502325, -7.587428922371031, + -6.992558402580507, -9.54477866233574, -7.0691430439756715, + -6.0008086042221205, -6.0079129112663265, -8.273962947039259, + -7.457398913104675, -7.183861348039058, -9.235518989239411, + -9.247910721534573, -8.527701618931673, + }, + { + -6.725963719695806, -4.710012269685353, -4.981543035572536, + -5.7693914443296395, -5.741238652017211, -6.34104225410854, + -6.461701921452123, -6.616580803658851, -7.050213688876461, + -6.932988782385651, -2.7442254377569215, -3.902092293780571, + -4.081087684032349, -1.8459531867558991, -2.260196124051401, + -3.8113055093318327, -2.1904142607255017, -3.5077174805325915, + -3.070625904070985, -5.688404814159279, -3.676818033328958, + -3.705969932950152, -4.175966474127823, -3.555777846153588, + -3.6599169276373167, -4.233380646142029, -7.565625193952616, + -4.758247211588438, -2.668505149650637, -2.593317887541856, + -3.982238613532973, -4.922572014698711, -4.457568202222798, + -6.922768742308341, -4.874097132395777, -3.7366670217502587, + -6.099051002533345, -5.950541812233132, -6.754321355464054, + -6.000835251677606, -6.818602573551164, -6.538250446625281, + -6.5136752899401404, -5.7389354920585625, -6.837502673710269, + -7.922940958163197, -6.4879709082904045, -6.559235554477551, + -6.415308634074, -6.963830229106114, -7.464928001069955, + -6.466545899251641, -9.489635645899149, -6.3635319575989815, + -5.527598474760337, -6.407003965795449, -7.463914829757348, + -7.418626873140783, -6.4607730713017455, -8.850715648219035, + -9.544876513609825, -7.984492491801492, + }, + { + -7.251231594323855, -5.21374696908488, -5.698223064924641, + -6.1375976604763816, -6.322511629217009, -6.768362468268195, + -6.494106816635844, -6.457670861321346, -6.860741199932812, + -6.755693520691695, -4.0580333694338515, -3.9411669781297105, + -3.7467312772977017, -3.0227837077197126, -3.61529958808048, + -3.574469853485688, -3.2007714947116837, -4.080705573827712, + -4.670471138732819, -5.7966968651863855, -4.155602316322404, + -2.9527040483599185, -2.862751521261682, -2.037725742388079, + -3.7818488691194445, -3.113228390392548, -8.164264994327704, + -1.8458475827404217, -2.5467378386179824, -2.7020967605017185, + -3.188124830525815, -3.6799312814828857, -3.4909044267832696, + -5.197365837878935, -5.202188998354364, -5.870321468395694, + -7.4721345906728445, -7.508740887837122, -7.308294052687071, + -7.524695844262695, -8.528908107915612, -7.92499206108361, + -7.684820971302881, -8.246116020758024, -8.52404191826444, + -9.16793672064164, -7.835273241415741, -7.178620119286367, + -7.559608480967977, -8.499112535221915, -8.875184344633446, + -7.775193695995215, -10.198665810583236, -8.01769231637253, + -6.730988787795164, -7.673144441641045, -9.323628945401888, + -8.330457169191774, -8.013022138590646, -9.77099654516406, + -9.908396414566504, -9.416350685452109, + }, + { + -6.383306695601549, -4.903894061372386, -5.294037540593016, + -5.450500954330478, -5.333223053427145, -6.308171725769449, + -6.710407117707228, -6.887542628987831, -6.847572675445971, + -7.035457528925254, -2.2105034037003235, -5.0202198363584465, + -4.228806005807516, -4.2610399361561155, -2.630714925229401, + -3.899363165669206, -4.911168632565807, -1.9598915311908123, + -2.357698156549484, -6.874278408705108, -5.4856719078268386, + -2.9453898821076563, -4.76865138002776, -5.849682782165729, + -2.1582691520417923, -3.13711767038794, -7.932068702852963, + -2.1341546758789423, -3.3759594869296796, -3.525124340216131, + -3.4812218611704324, -5.083766155674364, -5.7936328268294535, + -4.353576622920111, -5.907706290260866, -6.135534326871947, + -6.846532994187875, -4.609063889576182, -7.503287789431182, + -7.483445868632177, -7.2287173521187755, -7.853210127940644, + -7.155334806184752, -7.407188463381393, -8.090673733029602, + -9.081164896953064, -7.857960730699241, -7.896800564015505, + -7.262268886290009, -8.128645226782451, -8.177248142566354, + -7.485412437604219, -9.720755973044048, -7.9739596445621235, + -6.796539470357211, -6.907958941482774, -8.646989682962468, + -8.283628546124925, -7.797896489956591, -6.6686665609139695, + -9.771081056931564, -8.750720965163202, + }, + { + -4.685181991517679, -3.1394221383869034, -4.505733999919456, + -4.7128201143378075, -4.721709061755053, -4.800636320344724, + -4.867216645426291, -4.85864761270119, -4.820149134568306, + -4.881081685563463, -3.6610088191091923, -4.523883318425133, + -4.405422178721609, -4.511747247142908, -4.4973757423052545, + -4.410859930021738, -4.692381446660533, -3.970040309575452, + -4.061847858828575, -4.724689689893191, -4.678033998252392, + -4.350556098635668, -4.167208374486401, -4.450909776342023, + -4.644417387453407, -3.724277324458299, -4.630737283549326, + -4.509337607422754, -3.787718752781497, -4.309323510802315, + -0.6823718671879156, -4.449775346271411, -4.306372205167735, + -4.410859930021738, -4.920232593125288, -4.3914208729895154, + -6.478377211171838, -6.487035273914953, -6.801150603434889, + -6.531487036485786, -6.875258575588611, -6.039010551387992, + -6.9553012832621475, -5.362670489144429, -7.042312660251777, + -7.027274782887236, -6.983472160228843, -6.58757650313683, + -6.732157731947938, -5.820321350423163, -6.504579583565862, + -6.710178825229162, -6.395685495326725, -6.710178825229162, + -6.559138567816296, -5.802621773323762, -6.789316145787886, + -6.8131267944816045, -6.29749346914751, -7.1889161344436525, + -7.012459697102096, -6.888161980424519, + }, + { + -6.973841065843466, -5.090695336189662, -5.341561221116127, + -6.0115634498929555, -6.202945132409144, -6.628849553394376, + -6.911669409716161, -7.0009992844957765, -6.886081347969349, + -6.711294966663227, -2.260331825727248, -3.839334077735597, + -3.9667628422059718, -3.420241689905642, -1.8735787833427253, + -4.188499103605226, -3.691171678101274, -3.8593630820739957, + -2.3524981891706576, -6.231341006564324, -3.7662373591100775, + -3.6465568924129386, -3.4963200738799767, -3.4321214183238204, + -2.593050419274572, -4.346868167488547, -7.8364379197408365, + -3.990422275679548, -2.904818352752614, -2.3714612152432646, + -3.1378637759089556, -4.357037863124861, -4.651314292504279, + -6.760867254793699, -4.107313187039242, -4.3976920060255535, + -6.440759053046417, -6.465477098310146, -6.582182413654962, + -6.610599862587676, -6.978263755093254, -6.719449335464573, + -6.730879435939417, -6.514035039942995, -7.843934206718764, + -7.66305587798569, -6.780807453646412, -6.810345466083276, + -6.347157031161333, -7.321885120582923, -7.779113558339937, + -6.602957069625781, -9.658525717989464, -6.893034874465941, + -5.640541915214837, -6.61808064554899, -7.825297811959789, + -7.623352248005446, -6.729273803531229, -8.625119353004427, + -9.394209194493895, -8.033505455437194, + }, + { + -5.604855175474509, -4.462285490693087, -4.571350651692403, + -5.056493889833258, -5.072440143885144, -5.681064638387167, + -6.280536665394942, -5.74786868753166, -6.300402104581103, + -6.443541857970619, -3.08781816164275, -3.6971443171127936, + -2.396573090959646, -4.216785762291264, -2.055069601332964, + -4.2400357108180975, -4.510301966657801, -2.8725223515174108, + -2.985980079879496, -6.151031964062443, -4.17853136290412, + -3.739735408911567, -4.177743865892257, -4.758224336321508, + -3.406169247786832, -2.7407038589797272, -6.9336687884910315, + -4.884332887441078, -2.4403221799860293, -1.8263508274951405, + -3.4054696763623764, -4.562592776233452, -4.420217843796468, + -6.782722037918705, -4.9372619598181355, -5.457485531187882, + -6.256287610141627, -6.611108985431037, -6.565907361549403, + -6.49534726380481, -7.061567128112425, -6.599672447239019, + -6.7013251912547664, -6.80746927442344, -7.516987128232732, + -8.06324629910308, -6.809992050813993, -6.860502751231873, + -6.646463246903641, -7.480934599971211, -7.54322475844959, + -6.575185095427639, -8.985698479921282, -6.994899040702809, + -5.767168821265772, -6.530694737719788, -7.839611467995152, + -7.435063610819411, -7.091053743823944, -8.707954377459634, + -9.10781723001913, -8.370947238445352, + }, + { + -6.890344120648169, -5.02940421086846, -5.408867487897182, + -5.967857793669728, -6.047653312106489, -6.46237897058548, + -6.749954722267399, -6.888388607925276, -6.98955227182629, + -7.21719797180092, -2.4868466816371, -4.771074471877903, + -3.6481121610473157, -4.664656454469179, -1.6817234068168874, + -4.586866350603287, -4.845552155508254, -3.154508356873059, + -2.57905575846229, -6.254276196451527, -4.921506816489567, + -4.4705470709445265, -1.6402347840968394, -5.1280286653720175, + -2.882844123251368, -4.595878517780293, -8.231899529453244, + -3.131328577959143, -2.841674626039629, -3.3059284308174086, + -3.1776616460738145, -4.478455963747706, -4.881954395151054, + -7.651052083171771, -4.569886902888001, -4.27044926534142, + -6.859624364774909, -6.620591583748609, -7.035724951332805, + -7.0226528697654516, -7.663432064808081, -6.880725735179707, + -7.096836038369936, -7.001238775261635, -7.94609099384926, + -8.742881147291776, -7.2506609077408095, -7.3153280695559095, + -7.053568548019118, -7.728589206441718, -8.033869830584925, + -6.95515998225557, -10.250083546611185, -7.317576103473792, + -6.34314386192865, -6.83930777142871, -8.443181097280567, + -7.8493114938202, -7.476614929901965, -9.60010853791645, + -8.54213954241637, -8.677952686589895, + }, + { + -7.97530600380511, -6.415691397743754, -6.861539964259856, + -7.354178676035024, -7.5544322703441, -7.956843940965374, + -8.00285595557334, -7.941715059369074, -7.997284910523884, + -7.042078762400056, -4.593656623473984, -3.4358427231129607, + -2.813804032113097, -3.8443096208464707, -2.253236786554918, + -3.523372060796657, -3.771274119941123, -4.213005093082092, + -4.26676474950711, -6.604661414352346, -4.071781743539046, + -3.0636392294004997, -2.748766550188664, -1.5979085155684218, + -6.096125476162904, -3.8319741165296572, -8.437883856353995, + -2.362166563210972, -2.079080404566457, -2.423752746988421, + -6.1373971409737065, -6.112261605870961, -6.033619491127531, + -6.187046440930159, -5.880551407088361, -5.508879098944167, + -8.771401153617497, -8.582218506443597, -8.081702252336468, + -7.820268405959624, -9.01805471242806, -8.991966276343762, + -8.037776271878622, -8.339328192194554, -9.20125771484982, + -9.932145223392613, -8.711282886564021, -8.633361864553784, + -8.75433589306331, -8.724333642759511, -9.49070501282617, + -8.37573721938598, -10.194509487860104, -8.552742688310644, + -7.968342726152964, -7.956843940965374, -9.863152351905661, + -9.598817643450872, -9.08877973142313, -9.278218755985948, + -10.729432663205156, -9.20125771484982, + }, + { + -5.1527222081970026, -5.012025127950751, -5.39388426501389, + -6.217687012129963, -6.355002880531065, -5.6351154829069525, + -6.363993950090922, -6.903492332891861, -6.853964796193012, + -7.129436704737862, -2.71361594954537, -4.917813116205636, + -4.909342477322127, -4.834757647401019, -1.3606202815267094, + -5.049186428683174, -4.831934627067998, -4.02878090419557, + -0.9857025802248848, -6.9583248115172545, -5.928056254423655, + -5.1548164498001166, -5.532591229650075, -5.575217341325883, + -2.1159432194090155, -4.2897086796604516, -7.362881241689201, + -5.200780422733714, -4.007732553855012, -4.899317914996637, + -5.732412626945404, -5.409409253401815, -4.570479874572386, + -6.004563148213606, -4.806034206660294, -6.602559371961363, + -8.008146936015933, -8.264296783798311, -8.114951495245993, + -7.067733915701082, -8.653122572902513, -8.40456220860418, + -8.35558820400772, -8.362968311305343, -9.101577865942044, + -9.242891399008622, -8.693532111240389, -8.468328826963168, + -8.348262163915647, -8.71436619814323, -8.123659823137777, + -7.872345394856871, -9.1093600063841, -8.693532111240389, + -7.279281672853908, -7.536521246398329, -8.924437667890087, + -8.99854564004381, -8.061492916721225, -8.91796515338447, + -7.579574252897618, -8.90514446495541, + }, + { + -6.625065979493934, -4.788580474517243, -5.256407824340383, + -5.37832984858721, -5.2038978790684185, -6.079823689638168, + -6.317086566978479, -6.646320764762257, -6.72855886299923, + -6.886116701148103, -1.505097000684665, -4.82588581184553, + -5.253602398681012, -4.989577089424305, -1.5758741362397704, + -5.185784176269271, -4.916501452893276, -3.8159863951228723, + -1.6041923545980379, -5.633841418592661, -5.7199839934992545, + -5.2161585690547705, -5.369003667873634, -3.3721189713516244, + -2.661522576130661, -4.670108701549678, -7.227790153984301, + -5.183916064386551, -2.494756625753895, -3.869077211017653, + -4.27104038948766, -6.0954898063825675, -4.71167529774246, + -7.136943681210728, -5.987593808088792, -6.590446143960144, + -7.403791666759668, -6.814987319540992, -7.030531087602099, + -7.451894781778061, -8.262224216961, -7.8389014914528286, + -7.199379152151521, -7.871278420177289, -7.002535480112671, + -8.630549778119708, -8.06204504276704, -7.169938203345767, + -7.199379152151521, -8.169374062997537, -8.264930578558742, + -7.090662928646722, -9.550754409315, -8.033689817011913, + -7.078178371984477, -7.674437552058881, -7.921898411023797, + -8.116604027017473, -8.273093889197904, -9.39660372948774, + -7.831846788472939, -9.063739434242727, + }, + { + -5.33959692367457, -3.901875433911095, -3.3263523840890916, + -4.682859878096868, -5.0569946532038434, -4.833678156338227, + -5.439237690659609, -5.4278954140556746, -5.662170026427569, + -5.462315637942154, -3.751806551579704, -4.660118258928943, + -3.8333548860836233, -3.8811153084238335, -3.4958051088141735, + -4.553750303632283, -4.534932311997922, -1.8458046740483725, + -3.0281790617647277, -4.436160927561686, -4.943995470762378, + -4.549402848736927, -3.9218705969950047, -5.328568099202016, + -4.452091297246621, -1.4302390343932077, -6.080922600826194, + -5.1148485422425125, -3.3219189169449392, -2.621905648275213, + -4.3789508674983555, -4.262632175345694, -5.088920047216344, + -3.3849839728869298, -3.307281062205608, -5.974439120423745, + -4.379829216259262, -6.791884017661267, -6.8166991867809905, + -6.068955964208674, -7.1565271312491765, -6.683586642330131, + -6.801736314104279, -6.532793078538745, -7.518256845849533, + -8.152370494825284, -7.391505140210389, -6.7194113688482755, + -6.416271872702784, -7.279027156783699, -6.4871905993829495, + -6.66039719741122, -8.204669994228134, -6.878895394650897, + -6.094660190607558, -6.427558674237414, -8.084652320770335, + -7.339319387039819, -7.347830076707727, -6.950948712290955, + -8.303341521735163, -7.468823387994359, + }, + { + -5.431835629645527, -3.900968951139421, -4.151004765492447, + -4.646528675532386, -4.521804796114552, -5.283128043243618, + -5.29007437093391, -5.66335521876192, -5.740643416211578, + -5.366660147370001, -2.670098748915231, -3.286242237340954, + -3.3585141055181613, -3.755602707715123, -2.5586532961090027, + -3.6926656242369105, -3.8330192649391925, -3.254475467118251, + -3.8290307266838655, -5.019290301102854, -4.363753678748955, + -2.9684754808494547, -3.151618081678543, -3.50657051212598, + -2.895083629045828, -2.91665360144011, -6.285849431998168, + -3.6228229122699127, -2.152751777383897, -3.3162167836347605, + -4.2488413058475105, -4.652746019284857, -3.9288228848114928, + -5.349158624766704, -5.572231075081914, -5.261791793324819, + -5.624778734167247, -5.278347157643276, -5.3043854022704995, + -5.210237912616575, -6.346428699255328, -5.93048158286466, + -5.615084400228674, -5.89255288484716, -5.916841563358977, + -6.777270925569916, -6.237681480853062, -5.483828321441287, + -5.599254877723378, -5.735607736245962, -6.332645938959907, + -5.443956990177872, -7.787390135984574, -5.974679054270741, + -4.487700164671342, -5.484479363130949, -7.787390135984574, + -6.624646575217306, -5.915838554247808, -7.256570295689133, + -7.872330812633658, -7.3300889926342885, + }, + { + -5.8962959205599565, -5.022482812723318, -5.262967235210922, + -5.638331471894197, -5.778457744433211, -6.3109845765456285, + -6.29163873510587, -6.56832765910669, -6.49255510063436, + -6.3650100589758205, -2.863492599093995, -4.089024629780227, + -5.154949557468251, -4.484159695248834, -0.9343644420039627, + -4.498161263246394, -4.6279862283262885, -3.982370058053253, + -2.2035282537140866, -6.199482179547703, -4.571665803362194, + -4.084960905072187, -4.755413554835734, -4.800997636581884, + -3.6971982708382995, -4.566795129611478, -6.871351961660363, + -5.099526707333125, -4.135788994836005, -3.1706024468180507, + -2.212641008744242, -5.078715683958774, -3.5811841558979225, + -6.741940888008861, -4.856098125468122, -3.980907107020626, + -6.712325967469931, -6.644006723492454, -7.153784678226001, + -6.945055157447498, -6.898020208742524, -7.021563043483541, + -7.173168545047049, -6.854391346353318, -7.669859657878135, + -8.284314569862415, -6.895321151773359, -7.513205847832758, + -6.795366054682441, -7.556635405760094, -7.827916592672458, + -7.241272132349116, -8.35137880044296, -7.187505708193456, + -6.1188193168155305, -7.139919638088829, -8.075324765986299, + -7.379987004046535, -7.364785842714484, -8.480789874094464, + -8.527936652520165, -7.717773013574032, + }, + { + -5.1596391409467905, -4.446625879300866, -4.792661465824471, + -5.092676376197713, -3.486026851337387, -5.293817992318437, + -5.4207097441730605, -5.031624556083574, -5.433394903700377, + -5.123410202650332, -4.715036303793935, -3.7021536789683007, + -3.7870302329121537, -4.088809455118969, -4.812132309601868, + -4.898021981482298, -4.704305701448848, -5.045386241156256, + -4.694501701352228, -7.439823297251669, -3.3979941838940024, + -2.7387677229303073, -3.4751598040428164, -2.0375451283648167, + -7.179408351140228, -3.811103778633784, -5.971540702552751, + -2.9729698958508197, -3.825149191015764, -4.478865937874288, + -2.6641628726801234, -5.3193066285353545, -5.996634270150407, + -6.375549146164451, -6.785390628872414, -6.572829186888741, + -4.124487206195906, -4.535138151336436, -3.7135200209337587, + -3.96463850525401, -4.554386339641352, -4.765674647825141, + -4.429638997109137, -5.277664924510665, -5.02086262836912, + -5.937153360243275, -4.7565501004863675, -3.693750268171921, + -3.749678820831146, -3.176252392796863, -5.722496422806384, + -4.546642548744449, -5.417354036326088, -3.1803577086920454, + -3.749362815140414, -4.066652137558128, -4.660532401294013, + -5.1570534898617675, -5.430844966067104, -5.284241491875228, + -5.5748604240003194, -5.63236491848051, + }, + { + -2.1543734650260515, -4.349659150265929, -4.518901599180162, + -4.334150339570116, -5.146931928181808, -4.726677895862385, + -3.8738862022041083, -5.547738126032732, -4.965611539500847, + -5.329386916158828, -2.0510193107742145, -7.017067715762613, + -6.5514024881154755, -6.782731624016124, -2.1387030138865364, + -6.887331394106641, -7.26098839501858, -5.915573580719614, + -2.792353319034718, -6.475549145597215, -8.196805549243091, + -2.7290425937620233, -6.57338139483425, -7.482751683863832, + -2.7069370493135643, -6.6217549791184664, -7.959675755954141, + -2.6929063910157374, -6.402701413055781, -7.378081061299943, + -3.038722067403394, -7.146279447242619, -7.442205589469482, + -7.777354199160186, -5.436617340201765, -7.9268859331311505, + -4.21410095816366, -3.7159537926574395, -3.274052690014408, + -3.8709022387814955, -3.903841048493916, -4.907248138723605, + -5.564815386850924, -5.634025390029099, -4.794669640998671, + -6.37044055083756, -5.773624479216047, -4.984713243886536, + -4.705003878814023, -5.884409585684786, -4.869643914101749, + -5.8105008762596295, -6.426399204485604, -4.552569937605819, + -4.688840192155229, -5.1496039404596194, -5.387063525747035, + -5.940338138344011, -5.40158748887255, -6.8282736444630405, + -6.302506440698851, -6.49077786629904, + }, + { + -5.042027972011844, -4.299718672656642, -4.243324982365076, + -1.9825142998026875, -4.725280240738409, -4.751810055033314, + -5.528415080508104, -5.372269658882421, -5.58057814379269, + -5.634199235231788, -2.092585085927561, -5.359310514239916, + -4.924244936355461, -6.182928172882705, -4.358300013368391, + -5.644135441890917, -5.3193472574280785, -2.4216711986248063, + -3.5454438576615357, -6.6553797837670805, -6.406683158028254, + -3.42063060974259, -6.230496589801815, -5.313180742512414, + -2.1141313157147192, -6.38251279710044, -7.199431055161392, + -3.210947634389801, -4.823896962521179, -5.599497627038855, + -3.7500872171087103, -6.903338504201014, -6.960201366095558, + -7.542682978767983, -5.613234106766742, -6.36476085164199, + -3.6714819678600925, -4.875454772186028, -4.20471280907303, + -3.884017303250544, -3.4828969984685907, -4.20471280907303, + -5.449056802096191, -3.411103801364231, -4.476181634325785, + -6.299489461288767, -4.386290645201218, -5.042027972011844, + -4.789932598747867, -5.333885415874105, -4.1803254304220925, + -4.950988261330972, -6.251222720318932, -4.73156059252596, + -4.355150404465494, -4.750641830601388, -5.174666905853347, + -5.78646844695934, -5.852762894799176, -6.180486169227153, + -6.353098911894147, -6.096324376905443, + }, + { + -3.621153540136533, -3.927545190670303, -4.761298021301781, + -5.2327070159671, -5.390836480884121, -5.489330016767771, + -5.419938023343771, -5.638503085521277, -4.333907884333748, + -4.5925613698517225, -2.3774699118392917, -7.240027999198052, + -5.856199730573355, -5.309006462636489, -2.1188535037876086, + -6.988713570917146, -7.259929153515347, -5.733217613565709, + -2.1988937611323722, -6.914879308906094, -7.285375819176511, + -7.471829613255376, -6.297483936486472, -6.369085087302357, + -3.0144535413397078, -6.131611710433321, -8.510182710405289, + -3.290870578196505, -5.79050447130796, -7.191941812530414, + -3.661034886200146, -5.933160771709483, -6.992552347224311, + -6.430741168725453, -6.006448567479316, -7.6405790925037875, + -3.820123280601132, -4.60874181430768, -4.552214016916188, + -4.152125265844404, -1.8440074384079603, -4.33229280995845, + -4.748982594711727, -5.590043992369601, -4.083551769494654, + -5.885005729822436, -5.570371226770897, -4.35431953325285, + -5.037889407059404, -5.600505333614971, -5.2234842891983275, + -5.239347146606377, -6.012203979049936, -3.2506791222758458, + -3.493367719618491, -4.386449689183688, -4.5929103490909995, + -3.6635121789617022, -5.259536257638402, -6.071656848963408, + -5.913358144413303, -6.637031915632404, + }, + { + -2.697794466436189, -3.604662393389068, -4.011263957500705, + -4.728080882146555, -4.781372179196779, -5.33155887684335, + -5.439355447299057, -5.586922158039964, -5.370633615250003, + -5.513150772897161, -4.398260316869362, -5.11207163673225, + -4.190082883743458, -3.4501177344839546, -6.597640067453566, + -5.490434815062624, -5.150911470048515, -4.837107457567751, + -2.85030853825846, -7.320346050255055, -5.997701716211621, + -3.4753179136511876, -4.2866759373606715, -3.397053452568678, + -6.703360230660674, -4.113003141200578, -6.444877312901155, + -3.074043686998346, -3.452623303723489, -5.193702907090925, + -3.9327969395172757, -4.131242383720524, -5.418238523858134, + -3.7478131640115406, -6.184652086682414, -7.063123185287982, + -3.7394230709797665, -3.4179694218787144, -4.267288710081795, + -3.0709940324857143, -4.263518677341253, -4.701005637323521, + -4.417094629495229, -4.569236359692398, -4.473887479448818, + -5.8432973258667005, -4.728080882146555, -3.754953367214181, + -4.1263095154005, -3.415012032977706, -4.81618158386264, + -4.691820172692138, -5.666572324000903, -2.795735372125381, + -3.2437129699651224, -3.9019638438387987, -3.582993942690341, + -5.112804505947904, -4.894364079117085, -4.722110715160052, + -6.056219323109372, -5.5022692727096265, + }, + { + -4.619141531251806, -4.242478414047348, -4.655112011634125, + -5.260049492712331, -5.018630839221239, -5.434973330528597, + -5.291682300504695, -5.652426966065128, -4.133100006910822, + -4.834429582625707, -1.998550183209591, -5.7433143168994505, + -6.845817661060527, -6.562531200081784, -2.4549848852751466, + -6.626189051853761, -6.290387731057782, -5.567486289416045, + -2.141146196531367, -7.242549089199937, -7.104679294976816, + -2.8517566389677937, -5.192894636686687, -7.082206439124757, + -2.3092018462234853, -6.71448165899944, -8.017189304011065, + -2.1705324264442973, -5.663122255181876, -6.392769837961348, + -2.584044215625091, -7.28700085177077, -6.816997222525035, + -7.454881724409931, -7.498844847831047, -6.3210545294507545, + -3.9234738704665255, -5.210974225191004, -4.156261002958894, + -4.841496749848799, -4.597299789336757, -4.421170202790724, + -4.975365923256807, -5.672123216040852, -4.474317672329112, + -6.090327349926446, -5.367408011032831, -4.491383872387231, + -5.280618880960439, -5.815780620152271, -4.640826054386648, + -5.541761398177608, -6.307308608546119, -4.360911011272527, + -3.968855752517438, -5.060904184340462, -5.092811178129705, + -5.9935197902566175, -5.119506808592894, -5.086750553518014, + -6.052587021943599, -5.38489332537385, + }, + { + -5.261046281621696, -4.9148297535258045, -5.093521542269181, + -5.160962823064713, -5.492794680129424, -5.583178741597693, + -5.608843109973599, -5.828958322395465, -5.818217080564052, + -5.6351835265880075, -1.9431063436877503, -5.762127613913009, + -6.364566555601998, -6.310693565661846, -1.465488331318766, + -6.653479397439911, -6.983078754773362, -4.701955198551832, + -2.920630198813372, -7.381717897811127, -7.114655112562081, + -3.628618850962897, -4.741931970713727, -6.050483313874263, + -2.961525855445553, -6.683481647743711, -7.823550650090166, + -2.1208799042866553, -6.236585593508124, -6.735576759627112, + -3.3232120191551315, -6.891511561247872, -6.08336298799342, + -7.9413336857465495, -5.554867108771802, -7.122498290023107, + -4.193090909121505, -3.901105949199606, -4.152689701478908, + -4.935827900365578, -3.224590180736388, -5.347946392964479, + -5.205112607677643, -4.550306897288771, -4.485129674572183, + -6.303724896345753, -5.654950567328784, -4.928779413034516, + -5.0770940681272325, -5.30867433061095, -4.570963767879653, + -4.7737511552658995, -6.146602446129337, -3.6472116840395126, + -4.583317229489316, -4.247999881283233, -5.050961927850385, + -5.554867108771802, -5.239253651630843, -5.752077278059507, + -6.0889495947020595, -6.114482896707225, + }, + { + -4.8357984388643835, -4.657872493543467, -5.438431611883497, + -5.481947249319659, -5.480513557317811, -5.739017089171186, + -5.670890393572083, -5.859121927588727, -6.047318448825703, + -5.930658377913943, -1.7234534048021994, -6.808376835001858, + -7.018097365983928, -6.515303913915201, -2.0800327647225068, + -6.985307543160936, -6.434021280478344, -5.690138581877, + -3.0546210789668655, -7.433612809945593, -7.609892052283585, + -6.846949109788098, -7.145930737493813, -6.771237288052402, + -1.3242305561151027, -6.88123818326673, -7.934388097858083, + -6.599387031125742, -6.8029859863669815, -6.569147145936024, + -2.3927324727103167, -7.200418922777882, -7.33738477785104, + -7.62201341281593, -4.893045313052039, -5.366877919534872, + -4.1899951004809015, -5.453656819480563, -4.694403309779647, + -3.431804500645092, -4.0124147615767685, -5.525942614164559, + -5.309719505694924, -5.568828205702649, -4.124028746206436, + -6.415604553692113, -5.4275025413513065, -4.984699762805498, + -5.237511197353998, -5.157641365469638, -4.101047614464405, + -5.085610237260755, -5.550223017871613, -4.869440755282619, + -4.489173830779152, -4.234321454353582, -5.041242413079193, + -5.780689785571754, -5.231908941805328, -5.964947451392574, + -5.752089170738539, -6.139854701747302, + }, + { + -5.508465007700513, -4.839258034686576, -5.109472414097966, + -5.519275923804729, -5.707703038161169, -5.439902336263216, + -5.7235763873174585, -5.821556795677663, -5.734300350680434, + -5.610247702010455, -5.910709501108635, -5.7235763873174585, + -4.435262434557772, -3.8610478472011964, -5.758858201462099, + -6.0632537548876195, -6.044700346991872, -4.117878794499082, + -6.575328598054043, -7.243783166023616, -6.403478341127383, + -5.083829983484629, -3.317712944869911, -1.7163886672099804, + -5.622223893057171, -5.543479525545615, -7.373836294271814, + -4.235327303674574, -3.4757995308184078, -3.8216678856997803, + -6.863010670505823, -5.619817153026606, -5.966922645949188, + -7.096625521687328, -7.1845942946332855, -6.5629060780554855, + -3.9611313057581787, -4.164449795424729, -3.6745940531223313, + -2.809054169189011, -3.303860323066671, -4.356804692686432, + -4.075943929085389, -4.993775889109478, -3.5055329961999946, + -5.4956443192714515, -3.896020865817486, -3.604273312212492, + -3.5622476861654384, -2.789152302491872, -3.9102728885246876, + -3.0660244750660355, -5.631909198791635, -4.404134333185408, + -2.9033092297609358, -3.155554830818952, -5.387104150696246, + -4.341290047595106, -5.258585299895267, -5.109472414097966, + -5.821556795677663, -5.14024407276472, + }, + { + -4.364789554126129, -4.918384854279969, -4.918384854279969, + -5.1613310328903585, -4.587530609962979, -4.784853461655446, + -5.006160465162354, -5.0403784224726955, -4.994002260682545, + -5.402493089707247, -1.4796200682550344, -6.181000119218348, + -6.701776073837507, -6.252825853789604, -2.1508860947624866, + -6.086590434747274, -5.772240115213331, -5.482914657017821, + -3.9652464014134288, -6.426672783592586, -6.401671481387169, + -6.285261129542758, -5.890845857621178, -6.3772800282630095, + -1.9660163181458141, -6.770768945324459, -7.107241181945672, + -5.362398632505369, -5.920923312858457, -6.7530693682250575, + -2.0597919147266994, -6.701776073837507, -6.505661194911217, + -6.5476253940102485, -6.637237552699936, -6.7530693682250575, + -3.9354569646113213, -4.092932522818745, -4.814706424805127, + -4.961309898997003, -4.355174095426687, -4.949681861001884, + -4.869194610089197, -5.046727650151355, -4.8507000274530325, + -5.115948027042541, -4.814706424805127, -4.689097748317353, + -4.863875448611597, -5.043547997233975, -3.6732539774605253, + -3.9481153614832447, -5.340799520701906, -4.835115296436334, + -4.068688911208753, -5.024679512929592, -4.753487802377227, + -4.994002260682545, -5.247044551164388, -4.698046353893368, + -5.307182909902921, -5.227776132298511, + }, + { + -5.115394338583977, -4.7561420736309445, -5.007842676853897, + -5.66123448019507, -5.033665707964376, -5.645764558422938, + -5.535809351386801, -5.907471144471458, -5.828086115216893, + -6.105296887801378, -1.6832402388074508, -7.437524027650993, + -5.590313253886724, -6.862982589498895, -3.0491034044747836, + -5.586172461220692, -6.632458930887062, -5.218606625703514, + -2.4710973040085813, -7.398809515470303, -7.268447697607059, + -2.7116510959031834, -6.686853002952861, -3.7188303108266303, + -1.9217106058640063, -6.098376444956805, -7.548749662761217, + -2.711068175455316, -6.088084758920257, -7.1630871819492326, + -2.4953178542046213, -6.5371487510827375, -6.531815405107375, + -6.592107635363495, -5.218606625703514, -7.464192274733154, + -4.025855346121542, -5.098759278079445, -4.812664759541027, + -5.369662433402117, -3.845996309584077, -5.031290409935469, + -4.627804324836165, -5.549657248245595, -3.554062953084761, + -6.151486270270753, -4.8426621708268565, -4.640563668589926, + -5.069989313821911, -5.128381534110789, -4.6191257693799175, + -5.164313543336852, -5.9721996171719525, -5.146851716173189, + -4.482065653234942, -4.222339216728002, -5.190396010216277, + -5.978297197040071, -4.681519433426748, -6.057830350562455, + -5.619789072019677, -5.749664153441089, + }, + { + -4.867406343870729, -4.4533740975545415, -4.953434176237852, + -5.384635998990067, -5.328952912513263, -5.6593669190947695, + -5.597013688377717, -5.757806991908022, -5.710273291442284, + -6.117674508577314, -2.0752354231922467, -5.330274789771179, + -7.239817294655618, -4.653127950557675, -1.9861891376978962, + -6.498730651806554, -6.748830013109291, -5.799220777458779, + -1.8880861157511444, -7.060476365999801, -6.592340150928861, + -6.615981913985901, -5.98705432616025, -6.055682832549346, + -2.2283490142554703, -6.244583360441343, -7.060476365999801, + -6.9543698600052535, -6.005072831662928, -6.675112437212724, + -3.324941220245065, -6.737960340872387, -6.846328377594169, + -7.276184938826493, -5.466076330930241, -7.601607339261121, + -3.2284338811241238, -4.7571452826690335, -4.2772171644143535, + -3.794375211195705, -2.9321390456478555, -4.655146114713912, + -4.556474587206883, -4.658518799192551, -3.9133691163329902, + -6.159223511490186, -4.874928318325417, -3.9851607325065794, + -4.593769151695071, -4.865742449976033, -4.192747084853396, + -4.158562847663465, -5.583291346122616, -4.6687056444995445, + -4.403457644910037, -4.135470875670939, -4.338591952168042, + -5.484425469598437, -5.10713123845697, -4.666659956776251, + -5.507845743806536, -5.56639905255811, + }, + { + -5.367738120634485, -4.778432275797196, -5.153988612512787, + -4.9880684755260525, -5.022992659762126, -5.315533041364984, + -5.937669186281741, -6.10637419308453, -5.985149724514735, + -6.0666028696576335, -1.4917500304139726, -5.449683054485573, + -5.538947192058466, -5.596026010244295, -2.1356770840200325, + -7.153812526227932, -7.094389105757131, -6.013018623523528, + -2.2775252846627545, -6.010847070010019, -7.246405313055757, + -6.755173383190167, -6.697507741340358, -5.530860894627108, + -1.9981251565724831, -5.698525293621089, -8.083802102460249, + -4.860110306811463, -5.882753428685626, -6.945748081740843, + -2.8863534391722627, -7.100819996087421, -6.962461562714583, + -7.231590227270615, -4.4224533361891325, -6.0688990819179836, + -3.292438554454217, -4.782234561746934, -4.459461169483883, + -4.359771877270199, -3.9163352754091854, -5.15214868559078, + -5.0863471755948115, -5.622152314836516, -4.041658687615878, + -6.321199217523649, -5.22160122153078, -5.082053480720112, + -4.742403534262838, -4.896452531238822, -4.577416633798337, + -4.325814840936012, -5.22258692885554, -5.570564946046022, + -3.7753199382595333, -4.751597592285044, -5.351900685535859, + -5.538947192058466, -3.6838339356894148, -5.717756655548977, + -5.9396873504379775, -6.193602560418941, + }, + { + -4.999514188504179, -4.700587505853424, -4.67022803135639, + -4.81576740495976, -4.92872993147181, -5.418517964945142, + -5.4525477135314535, -5.471965799388554, -5.601583292691941, + -5.294558257397029, -2.0675057720915833, -6.606510097282728, + -6.66295140818768, -6.268297216183731, -1.8472066532219233, + -6.801101746668497, -6.464629510047284, -5.518112402024337, + -2.6153829107633095, -5.40926720517299, -7.042937430994859, + -6.122880216325227, -4.984950824316282, -6.978988706394586, + -2.5341534752438712, -6.335738496979263, -7.22903971062872, + -5.047082605423289, -6.606510097282728, -7.195517018590076, + -3.3881221963080814, -6.764734102497622, -6.935691900641261, + -6.06802185577318, -5.518112402024337, -6.21743879895024, + -3.306166689176508, -4.5835098665078435, -4.096018906794089, + -2.7778687007936327, -3.149770331333854, -4.522505810044474, + -3.6065186143693406, -4.579485716208118, -3.9009018998360694, + -5.695109350702764, -4.592420415540891, -4.39850374434322, + -4.858371250530563, -4.687974726037726, -4.284600731462279, + -4.828050056815601, -5.7714823294873385, -4.760362534007699, + -3.6910492160852075, -3.1337486268025883, -5.114702793098268, + -5.044521784561615, -4.990993138772245, -5.21262268525524, + -5.1495981689488834, -4.939033399841534, + }, + { + -5.573013579156251, -4.7557456323553735, -4.835888625843566, + -5.4310032481645445, -5.375816832597254, -5.748275644237248, + -5.617589203744957, -5.426286258286405, -5.527732874623095, + -6.027336973645122, -5.771265162461947, -3.4363190171520683, + -4.863649827007062, -5.342810536129083, -3.9603112459777443, + -3.1741279621323484, -6.522548369609561, -4.089429329592385, + -5.667234434234122, -6.573100648772392, -5.74180312973163, + -3.6814950844272993, -4.555731535734934, -2.60141208694855, + -6.057840427938536, -2.6538446056843625, -7.3352407008192895, + -2.4633202784204524, -3.0508881488790776, -5.069846636151324, + -3.6495408996227328, -4.548858656447172, -5.7613477258046, + -6.031638055544512, -6.61856302284915, -5.764642621701453, + -4.906092884516815, -3.945751808422034, -4.075923906392578, + -4.145352412824341, -4.3284585910787134, -4.1375398730875474, + -4.386633312115669, -4.864991208831262, -5.2459274106602996, + -5.389330551763976, -4.366047982499911, -3.5236520927129136, + -3.6216686341149815, -3.0467401241562313, -4.4457511661828155, + -3.28056139498962, -5.728982441302569, -3.2282776746379773, + -3.3483163872180257, -3.6120365041642786, -4.280940073114231, + -4.240597235287379, -4.439591885822281, -4.571620648562269, + -5.13801612348307, -5.583972592945972, + }, + { + -5.648871675811755, -4.741030782968909, -5.070478143556586, + -5.038332518583169, -4.984450311189462, -5.652113169735926, + -5.878916005474925, -6.018576120040821, -5.698633185370818, + -5.924757364823166, -1.8468585970227798, -6.844251516414859, + -7.126600242891277, -6.081398705962876, -2.934905888757429, + -3.6595354362414967, -6.817864761241664, -3.5921374614243615, + -2.9623018840998165, -7.15537920744132, -5.837035508229937, + -2.956367800752568, -5.44072677540776, -6.135188880549014, + -2.35874447462609, -6.817864761241664, -7.744626792983114, + -1.829808862763535, -4.656579250518651, -7.012765100242478, + -3.346772137573546, -6.156466278996299, -6.772055225210369, + -5.459294948136628, -6.2464145156592386, -6.772055225210369, + -3.9054574859103, -5.259720143195114, -3.484916480113615, + -4.59888775210554, -4.26703708287839, -5.280773552392946, + -4.568219053541838, -5.236954932422101, -4.227597595795837, + -6.429913235626207, -5.14878310004672, -4.299244656170703, + -5.066854951187166, -5.722730736949879, -4.343868104023761, + -4.895290258966649, -5.924757364823166, -4.2366225736701635, + -3.439140235431567, -5.290903070631802, -4.913738130330258, + -5.32777360644013, -5.700335313441349, -4.118883335232663, + -6.285399962372192, -5.825384891009962, + }, + { + -4.134798672989471, -4.089847285127205, -4.1376763728170864, + -4.175860159787245, -3.5867437074551245, -4.073498147125676, + -4.247208240584712, -4.485742942219219, -4.161001045383496, + -4.76664532768562, -4.782994465687151, -5.224827217966189, + -5.79734641073752, -5.371880635922686, -5.362028339479674, + -4.805217602471861, -5.696541711615555, -5.1428140663053545, + -4.243997964954463, -5.696541711615555, -6.074978147335799, + -4.388579193765571, -4.887134724939747, -5.433124261163405, + -5.127188748402274, -5.465212575714905, -5.5681605449673475, + -3.871109027280912, -4.3522115495946965, -4.887134724939747, + -1.4812734394569864, -4.9697213159490214, -5.42265296129611, + -5.5681605449673475, -5.696541711615555, -5.592551998091507, + -3.9025017396887742, -4.303421385425264, -4.377529357578986, + -3.2036759521599856, -4.414847120586181, -4.370230055097374, + -4.283218678107745, -4.293269013961246, -4.570900750559526, + -4.639893622046477, -4.473572406598963, -3.944368319081564, + -3.9659778034144195, -4.663991173625537, -3.393956618621509, + -4.489832927470744, -3.9232159440763374, -4.221810049979101, + -3.677082874537429, -4.095356940938174, -3.0285527594662907, + -4.570900750559526, -4.348646483430199, -4.449666885745409, + -4.481669616831582, -4.21556002963393, + }, + { + -5.468543074118657, -4.3409301123745765, -4.939132602788878, + -5.030955774234888, -5.486636817561356, -5.444018684255573, + -5.476853371252285, -5.808607963491769, -5.908385471304435, + -5.666153323427702, -2.5849374499856705, -5.739356727450997, + -6.452595908093631, -6.391754248837386, -1.3630653142736813, + -7.218432822021854, -6.741508749931544, -4.321907198551311, + -2.3295168721177877, -7.554905058643067, -7.600367432719824, + -7.660265574300893, -6.068262723688894, -7.064282142194595, + -2.4089621725210324, -6.261701120589188, -7.218432822021854, + -7.400754378815808, -6.405740490812126, -6.66108718262097, + -3.202908170420881, -7.345184527660997, -7.318516280578836, + -7.17951740577218, -6.2346317986209705, -7.685266876506311, + -3.523815890500982, -5.001129001779655, -4.254551658580372, + -3.1791480369827805, -3.272238460048792, -4.979751531007288, + -4.696520976240299, -4.996816791561475, -3.7821441205484283, + -6.068262723688894, -4.767838317222632, -4.961310103104565, + -3.3169412686460493, -4.254142074302682, -3.686717843919221, + -4.781622132973809, -6.395232513213711, -4.882290151848487, + -4.035101934573064, -4.142657840794326, -4.886134831709296, + -5.154991621688459, -5.261248596393322, -5.763145589415012, + -5.307128202144016, -5.4187681732866855, + }, + { + -3.7153348719791315, -4.23835637018879, -5.401341822408315, + -5.655381048015406, -5.3230099791384085, -5.723838105173554, + -5.787159933132683, -6.191069830974041, -6.118044695959151, + -6.512733837722823, -2.85919837658876, -7.902248813542757, + -2.4061387894065027, -6.693770047296114, -2.250325724429897, + -7.350962550860164, -6.005451461342365, -3.2057646919026945, + -3.1269276063809657, -8.282234836349936, -4.929356031958588, + -4.893377430923357, -4.350779231381914, -5.039126698093967, + -3.0950598675329433, -2.702149075140166, -7.065140944061303, + -6.4656293685353505, -6.6671525062961585, -1.5532677412410396, + -3.407685858090993, -6.917395410231305, -4.818860209735889, + -8.330553413620743, -4.697144805661785, -7.44034239452964, + -4.201345166462018, -5.036551747250591, -4.447052124631202, + -4.271306064673767, -4.3875307527168115, -5.68024420720805, + -5.463555006909888, -4.6680077696105, -4.729585831829687, + -6.877248342217893, -5.7894972929813875, -4.744562286019245, + -4.863271676535759, -5.996776708148691, -5.115645847928164, + -4.782587554878788, -6.69569868820252, -5.6198664278662305, + -4.372289459396023, -3.8304137647360905, -5.143464614214886, + -5.692911512408815, -5.442384299786921, -6.144390895990383, + -6.553756817069401, -6.771887826560066, + }, + { + -5.439192275092616, -4.781176186054329, -4.766861598657258, + -5.578787193555129, -5.543563426336324, -5.577507602849504, + -5.742587353208953, -6.240425781448133, -6.11031213333427, + -6.461710020172159, -2.6665339561656087, -5.7471225083743445, + -6.597455272456702, -6.655866034613116, -2.1677366785812078, + -7.063212610820987, -6.615344837207477, -1.8542823223629723, + -2.8203019407701473, -7.921874229858505, -5.964600322152877, + -7.4190807777897785, -7.115398363991557, -7.50316389500032, + -2.434370037607787, -6.75042461723813, -8.501692725111447, + -2.436966652419961, -5.508344242912733, -7.262628600974241, + -3.3318853658660776, -6.320468489121669, -4.662752576421778, + -7.96269622437876, -4.3951216252530045, -7.773454224740232, + -3.4248833433841024, -4.6171877485771935, -4.856615893655904, + -5.148452521314832, -3.262215858586344, -5.067705520626301, + -4.995134827791466, -4.808655260848847, -4.056803079326151, + -6.372894286461519, -5.283769734998518, -4.869131701587735, + -4.356292992089062, -5.580068423711178, -4.205055407055328, + -5.300108397353307, -6.413362236014366, -4.081705328198343, + -3.5651654031689133, -4.4607321960690065, -4.914213385439241, + -3.3562768189902368, -5.301077859377855, -5.2191716350828905, + -5.66568217643417, -5.148452521314832, + }, + { + -4.692472771166889, -4.3008934499719516, -4.549181741142988, + -4.955265087292482, -5.083773336506403, -5.36724487642184, + -5.286555965171697, -5.606625988867734, -5.3561026998685985, + -5.6766361544403825, -6.310506725070788, -5.345083304618988, + -6.354631529979725, -5.436838168221035, -2.778870257014558, + -5.888394383529467, -5.518132910614282, -3.971194340356585, + -5.541198183545278, -7.353160360090853, -5.418980550821029, + -3.590537431731367, -3.3241709789128793, -1.8944436557320132, + -5.91226186493611, -3.390599447549114, -7.2559966116372046, + -3.6807421830309885, -3.742794171598555, -5.40143624117012, + -6.914247317915148, -6.268246915780905, -5.966865998970962, + -6.802329401711162, -7.18453764765506, -6.063197107909394, + -4.040088147996806, -3.4233375319614976, -2.7095546985241823, + -4.374927246403353, -3.562959147374605, -4.404434128912793, + -4.579296940419071, -4.391681565015401, -4.438151297809303, + -5.606625988867734, -4.3282656378478706, -4.1449534915722195, + -4.0950638220693705, -3.3913758451036427, -5.0164696546102965, + -4.409796072054178, -6.030044900592494, -3.187603169643488, + -2.500811269450856, -3.4121731416766146, -5.192891489277063, + -4.804034117568558, -4.273312053475689, -5.056716535118889, + -5.514880875227905, -5.219114684376165, + }, + { + -5.262101982971463, -4.751704716461968, -4.4750082149096455, + -5.006514043913175, -5.07515161250067, -5.251452255054804, + -5.285938431125974, -5.392274080942006, -5.414469813333791, + -5.840086736879263, -2.606359904105076, -6.823463761969788, + -6.89757173412351, -5.234645136738424, -1.8016665052081617, + -5.931967688708849, -5.7146991018376605, -5.437169400849897, + -1.672652532530341, -6.72630001351614, -6.773702252410724, + -5.308112288620296, -6.00575385876808, -6.6810434219280195, + -2.3521748840468923, -5.96169386897405, -7.419447194076086, + -6.095225261598572, -5.008173795331539, -6.397795946544104, + -5.514028406375509, -5.907173030095633, -6.070893160939042, + -4.572171963363293, -6.91979487090822, -6.823463761969788, + -3.666029218824578, -4.169934399745995, -4.315857524665681, + -2.6071122929751187, -3.822642418908326, -5.326212330263914, + -4.3600448319184775, -4.917707706137853, -3.8075287810982776, + -5.953110125282659, -5.187841070540621, -4.792366055507543, + -4.8403533292283845, -5.164264339976624, -4.320022461964966, + -3.8952676954062846, -5.745470760504414, -4.036941945426083, + -4.228361366337563, -4.708930900478682, -5.247223918945283, + -5.006514043913175, -3.8952676954062846, -4.574322501826521, + -5.886970322778113, -5.748949024880739, + }, + { + -5.49714261021104, -4.668772759709356, -4.7793028170607235, + -5.269011517385791, -5.403728165571614, -5.600190986110487, + -5.607143505425369, -5.736034518493389, -5.749350294469161, + -5.980569259788917, -1.8463761496649909, -6.7957313909118255, + -6.842614976810675, -6.277301167760615, -1.6016725181239624, + -6.834646807161499, -6.483542908272222, -3.3781298559126016, + -1.9147238137262874, -7.264209466848723, -6.97044834832056, + -6.2503937148406905, -6.323821183395508, -6.466922027036182, + -2.304012035094697, -6.232849405189781, -7.182292344380837, + -4.500040235600472, -6.194465162181466, -7.045955900828706, + -3.7335540179496816, -6.633976111699347, -6.224191342446667, + -7.560054849939665, -6.541029999189903, -7.7391030813886506, + -3.9953827115751284, -5.225208894727398, -4.2623981472183505, + -4.983820106246462, -3.5900722944719905, -4.880831478599072, + -4.625152137233465, -5.350160419862394, -4.464551422640724, + -6.03257404477923, -5.25911044640308, -4.290672925686517, + -4.802954305142684, -5.287422079228971, -4.798800612773991, + -4.757191363453292, -5.977196575310277, -4.844383490556383, + -2.946883968159507, -4.887603513509017, -5.795997983260946, + -5.6188395451885595, -4.719156549714065, -5.921535728204724, + -6.1778672707724285, -5.694577804815043, + }, + { + -4.469097220436953, -3.6430767584970343, -3.7844067498952314, + -3.931680914923784, -4.272838155799217, -4.129621978555971, + -4.297357772973536, -4.384369149963165, -4.338992745625113, + -4.178787025578902, -4.976420213651742, -4.292052720743842, + -5.461928029433443, -5.887479896108345, -4.57562801124297, + -5.526466550571014, -6.262964172004573, -4.4462034005711, + -5.238784478119233, -6.017291507630335, -6.355745905455539, + -5.968972930359527, -5.520424236115051, -6.328346931267425, + -5.70515833931439, -4.218953067304238, -6.6434279779073195, + -5.648805402763258, -5.5635078222513625, -4.870360641691417, + -6.412904319295488, -1.949196049230905, -5.968972930359527, + -4.4990139814749455, -6.027241838483503, -6.442757282445169, + -4.057336183871974, -4.448263133534111, -4.000410247075965, + -3.7940068236242506, -4.150834895195163, -4.166267300233975, + -4.566303934367847, -4.3901887590164295, -4.028468199871122, + -4.376661982718227, -4.915384323065373, -3.014652447863089, + -3.369082406994758, -4.61381880997536, -4.55706599338291, + -3.5814225774035497, -4.577972678202224, -4.001728638829222, + -3.270401473211861, -3.264703452097223, -3.9718368746319084, + -3.132988733879366, -4.361423592613295, -2.595191993338583, + -4.516519898988561, -4.912100247864183, + }, + { + -3.9699474093476774, -4.189042372135887, -4.058989243887689, + -4.102030995146257, -4.171520021443684, -4.318254103615893, + -4.424253392850214, -4.758810531535327, -4.504029895277935, + -4.414714369803455, -3.0879080140350674, -5.469976217597951, + -3.790334046490602, -5.366435538657111, -3.235086137375257, + -5.727805326900051, -5.408282648592611, -4.971959552124542, + -5.070590155566169, -5.076706382583605, -6.181472536826093, + -5.921961341341008, -4.9999725883522155, -5.5070174892783, + -1.5568628976878147, -5.5070174892783, -6.110013572843948, + -4.0678979887767985, -5.089052218405905, -5.200643283814367, + -3.6661684098537215, -4.557328477002297, -5.479108701161223, + -5.159821289294111, -5.479108701161223, -5.616579691789827, + -3.7152580200502454, -4.23556238777078, -4.212031890360586, + -3.866007780968386, -4.0678979887767985, -4.4633463186414915, + -4.142178063159669, -4.318254103615893, -4.276053749125516, + -4.772293881872614, -4.332726136224427, -4.11133338780857, + -3.9089648990693253, -4.309670359924501, -3.9127889955077286, + -4.338574106106851, -4.528549512452254, -4.149433234040841, + -3.5292744136497762, -3.6009751629760713, -4.350373653038005, + -4.828122331425556, -4.047963773875981, -4.665125047458004, + -4.673288358097166, -4.633122316371831, + }, + { + -4.806719860900339, -3.6507473420444714, -4.429814962058902, + -4.347330952102998, -4.3949170222076255, -4.698366552912107, + -4.809448375553543, -4.35423944244681, -4.534137594904308, + -4.3611959922401695, -2.871768725634745, -4.598399529122678, + -5.564587232141456, -5.519124858064699, -2.4250566837898457, + -5.688201188108633, -5.967149580591659, -5.01834957015221, + -2.505498094634157, -5.3939617151106924, -6.3813483686685775, + -5.360223575478843, -5.821732580733155, -5.742268409378909, + -2.5427453130265225, -6.0299504818306895, -5.949907774157153, + -6.048642614842842, -6.106911522966818, -6.522426966928483, + -1.9368412502165815, -5.384205540165328, -3.2469989725553616, + -5.166904264475346, -4.662348253722951, -5.93295821584338, + -4.052787789271272, -3.8964417188805776, -4.725629703670472, + -3.994277013931218, -3.570836586604418, -4.883136091344702, + -4.20182336843176, -4.894970548991704, -4.1342203512338624, + -5.159122124033291, -4.442985425248647, -4.740819869164446, + -4.583031498894364, -4.589588899440523, -4.4058064220068935, + -4.87144005158151, -4.8541485544714496, -4.806719860900339, + -4.241282205172307, -4.937530163410501, -4.382360803432213, + -5.0116381355642226, -4.368201274828579, -4.782492565565015, + -4.544565218066568, -4.4563316672015425, + }}; + +constexpr auto kTokenMap = base::MakeFixedFlatMap( + {{'1', 1}, {'0', 0}, {'3', 3}, {'2', 2}, {'5', 5}, {'4', 4}, + {'7', 7}, {'6', 6}, {'9', 9}, {'8', 8}, {'A', 36}, {'C', 38}, + {'B', 37}, {'E', 40}, {'D', 39}, {'G', 42}, {'F', 41}, {'I', 44}, + {'H', 43}, {'K', 46}, {'J', 45}, {'M', 48}, {'L', 47}, {'O', 50}, + {'N', 49}, {'Q', 52}, {'P', 51}, {'S', 54}, {'R', 53}, {'U', 56}, + {'T', 55}, {'W', 58}, {'V', 57}, {'Y', 60}, {'X', 59}, {'Z', 61}, + {'a', 10}, {'c', 12}, {'b', 11}, {'e', 14}, {'d', 13}, {'g', 16}, + {'f', 15}, {'i', 18}, {'h', 17}, {'k', 20}, {'j', 19}, {'m', 22}, + {'l', 21}, {'o', 24}, {'n', 23}, {'q', 26}, {'p', 25}, {'s', 28}, + {'r', 27}, {'u', 30}, {'t', 29}, {'w', 32}, {'v', 31}, {'y', 34}, + {'x', 33}, {'z', 35}}); + +constexpr double kClassifierThreshold = 0.015; + +bool IsHashLikely(RegexUtil& regex_util, + std::string value, + double threshold_multiplier) { + regex_util.TransformToAlphanumeric(value); + + double log_prob_sum = 0.0; + size_t add_count = 0; + for (size_t i = 0; i < value.length() - 1; i++) { + auto matrix_pos_a = kTokenMap.find(value[i]); + auto matrix_pos_b = kTokenMap.find(value[i + 1]); + if (matrix_pos_a == kTokenMap.end() || matrix_pos_b == kTokenMap.end()) { + continue; + } + + log_prob_sum += + kClassifierTransitionMatrix[matrix_pos_a->second][matrix_pos_b->second]; + add_count++; + } + + if (add_count == 0) { + return 1.0; + } + + double prob = std::exp(log_prob_sum / add_count); + return prob < (threshold_multiplier * kClassifierThreshold); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/hash_detection.h b/components/web_discovery/browser/hash_detection.h new file mode 100644 index 000000000000..c6461aa40bc4 --- /dev/null +++ b/components/web_discovery/browser/hash_detection.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_HASH_DETECTION_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_HASH_DETECTION_H_ + +#include + +#include "brave/components/web_discovery/browser/regex_util.h" + +namespace web_discovery { + +// Uses a pre-trained Markov chain classifier to detect the likelihood +// of a hash in a given piece of text. Used in privacy guard functions +// for detecting potentially private URLs/queries. +bool IsHashLikely(RegexUtil& regex_util, + std::string value, + double probability_multiplier = 1.0); + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_HASH_DETECTION_H_ diff --git a/components/web_discovery/browser/hash_detection_unittest.cc b/components/web_discovery/browser/hash_detection_unittest.cc new file mode 100644 index 000000000000..94e6b944b28b --- /dev/null +++ b/components/web_discovery/browser/hash_detection_unittest.cc @@ -0,0 +1,29 @@ + +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/hash_detection.h" + +#include "brave/components/web_discovery/browser/regex_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace web_discovery { + +TEST(WebDiscoveryHashDetectionTest, Basic) { + RegexUtil regex_util; + + EXPECT_FALSE(IsHashLikely(regex_util, "test")); + EXPECT_FALSE(IsHashLikely(regex_util, "this is a test query")); + + EXPECT_FALSE(IsHashLikely(regex_util, + "pneumonoultramicroscopicsilicovolcanoconiosis")); + + EXPECT_TRUE(IsHashLikely(regex_util, + "N46iSNekpT:08ca45b7d7ea58ee:88dcbe4446168966a1")); + EXPECT_TRUE( + IsHashLikely(regex_util, "2btjjy78REtmYkkW0csHUbJZOstRXoWdX1mGrmmfeHI")); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/patterns.cc b/components/web_discovery/browser/patterns.cc new file mode 100644 index 000000000000..34cce886417e --- /dev/null +++ b/components/web_discovery/browser/patterns.cc @@ -0,0 +1,321 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/patterns.h" + +#include + +#include "base/containers/fixed_flat_map.h" +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "third_party/re2/src/re2/re2.h" + +namespace web_discovery { + +namespace { + +constexpr char kNormalPatternsKey[] = "normal"; +constexpr char kStrictPatternsKey[] = "strict"; +constexpr char kUrlPatternsKey[] = "urlPatterns"; +constexpr char kSearchEnginesKey[] = "searchEngines"; +constexpr char kIdMappingKey[] = "idMapping"; +constexpr char kScrapeRulesKey[] = "scrape"; +constexpr char kSubSelectorKey[] = "item"; +constexpr char kRuleTypeKey[] = "type"; +constexpr char kAttributeKey[] = "etype"; +constexpr char kResultTypeKey[] = "results"; +constexpr char kActionKey[] = "action"; +constexpr char kFieldsKey[] = "fields"; +constexpr char kPayloadsKey[] = "payloads"; +constexpr char kJoinFieldAction[] = "join"; +constexpr char kFunctionsAppliedKey[] = "functionsApplied"; +constexpr char kQueryTemplateKey[] = "queryTemplate"; +constexpr char kQueryTemplatePrefixKey[] = "prefix"; + +constexpr auto kScrapeRuleTypeMap = + base::MakeFixedFlatMap({ + {"standard", ScrapeRuleType::kStandard}, + {"searchQuery", ScrapeRuleType::kSearchQuery}, + {"widgetTitle", ScrapeRuleType::kWidgetTitle}, + }); +constexpr auto kPayloadRuleTypeMap = + base::MakeFixedFlatMap({ + {"query", PayloadRuleType::kQuery}, + {"single", PayloadRuleType::kSingle}, + }); +constexpr auto kPayloadResultTypeMap = + base::MakeFixedFlatMap({ + {"single", PayloadResultType::kSingle}, + {"clustered", PayloadResultType::kClustered}, + {"custom", PayloadResultType::kCustom}, + }); + +bool ParsePayloadRule(const base::Value& rule_value, PayloadRule* rule_out) { + auto* rule_list = rule_value.GetIfList(); + if (!rule_list || rule_list->size() < 2) { + VLOG(1) << "Payload rule details is not a list of appropiate size"; + return false; + } + auto* selector = (*rule_list)[0].GetIfString(); + auto* payload_key = (*rule_list)[1].GetIfString(); + if (!selector || !payload_key) { + VLOG(1) << "Selector or key missing from payload rule"; + return false; + } + rule_out->selector = *selector; + rule_out->key = *payload_key; + if (rule_list->size() > 2) { + auto* field_action = (*rule_list)[2].GetIfString(); + if (field_action && *field_action == kJoinFieldAction) { + rule_out->is_join = true; + } + } + return true; +} + +std::optional> ParsePayloadRules( + const base::Value::Dict* payload_dict) { + std::vector result(payload_dict->size()); + + auto rule_group_it = result.begin(); + for (const auto [key, rule_group_value] : *payload_dict) { + auto* rule_group_dict = rule_group_value.GetIfDict(); + if (!rule_group_dict) { + VLOG(1) << "Payload rule group is not a dict"; + return std::nullopt; + } + auto* action = rule_group_dict->FindString(kActionKey); + auto* fields = rule_group_dict->FindList(kFieldsKey); + auto* rule_type_str = rule_group_dict->FindString(kRuleTypeKey); + auto* result_type_str = rule_group_dict->FindString(kResultTypeKey); + if (!action || !rule_type_str || !result_type_str) { + VLOG(1) << "Payload rule group attributes missing"; + return std::nullopt; + } + auto rule_type_it = kPayloadRuleTypeMap.find(*rule_type_str); + auto result_type_it = kPayloadResultTypeMap.find(*result_type_str); + if (rule_type_it == kPayloadRuleTypeMap.end() || + result_type_it == kPayloadResultTypeMap.end()) { + VLOG(1) << "Payload rule or result types unknown"; + return std::nullopt; + } + rule_group_it->action = *action; + rule_group_it->key = key; + rule_group_it->result_type = result_type_it->second; + rule_group_it->rule_type = rule_type_it->second; + if (fields) { + rule_group_it->rules = std::vector(fields->size()); + + auto rule_it = rule_group_it->rules.begin(); + for (const auto& rule_value : *fields) { + if (!ParsePayloadRule(rule_value, rule_it.base())) { + return std::nullopt; + } + rule_it = std::next(rule_it); + } + } + + rule_group_it = std::next(rule_group_it); + } + return result; +} + +RefineFunctionList ParseFunctionsApplied(const base::Value::List* list) { + RefineFunctionList result; + for (const auto& function_val : *list) { + const auto* function_list = function_val.GetIfList(); + if (!function_list || function_list->size() <= 1) { + continue; + } + std::vector function_vec; + for (const auto& element : *function_list) { + function_vec.push_back(element.Clone()); + } + result.push_back(std::move(function_vec)); + } + return result; +} + +std::optional> ParseScrapeRules( + const base::Value::Dict* scrape_url_dict) { + base::flat_map result; + + for (const auto [selector, rule_group_value] : *scrape_url_dict) { + auto* rule_group_dict = rule_group_value.GetIfDict(); + if (!rule_group_dict) { + VLOG(1) << "Scrape rule group is not a dict"; + return std::nullopt; + } + + auto& rule_group = result[selector]; + for (const auto [report_key, rule_value] : *rule_group_dict) { + auto* rule_dict = rule_value.GetIfDict(); + if (!rule_dict) { + VLOG(1) << "Scrape rule details is not a dict"; + return std::nullopt; + } + auto* sub_selector = rule_dict->FindString(kSubSelectorKey); + auto* attribute = rule_dict->FindString(kAttributeKey); + auto* rule_type_str = rule_dict->FindString(kRuleTypeKey); + auto* functions_applied = rule_dict->FindList(kFunctionsAppliedKey); + if (!attribute) { + VLOG(1) << "Attribute missing from rule"; + return std::nullopt; + } + auto rule = std::make_unique(); + rule->rule_type = ScrapeRuleType::kOther; + if (rule_type_str) { + auto rule_type_it = kScrapeRuleTypeMap.find(*rule_type_str); + if (rule_type_it != kScrapeRuleTypeMap.end()) { + rule->rule_type = rule_type_it->second; + } + } + rule->attribute = *attribute; + if (sub_selector) { + rule->sub_selector = *sub_selector; + } + if (functions_applied) { + rule->functions_applied = ParseFunctionsApplied(functions_applied); + } + rule_group[report_key] = std::move(rule); + } + } + return result; +} + +std::optional> ParsePatternsURLDetails( + const base::Value::Dict* root_dict) { + auto* url_patterns_list = root_dict->FindList(kUrlPatternsKey); + auto* search_engines_list = root_dict->FindList(kSearchEnginesKey); + auto* scrape_dict = root_dict->FindDict(kScrapeRulesKey); + auto* payloads_dict = root_dict->FindDict(kPayloadsKey); + auto* id_mapping_dict = root_dict->FindDict(kIdMappingKey); + auto* query_templates_dict = root_dict->FindDict(kQueryTemplateKey); + if (!url_patterns_list || !search_engines_list || !scrape_dict || + !id_mapping_dict) { + VLOG(1) << "Missing URL patterns, search engines, scrape rules, id " + "mapping, or query templates"; + return std::nullopt; + } + + std::vector result(url_patterns_list->size()); + + for (size_t i = 0; i < url_patterns_list->size(); i++) { + auto* url_regex = (*url_patterns_list)[i].GetIfString(); + if (!url_regex) { + VLOG(1) << "URL pattern is not string"; + return std::nullopt; + } + auto& details = result[i]; + + details.url_regex = std::make_unique(*url_regex); + + std::string i_str = base::NumberToString(i); + + auto* id = id_mapping_dict->FindString(i_str); + auto* scrape_url_dict = scrape_dict->FindDict(i_str); + auto* payloads_url_dict = payloads_dict->FindDict(i_str); + if (!id || !scrape_url_dict) { + VLOG(1) << "ID or scrape dict missing for pattern"; + return std::nullopt; + } + details.id = *id; + + details.is_search_engine = + base::ranges::find(search_engines_list->begin(), + search_engines_list->end(), + i_str) != search_engines_list->end(); + + auto scrape_rule_groups = ParseScrapeRules(scrape_url_dict); + if (!scrape_rule_groups) { + return std::nullopt; + } + details.scrape_rule_groups = std::move(*scrape_rule_groups); + if (payloads_url_dict) { + auto payload_rule_groups = ParsePayloadRules(payloads_url_dict); + if (!payload_rule_groups) { + return std::nullopt; + } + details.payload_rule_groups = std::move(*payload_rule_groups); + } + if (query_templates_dict) { + auto* query_template_dict = query_templates_dict->FindDict(i_str); + if (query_template_dict) { + auto* prefix = query_template_dict->FindString(kQueryTemplatePrefixKey); + if (prefix) { + details.search_template_prefix = *prefix; + } + } + } + } + + return result; +} + +} // namespace + +ScrapeRule::ScrapeRule() = default; +ScrapeRule::~ScrapeRule() = default; + +PayloadRule::PayloadRule() = default; +PayloadRule::~PayloadRule() = default; + +PayloadRuleGroup::PayloadRuleGroup() = default; +PayloadRuleGroup::~PayloadRuleGroup() = default; + +PatternsURLDetails::PatternsURLDetails() = default; +PatternsURLDetails::~PatternsURLDetails() = default; + +PatternsGroup::PatternsGroup() = default; +PatternsGroup::~PatternsGroup() = default; + +const PatternsURLDetails* PatternsGroup::GetMatchingURLPattern( + const GURL& url, + bool is_strict_scrape) const { + const auto& patterns = is_strict_scrape ? strict_patterns : normal_patterns; + for (const auto& pattern : patterns) { + if (re2::RE2::PartialMatch(url.spec(), *pattern.url_regex) && + !pattern.scrape_rule_groups.empty()) { + return &pattern; + } + } + return nullptr; +} + +std::unique_ptr ParsePatterns(const std::string& patterns_json) { + auto result = std::make_unique(); + auto patterns_value = base::JSONReader::Read(patterns_json); + if (!patterns_value || !patterns_value->is_dict()) { + VLOG(1) << "Patterns is not JSON or is not dict"; + return nullptr; + } + const auto& patterns_dict = patterns_value->GetDict(); + + auto* normal_dict = patterns_dict.FindDict(kNormalPatternsKey); + auto* strict_dict = patterns_dict.FindDict(kStrictPatternsKey); + if (!normal_dict && !strict_dict) { + VLOG(1) << "No normal or strict rules in patterns"; + return nullptr; + } + + if (normal_dict) { + auto details = ParsePatternsURLDetails(normal_dict); + if (!details) { + return nullptr; + } + result->normal_patterns = std::move(*details); + } + if (strict_dict) { + auto details = ParsePatternsURLDetails(strict_dict); + if (!details) { + return nullptr; + } + result->strict_patterns = std::move(*details); + } + return result; +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/patterns.h b/components/web_discovery/browser/patterns.h new file mode 100644 index 000000000000..b9e3b749e104 --- /dev/null +++ b/components/web_discovery/browser/patterns.h @@ -0,0 +1,174 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PATTERNS_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PATTERNS_H_ + +#include +#include +#include +#include + +#include "base/containers/flat_map.h" +#include "base/values.h" +#include "url/gurl.h" + +namespace re2 { +class RE2; +} // namespace re2 + +namespace web_discovery { + +enum class ScrapeRuleType { + // Will retrieve a value not defined in the DOM, such as the client country + // code or the current url. + kStandard, + // If the following two types are used for a rule, the value will be marked + // as the search query, which will be used for privacy checks. + kSearchQuery, + kWidgetTitle, + // All other rules should have this type. No special processing will be + // performed. + kOther +}; +enum class PayloadRuleType { + // Coupled with the `kClustered` result type. + // All instances of a given attribute will be grouped into a single payload. + kQuery, + // Coupled with the `kSingle` result type. + // Each instance of a given attribute will have its own payload. + kSingle +}; +enum class PayloadResultType { + // Coupled with the `kSingle` rule type. + kSingle, + // Coupled with the `kClustered` rule type. + kClustered, + // Currently unsupported/ignored. + kCustom +}; + +// Contains functions for refining the scraped value. The inner vector +// contains the function name and arguments for the function. +using RefineFunctionList = std::vector>; + +// Defines rule for scraping an attribute from a given selected element. +struct ScrapeRule { + ScrapeRule(); + ~ScrapeRule(); + + ScrapeRule(const ScrapeRule&) = delete; + ScrapeRule& operator=(const ScrapeRule&) = delete; + + // An optional selector for an element within the current selected element. + // The attribute will be retrieved from the embedded element. + std::optional sub_selector; + ScrapeRuleType rule_type; + // The name of the attribute to retrieve for a DOM element. + std::string attribute; + // Functions used to refine the retrieved value. See the "func ids" defined + // in content_scraper.cc for all possible functions. + RefineFunctionList functions_applied; +}; + +// A map of keys (arbitrary IDs used for storing the scraped result) to scrape +// rules. +using ScrapeRuleGroup = + base::flat_map>; + +// A rule for provided a single key/value pair within the submission payload. +struct PayloadRule { + PayloadRule(); + ~PayloadRule(); + + PayloadRule(const PayloadRule&) = delete; + PayloadRule& operator=(const PayloadRule&) = delete; + + // The DOM selector of the scraped attribute. + std::string selector; + // The arbitrary key associated with the scraped value. + std::string key; + // If set to true, an array-like Dict (each dict key is an index) + // will be rendered. + // Each value in the Dict will be a Dict containing all keys/values + // associated with the selector. This is commonly used for listing search + // results. + bool is_join = false; +}; + +// Contains rules for generating a payload for submission. +struct PayloadRuleGroup { + PayloadRuleGroup(); + ~PayloadRuleGroup(); + + PayloadRuleGroup(const PayloadRuleGroup&) = delete; + PayloadRuleGroup& operator=(const PayloadRuleGroup&) = delete; + + // An arbitrary ID for the rule group. Current, this isn't used in the + // payload. + std::string key; + PayloadRuleType rule_type; + PayloadResultType result_type; + // The name of the "action" for the given payload. + std::string action; + // The rules for generating the fields within the payload. + std::vector rules; +}; + +// Contains settings and rule groups associated with a particular URL. +struct PatternsURLDetails { + PatternsURLDetails(); + ~PatternsURLDetails(); + + PatternsURLDetails(const PatternsURLDetails&) = delete; + PatternsURLDetails& operator=(const PatternsURLDetails&) = delete; + + // The regex used to match the URL in the address bar. + std::unique_ptr url_regex; + bool is_search_engine; + // The two or three-letter arbitrary id associated with the site. + std::string id; + // The search path prefix used for constructing private search queries + // for double fetching. + std::optional search_template_prefix; + // The scraping rules for the site. A map of DOM selectors + // to rule groups. + base::flat_map scrape_rule_groups; + // The payload generation rules used for generating submissions + // from scraped attributes. + std::vector payload_rule_groups; +}; + +// The full "patterns" configuration provided by the Web Discovery server. +// The configuration provides rules for scraping certain pages. +struct PatternsGroup { + PatternsGroup(); + ~PatternsGroup(); + + PatternsGroup(const PatternsGroup&) = delete; + PatternsGroup& operator=(const PatternsGroup&) = delete; + + // Checks URL against all URL regexes in either the "normal" or "strict" set, + // and returns the URL details/rules if available. + const PatternsURLDetails* GetMatchingURLPattern(const GURL& url, + bool is_strict_scrape) const; + + // A list of URLs and rules used for scraping pages in the renderer, + // pre-"double fetch". These rules typically scrape simple attributes which + // are used to determine whether a page is private (i.e. the search query). + std::vector normal_patterns; + // A list of URLS and rules used for scraping contents from a "double fetch". + // The rules are usually more involved than the "normal" rules. In the case of + // search engine result pages, the rules will be used to retrieve the + // search results and any other relevant details. + std::vector strict_patterns; +}; + +// Returns nullptr if parsing fails. +std::unique_ptr ParsePatterns(const std::string& patterns_json); + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PATTERNS_H_ diff --git a/components/web_discovery/browser/patterns_unittest.cc b/components/web_discovery/browser/patterns_unittest.cc new file mode 100644 index 000000000000..f25f07091d62 --- /dev/null +++ b/components/web_discovery/browser/patterns_unittest.cc @@ -0,0 +1,345 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/patterns.h" + +#include +#include + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "brave/components/constants/brave_paths.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/re2/src/re2/re2.h" + +namespace web_discovery { + +class WebDiscoveryPatternsTest : public testing::Test { + public: + ~WebDiscoveryPatternsTest() override = default; + + protected: + std::unique_ptr LoadAndParsePatterns() { + base::FilePath data_path = + base::PathService::CheckedGet(brave::DIR_TEST_DATA); + + std::string patterns_json; + bool read_result = base::ReadFileToString( + data_path.AppendASCII("web_discovery/patterns.json"), &patterns_json); + EXPECT_TRUE(read_result); + + auto parsed_patterns = ParsePatterns(patterns_json); + EXPECT_TRUE(parsed_patterns); + EXPECT_EQ(parsed_patterns->normal_patterns.size(), 3u); + EXPECT_EQ(parsed_patterns->strict_patterns.size(), 3u); + + return parsed_patterns; + } +}; + +TEST_F(WebDiscoveryPatternsTest, GroupURLDetails) { + auto parsed_patterns = LoadAndParsePatterns(); + ASSERT_TRUE(parsed_patterns); + + const auto* normal_pattern = &parsed_patterns->normal_patterns[0]; + EXPECT_EQ(normal_pattern->id, "ex1"); + EXPECT_EQ(normal_pattern->url_regex->pattern(), "^https://example1.com"); + EXPECT_TRUE(normal_pattern->is_search_engine); + EXPECT_FALSE(normal_pattern->search_template_prefix); + EXPECT_TRUE(normal_pattern->payload_rule_groups.empty()); + EXPECT_EQ(normal_pattern->scrape_rule_groups.size(), 1u); + + normal_pattern = &parsed_patterns->normal_patterns[1]; + EXPECT_EQ(normal_pattern->id, "nase"); + EXPECT_EQ(normal_pattern->url_regex->pattern(), + "^https://notasearchengine.biz"); + EXPECT_FALSE(normal_pattern->is_search_engine); + EXPECT_FALSE(normal_pattern->search_template_prefix); + EXPECT_FALSE(normal_pattern->payload_rule_groups.empty()); + EXPECT_EQ(normal_pattern->scrape_rule_groups.size(), 2u); + + normal_pattern = &parsed_patterns->normal_patterns[2]; + EXPECT_EQ(normal_pattern->id, "ex2"); + EXPECT_EQ(normal_pattern->url_regex->pattern(), + "^https://search.example2.com"); + EXPECT_TRUE(normal_pattern->is_search_engine); + EXPECT_FALSE(normal_pattern->search_template_prefix); + EXPECT_TRUE(normal_pattern->payload_rule_groups.empty()); + EXPECT_EQ(normal_pattern->scrape_rule_groups.size(), 1u); + + const auto* strict_pattern = &parsed_patterns->strict_patterns[0]; + EXPECT_EQ(strict_pattern->id, "ex1"); + EXPECT_EQ(strict_pattern->url_regex->pattern(), "^https://example1.com."); + EXPECT_TRUE(strict_pattern->is_search_engine); + EXPECT_EQ(strict_pattern->search_template_prefix, "search?query="); + EXPECT_EQ(strict_pattern->payload_rule_groups.size(), 1u); + EXPECT_EQ(strict_pattern->scrape_rule_groups.size(), 3u); + + strict_pattern = &parsed_patterns->strict_patterns[1]; + EXPECT_EQ(strict_pattern->id, "nase"); + EXPECT_EQ(strict_pattern->url_regex->pattern(), + "^https://notasearchengine.biz"); + EXPECT_FALSE(strict_pattern->is_search_engine); + EXPECT_EQ(strict_pattern->search_template_prefix, "directory?query="); + EXPECT_EQ(strict_pattern->payload_rule_groups.size(), 1u); + EXPECT_EQ(strict_pattern->scrape_rule_groups.size(), 2u); + + strict_pattern = &parsed_patterns->strict_patterns[2]; + EXPECT_EQ(strict_pattern->id, "ex2"); + EXPECT_EQ(strict_pattern->url_regex->pattern(), + "^https://search.example2.com"); + EXPECT_TRUE(strict_pattern->is_search_engine); + EXPECT_FALSE(strict_pattern->search_template_prefix); + EXPECT_EQ(strict_pattern->payload_rule_groups.size(), 1u); + EXPECT_EQ(strict_pattern->scrape_rule_groups.size(), 1u); +} + +TEST_F(WebDiscoveryPatternsTest, NormalScrapeRules) { + auto parsed_patterns = LoadAndParsePatterns(); + ASSERT_TRUE(parsed_patterns); + const auto* normal_pattern = &parsed_patterns->normal_patterns[0]; + EXPECT_EQ(normal_pattern->scrape_rule_groups.size(), 1u); + + auto rule_group_it = normal_pattern->scrape_rule_groups.find("form .search"); + ASSERT_TRUE(rule_group_it != normal_pattern->scrape_rule_groups.end()); + const auto* rule_group = &rule_group_it->second; + EXPECT_EQ(rule_group->size(), 1u); + + auto rule_it = rule_group->find("q"); + ASSERT_TRUE(rule_it != rule_group->end()); + const auto* rule = rule_it->second.get(); + + EXPECT_EQ(rule->sub_selector, "input"); + EXPECT_EQ(rule->rule_type, ScrapeRuleType::kSearchQuery); + EXPECT_EQ(rule->attribute, "value"); + EXPECT_TRUE(rule->functions_applied.empty()); + + normal_pattern = &parsed_patterns->normal_patterns[1]; + EXPECT_EQ(normal_pattern->scrape_rule_groups.size(), 2u); + + rule_group_it = normal_pattern->scrape_rule_groups.find(".field1 input"); + ASSERT_TRUE(rule_group_it != normal_pattern->scrape_rule_groups.end()); + rule_group = &rule_group_it->second; + EXPECT_EQ(rule_group->size(), 1u); + + rule_it = rule_group->find("t"); + ASSERT_TRUE(rule_it != rule_group->end()); + rule = rule_it->second.get(); + + EXPECT_FALSE(rule->sub_selector); + EXPECT_EQ(rule->rule_type, ScrapeRuleType::kOther); + EXPECT_EQ(rule->attribute, "href"); + EXPECT_TRUE(rule->functions_applied.empty()); + + rule_group_it = normal_pattern->scrape_rule_groups.find(".field2 input"); + ASSERT_TRUE(rule_group_it != normal_pattern->scrape_rule_groups.end()); + rule_group = &rule_group_it->second; + EXPECT_EQ(rule_group->size(), 2u); + + rule_it = rule_group->find("t"); + ASSERT_TRUE(rule_it != rule_group->end()); + rule = rule_it->second.get(); + + EXPECT_FALSE(rule->sub_selector); + EXPECT_EQ(rule->rule_type, ScrapeRuleType::kOther); + EXPECT_EQ(rule->attribute, "href"); + EXPECT_EQ(rule->functions_applied.size(), 1u); + EXPECT_EQ(rule->functions_applied[0].size(), 3u); + EXPECT_EQ(rule->functions_applied[0][0], "parseU"); + EXPECT_EQ(rule->functions_applied[0][1], "qs"); + EXPECT_EQ(rule->functions_applied[0][2], "t"); + + rule_it = rule_group->find("b"); + ASSERT_TRUE(rule_it != rule_group->end()); + rule = rule_it->second.get(); + + EXPECT_FALSE(rule->sub_selector); + EXPECT_EQ(rule->rule_type, ScrapeRuleType::kOther); + EXPECT_EQ(rule->attribute, "textContent"); + EXPECT_TRUE(rule->functions_applied.empty()); + + normal_pattern = &parsed_patterns->normal_patterns[2]; + EXPECT_EQ(normal_pattern->scrape_rule_groups.size(), 1u); + + rule_group_it = normal_pattern->scrape_rule_groups.find("form .search-box"); + ASSERT_TRUE(rule_group_it != normal_pattern->scrape_rule_groups.end()); + rule_group = &rule_group_it->second; + EXPECT_EQ(rule_group->size(), 1u); + + rule_it = rule_group->find("q"); + ASSERT_TRUE(rule_it != rule_group->end()); + rule = rule_it->second.get(); + + EXPECT_EQ(rule->sub_selector, "input"); + EXPECT_EQ(rule->rule_type, ScrapeRuleType::kSearchQuery); + EXPECT_EQ(rule->attribute, "value"); + EXPECT_TRUE(rule->functions_applied.empty()); +} + +TEST_F(WebDiscoveryPatternsTest, StrictScrapeRules) { + auto parsed_patterns = LoadAndParsePatterns(); + ASSERT_TRUE(parsed_patterns); + const auto* strict_pattern = &parsed_patterns->strict_patterns[0]; + EXPECT_EQ(strict_pattern->scrape_rule_groups.size(), 3u); + + auto rule_group_it = strict_pattern->scrape_rule_groups.find("form .search"); + ASSERT_TRUE(rule_group_it != strict_pattern->scrape_rule_groups.end()); + const auto* rule_group = &rule_group_it->second; + EXPECT_EQ(rule_group->size(), 1u); + + auto rule_it = rule_group->find("q"); + ASSERT_TRUE(rule_it != rule_group->end()); + const auto* rule = rule_it->second.get(); + + EXPECT_EQ(rule->sub_selector, "input"); + EXPECT_EQ(rule->rule_type, ScrapeRuleType::kSearchQuery); + EXPECT_EQ(rule->attribute, "value"); + EXPECT_TRUE(rule->functions_applied.empty()); + + rule_group_it = strict_pattern->scrape_rule_groups.find("qurl"); + ASSERT_TRUE(rule_group_it != strict_pattern->scrape_rule_groups.end()); + rule_group = &rule_group_it->second; + EXPECT_EQ(rule_group->size(), 1u); + + rule_it = rule_group->find("qurl"); + ASSERT_TRUE(rule_it != rule_group->end()); + rule = rule_it->second.get(); + + EXPECT_FALSE(rule->sub_selector); + EXPECT_EQ(rule->rule_type, ScrapeRuleType::kStandard); + EXPECT_EQ(rule->attribute, "url"); + EXPECT_EQ(rule->functions_applied.size(), 1u); + EXPECT_EQ(rule->functions_applied[0].size(), 3u); + EXPECT_EQ(rule->functions_applied[0][0], "maskU"); + EXPECT_EQ(rule->functions_applied[0][1], false); + EXPECT_EQ(rule->functions_applied[0][2], false); + + rule_group_it = strict_pattern->scrape_rule_groups.find("ctry"); + ASSERT_TRUE(rule_group_it != strict_pattern->scrape_rule_groups.end()); + rule_group = &rule_group_it->second; + EXPECT_EQ(rule_group->size(), 1u); + + rule_it = rule_group->find("ctry"); + ASSERT_TRUE(rule_it != rule_group->end()); + rule = rule_it->second.get(); + + EXPECT_FALSE(rule->sub_selector); + EXPECT_EQ(rule->rule_type, ScrapeRuleType::kStandard); + EXPECT_EQ(rule->attribute, "ctry"); + EXPECT_TRUE(rule->functions_applied.empty()); + + strict_pattern = &parsed_patterns->strict_patterns[1]; + EXPECT_EQ(strict_pattern->scrape_rule_groups.size(), 2u); + + rule_group_it = strict_pattern->scrape_rule_groups.find("#content .a1"); + ASSERT_TRUE(rule_group_it != strict_pattern->scrape_rule_groups.end()); + rule_group = &rule_group_it->second; + EXPECT_EQ(rule_group->size(), 1u); + + rule_it = rule_group->find("age"); + ASSERT_TRUE(rule_it != rule_group->end()); + rule = rule_it->second.get(); + + EXPECT_FALSE(rule->sub_selector); + EXPECT_EQ(rule->rule_type, ScrapeRuleType::kOther); + EXPECT_EQ(rule->attribute, "textContent"); + EXPECT_TRUE(rule->functions_applied.empty()); + + strict_pattern = &parsed_patterns->strict_patterns[2]; + EXPECT_EQ(strict_pattern->scrape_rule_groups.size(), 1u); + + rule_group_it = strict_pattern->scrape_rule_groups.find("form .search-box"); + ASSERT_TRUE(rule_group_it != strict_pattern->scrape_rule_groups.end()); + rule_group = &rule_group_it->second; + EXPECT_EQ(rule_group->size(), 1u); + + rule_it = rule_group->find("age"); + ASSERT_TRUE(rule_it != rule_group->end()); + rule = rule_it->second.get(); + + EXPECT_EQ(rule->sub_selector, ".created-at"); + EXPECT_EQ(rule->rule_type, ScrapeRuleType::kOther); + EXPECT_EQ(rule->attribute, "value"); + EXPECT_TRUE(rule->functions_applied.empty()); +} + +TEST_F(WebDiscoveryPatternsTest, NormalPayloadRules) { + auto parsed_patterns = LoadAndParsePatterns(); + ASSERT_TRUE(parsed_patterns); + + const auto* normal_pattern = &parsed_patterns->normal_patterns[0]; + EXPECT_TRUE(normal_pattern->payload_rule_groups.empty()); + normal_pattern = &parsed_patterns->normal_patterns[2]; + EXPECT_TRUE(normal_pattern->payload_rule_groups.empty()); + + normal_pattern = &parsed_patterns->normal_patterns[1]; + EXPECT_EQ(normal_pattern->payload_rule_groups.size(), 1u); + + const auto& payload_group_it = normal_pattern->payload_rule_groups.front(); + EXPECT_EQ(payload_group_it.key, "key1"); + EXPECT_EQ(payload_group_it.rule_type, PayloadRuleType::kSingle); + EXPECT_EQ(payload_group_it.result_type, PayloadResultType::kSingle); + EXPECT_EQ(payload_group_it.action, "t"); + EXPECT_TRUE(payload_group_it.rules.empty()); +} + +TEST_F(WebDiscoveryPatternsTest, StrictPayloadRules) { + auto parsed_patterns = LoadAndParsePatterns(); + ASSERT_TRUE(parsed_patterns); + + const auto* strict_pattern = &parsed_patterns->strict_patterns[0]; + EXPECT_EQ(strict_pattern->payload_rule_groups.size(), 1u); + + const auto* payload_group = &strict_pattern->payload_rule_groups[0]; + EXPECT_EQ(payload_group->key, "key1"); + EXPECT_EQ(payload_group->rule_type, PayloadRuleType::kSingle); + EXPECT_EQ(payload_group->result_type, PayloadResultType::kSingle); + EXPECT_EQ(payload_group->action, "query"); + EXPECT_TRUE(payload_group->rules.empty()); + + strict_pattern = &parsed_patterns->strict_patterns[1]; + EXPECT_EQ(strict_pattern->payload_rule_groups.size(), 1u); + + payload_group = &strict_pattern->payload_rule_groups[0]; + EXPECT_EQ(payload_group->key, "key1"); + EXPECT_EQ(payload_group->rule_type, PayloadRuleType::kQuery); + EXPECT_EQ(payload_group->result_type, PayloadResultType::kClustered); + EXPECT_EQ(payload_group->action, "age-info"); + EXPECT_EQ(payload_group->rules.size(), 2u); + + const auto* payload_rule = &payload_group->rules[0]; + EXPECT_EQ(payload_rule->selector, "#content .a1"); + EXPECT_EQ(payload_rule->key, "age"); + EXPECT_FALSE(payload_rule->is_join); + + payload_rule = &payload_group->rules[1]; + EXPECT_EQ(payload_rule->selector, "ctry"); + EXPECT_EQ(payload_rule->key, "ctry"); + EXPECT_FALSE(payload_rule->is_join); + + strict_pattern = &parsed_patterns->strict_patterns[2]; + EXPECT_EQ(strict_pattern->payload_rule_groups.size(), 1u); + + payload_group = &strict_pattern->payload_rule_groups[0]; + EXPECT_EQ(payload_group->key, "key2"); + EXPECT_EQ(payload_group->rule_type, PayloadRuleType::kQuery); + EXPECT_EQ(payload_group->result_type, PayloadResultType::kClustered); + EXPECT_EQ(payload_group->action, "age-info"); + EXPECT_EQ(payload_group->rules.size(), 1u); + + payload_rule = &payload_group->rules[0]; + EXPECT_EQ(payload_rule->selector, "form .search-box"); + EXPECT_EQ(payload_rule->key, "age"); + EXPECT_TRUE(payload_rule->is_join); +} + +TEST_F(WebDiscoveryPatternsTest, BadPatterns) { + ASSERT_FALSE(ParsePatterns("ABC")); + ASSERT_FALSE(ParsePatterns("{}")); + ASSERT_FALSE(ParsePatterns(R"({"normal":{}, "strict":{}})")); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/payload_generator.cc b/components/web_discovery/browser/payload_generator.cc new file mode 100644 index 000000000000..2c631c15ad54 --- /dev/null +++ b/components/web_discovery/browser/payload_generator.cc @@ -0,0 +1,208 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/payload_generator.h" + +#include + +#include "base/containers/fixed_flat_set.h" +#include "brave/components/web_discovery/browser/privacy_guard.h" + +namespace web_discovery { + +namespace { + +constexpr char kCountryCodeFieldName[] = "ctry"; +constexpr char kSearchResultKey[] = "r"; +constexpr char kSearchResultURLKey[] = "u"; +constexpr size_t kMinSearchResultSize = 4; + +constexpr char kAliveAction[] = "alive"; +constexpr char kStatusFieldName[] = "status"; +constexpr char kTimestampFieldName[] = "t"; + +constexpr auto kQueryActions = base::MakeFixedFlatSet( + {"query", "anon-query", "widgetTitle"}); + +bool ValueHasContent(const base::Value& value) { + const auto* value_str = value.GetIfString(); + if (value_str) { + return !value_str->empty(); + } + if (!value.is_none()) { + return true; + } + return false; +} + +bool AggregatedDictHasContent(const base::Value::Dict& dict) { + for (const auto [_k1, value] : dict) { + const auto* value_dict = value.GetIfDict(); + if (!value_dict) { + continue; + } + for (const auto [_k2, sub_value] : *value_dict) { + if (ValueHasContent(sub_value)) { + return true; + } + } + } + return false; +} + +bool IsPrivateResult(RegexUtil& regex_util, + const PayloadRule& rule, + const PatternsURLDetails* matching_url_details, + const base::Value::Dict& dict) { + if (rule.key != kSearchResultKey) { + return false; + } + const auto* url = dict.FindString(kSearchResultURLKey); + if (!url) { + return false; + } + return IsPrivateURLLikely(regex_util, GURL(*url), matching_url_details); +} + +bool ShouldDropSearchResultPayload(const PayloadRule& rule, + size_t result_size) { + if (rule.key != kSearchResultKey) { + return false; + } + return result_size < kMinSearchResultSize; +} + +base::Value::Dict CreatePayloadDict(const PayloadRuleGroup& rule_group, + base::Value::Dict inner_payload) { + base::Value::Dict payload; + payload.Set(kActionKey, rule_group.action); + payload.Set(kInnerPayloadKey, std::move(inner_payload)); + return payload; +} + +std::optional GenerateClusteredJoinedPayload( + RegexUtil& regex_util, + bool is_query_action, + const PayloadRule& rule, + const PatternsURLDetails* matching_url_details, + const std::vector& attribute_values) { + base::Value::Dict joined_data; + size_t counter = 0; + for (const auto& value : attribute_values) { + if (value.empty()) { + continue; + } + if (is_query_action && + IsPrivateResult(regex_util, rule, matching_url_details, value)) { + VLOG(1) << "Omitting private search result"; + continue; + } + joined_data.Set(base::NumberToString(counter++), value.Clone()); + } + if (!AggregatedDictHasContent(joined_data)) { + VLOG(1) << "Skipped joined clustered payload due to lack of content"; + return std::nullopt; + } + if (is_query_action && + ShouldDropSearchResultPayload(rule, joined_data.size())) { + VLOG(1) << "Skipped search result payload due to too few results"; + return std::nullopt; + } + return base::Value(std::move(joined_data)); +} + +std::optional GenerateClusteredPayload( + RegexUtil& regex_util, + const PayloadRuleGroup& rule_group, + const PatternsURLDetails* matching_url_details, + const PageScrapeResult* scrape_result) { + base::Value::Dict inner_payload; + for (const auto& rule : rule_group.rules) { + base::Value payload_rule_data; + auto attribute_values_it = scrape_result->fields.find(rule.selector); + if (attribute_values_it == scrape_result->fields.end() || + attribute_values_it->second.empty()) { + VLOG(1) << "Skipped clustered payload due to no values for root " + "selector, action = " + << rule_group.action; + return std::nullopt; + } + if (rule.is_join) { + auto joined_payload = GenerateClusteredJoinedPayload( + regex_util, kQueryActions.contains(rule_group.action), rule, + matching_url_details, attribute_values_it->second); + if (!joined_payload) { + VLOG(1) << "Skipped joined clustered payload, action = " + << rule_group.action; + return std::nullopt; + } + payload_rule_data = std::move(*joined_payload); + } else { + const auto* value = attribute_values_it->second[0].FindString(rule.key); + if (!value || value->empty()) { + VLOG(1) << "Skipped non-joined clustered payload, action = " + << rule_group.action; + return std::nullopt; + } + payload_rule_data = base::Value(*value); + } + inner_payload.Set(rule.key, std::move(payload_rule_data)); + } + return CreatePayloadDict(rule_group, std::move(inner_payload)); +} + +void GenerateSinglePayloads(const ServerConfig& server_config, + const PayloadRuleGroup& rule_group, + const PageScrapeResult* scrape_result, + std::vector& payloads) { + auto attribute_values_it = scrape_result->fields.find(rule_group.key); + if (attribute_values_it == scrape_result->fields.end()) { + return; + } + for (const auto& attribute_value : attribute_values_it->second) { + auto dict = attribute_value.Clone(); + dict.Set(kCountryCodeFieldName, server_config.location); + payloads.push_back(CreatePayloadDict(rule_group, std::move(dict))); + } +} + +} // namespace + +std::vector GenerateQueryPayloads( + const ServerConfig& server_config, + RegexUtil& regex_util, + const PatternsURLDetails* url_details, + std::unique_ptr scrape_result) { + std::vector payloads; + for (const auto& rule_group : url_details->payload_rule_groups) { + if (rule_group.rule_type == PayloadRuleType::kQuery && + rule_group.result_type == PayloadResultType::kClustered) { + auto payload = GenerateClusteredPayload(regex_util, rule_group, + url_details, scrape_result.get()); + if (payload) { + payloads.push_back(std::move(*payload)); + } + } else if (rule_group.rule_type == PayloadRuleType::kSingle && + rule_group.result_type == PayloadResultType::kSingle) { + GenerateSinglePayloads(server_config, rule_group, scrape_result.get(), + payloads); + } + } + return payloads; +} + +base::Value::Dict GenerateAlivePayload(const ServerConfig& server_config, + std::string date_hour) { + base::Value::Dict payload; + payload.Set(kActionKey, kAliveAction); + base::Value::Dict inner_payload; + inner_payload.Set(kStatusFieldName, true); + inner_payload.Set(kTimestampFieldName, date_hour); + inner_payload.Set(kCountryCodeFieldName, server_config.location); + payload.Set(kInnerPayloadKey, std::move(inner_payload)); + return payload; +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/payload_generator.h b/components/web_discovery/browser/payload_generator.h new file mode 100644 index 000000000000..b234c5872abc --- /dev/null +++ b/components/web_discovery/browser/payload_generator.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PAYLOAD_GENERATOR_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PAYLOAD_GENERATOR_H_ + +#include +#include +#include + +#include "base/values.h" +#include "brave/components/web_discovery/browser/content_scraper.h" +#include "brave/components/web_discovery/browser/patterns.h" +#include "brave/components/web_discovery/browser/regex_util.h" + +namespace web_discovery { + +inline constexpr char kActionKey[] = "action"; +inline constexpr char kInnerPayloadKey[] = "payload"; + +// Generates "query" messages using the payload generation rules +// and scraped data for a given site. +std::vector GenerateQueryPayloads( + const ServerConfig& server_config, + RegexUtil& regex_util, + const PatternsURLDetails* url_details, + std::unique_ptr scrape_result); + +// Generates an "alive" message to indicate an opted-in +// status to the server. +base::Value::Dict GenerateAlivePayload(const ServerConfig& server_config, + std::string date_hour); + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PAYLOAD_GENERATOR_H_ diff --git a/components/web_discovery/browser/payload_generator_unittest.cc b/components/web_discovery/browser/payload_generator_unittest.cc new file mode 100644 index 000000000000..853f6c816c9c --- /dev/null +++ b/components/web_discovery/browser/payload_generator_unittest.cc @@ -0,0 +1,283 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/payload_generator.h" + +#include +#include + +#include "brave/components/web_discovery/browser/content_scraper.h" +#include "brave/components/web_discovery/browser/patterns.h" +#include "brave/components/web_discovery/browser/regex_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace web_discovery { + +class WebDiscoveryPayloadGeneratorTest : public testing::Test { + public: + ~WebDiscoveryPayloadGeneratorTest() override = default; + + // testing::Test: + void SetUp() override { + server_config_ = std::make_unique(); + server_config_->location = "us"; + + url_details_ = std::make_unique(); + + url_details_->payload_rule_groups = std::vector(2); + + auto& single_group = url_details_->payload_rule_groups[0]; + single_group.key = ".single-element"; + single_group.rule_type = PayloadRuleType::kSingle; + single_group.result_type = PayloadResultType::kSingle; + single_group.action = "single_action"; + + auto& query_group = url_details_->payload_rule_groups[1]; + query_group.key = "query_group"; + query_group.rule_type = PayloadRuleType::kQuery; + query_group.result_type = PayloadResultType::kClustered; + query_group.action = "query"; + query_group.rules = std::vector(2); + + auto& join_rule = query_group.rules[0]; + join_rule.selector = "#results"; + join_rule.key = "r"; + join_rule.is_join = true; + + auto& qurl_rule = query_group.rules[1]; + qurl_rule.selector = "qurl"; + qurl_rule.key = "qurl"; + qurl_rule.is_join = false; + } + + protected: + std::vector GenerateQueryPayloadsHelper( + std::unique_ptr scrape_result) { + return GenerateQueryPayloads(*server_config_.get(), regex_util_, + url_details_.get(), std::move(scrape_result)); + } + + std::unique_ptr server_config_; + + private: + RegexUtil regex_util_; + std::unique_ptr url_details_; +}; + +TEST_F(WebDiscoveryPayloadGeneratorTest, GenerateQueryPayloads) { + GURL test_url("https://example.com/test"); + auto scrape_result = std::make_unique(test_url, "test_id"); + + std::vector single_dicts; + base::Value::Dict single_dict1; + single_dict1.Set("ab", "value1"); + single_dict1.Set("cd", "value2"); + single_dicts.push_back(std::move(single_dict1)); + base::Value::Dict single_dict2; + single_dict2.Set("ef", "value3"); + single_dict2.Set("gh", "value4"); + single_dicts.push_back(std::move(single_dict2)); + scrape_result->fields[".single-element"] = std::move(single_dicts); + + std::vector result_dicts1; + base::Value::Dict result_dict1, result_dict2, result_dict3, result_dict4, + result_dict5; + result_dict1.Set("njk", "joinvalue1"); + result_dict2.Set("abc", "joinvalue2"); + result_dict3.Set("njk", "joinvalue3"); + result_dict4.Set("abc", "joinvalue4"); + result_dicts1.push_back(std::move(result_dict1)); + result_dicts1.push_back(std::move(result_dict2)); + result_dicts1.push_back(std::move(result_dict3)); + result_dicts1.push_back(std::move(result_dict4)); + std::vector result_dicts2; + result_dict5.Set("qurl", "https://example.com/test1"); + result_dicts2.push_back(std::move(result_dict5)); + scrape_result->fields["#results"] = std::move(result_dicts1); + scrape_result->fields["qurl"] = std::move(result_dicts2); + + auto payloads = GenerateQueryPayloadsHelper(std::move(scrape_result)); + ASSERT_EQ(payloads.size(), 3u); + + const auto* payload = &payloads[0]; + const auto* action = payload->FindString(kActionKey); + const auto* inner_payload = payload->FindDict(kInnerPayloadKey); + ASSERT_TRUE(action && inner_payload); + EXPECT_EQ(*action, "single_action"); + + EXPECT_EQ(inner_payload->size(), 3u); + + const auto* ctry = inner_payload->FindString("ctry"); + const auto* val1 = inner_payload->FindString("ab"); + const auto* val2 = inner_payload->FindString("cd"); + ASSERT_TRUE(ctry && val1 && val2); + EXPECT_EQ(*ctry, "us"); + EXPECT_EQ(*val1, "value1"); + EXPECT_EQ(*val2, "value2"); + + payload = &payloads[1]; + action = payload->FindString(kActionKey); + inner_payload = payload->FindDict(kInnerPayloadKey); + ASSERT_TRUE(action && inner_payload); + EXPECT_EQ(*action, "single_action"); + + EXPECT_EQ(inner_payload->size(), 3u); + + ctry = inner_payload->FindString("ctry"); + val1 = inner_payload->FindString("ef"); + val2 = inner_payload->FindString("gh"); + ASSERT_TRUE(ctry && val1 && val2); + EXPECT_EQ(*ctry, "us"); + EXPECT_EQ(*val1, "value3"); + EXPECT_EQ(*val2, "value4"); + + payload = &payloads[2]; + action = payload->FindString(kActionKey); + inner_payload = payload->FindDict(kInnerPayloadKey); + ASSERT_TRUE(action && inner_payload); + EXPECT_EQ(*action, "query"); + + EXPECT_EQ(inner_payload->size(), 2u); + + const auto* qurl = inner_payload->FindString("qurl"); + const auto* r_dict = inner_payload->FindDict("r"); + ASSERT_TRUE(qurl && r_dict); + EXPECT_EQ(*qurl, "https://example.com/test1"); + EXPECT_EQ(r_dict->size(), 4u); + const auto* r0_dict = r_dict->FindDict("0"); + const auto* r1_dict = r_dict->FindDict("1"); + const auto* r2_dict = r_dict->FindDict("2"); + const auto* r3_dict = r_dict->FindDict("3"); + ASSERT_TRUE(r0_dict && r1_dict && r2_dict && r3_dict); + EXPECT_EQ(r0_dict->size(), 1u); + EXPECT_EQ(r1_dict->size(), 1u); + EXPECT_EQ(r2_dict->size(), 1u); + EXPECT_EQ(r3_dict->size(), 1u); + const auto* r0_val = r0_dict->FindString("njk"); + const auto* r1_val = r1_dict->FindString("abc"); + const auto* r2_val = r2_dict->FindString("njk"); + const auto* r3_val = r3_dict->FindString("abc"); + ASSERT_TRUE(r0_val && r1_val && r2_val && r2_val); + EXPECT_EQ(*r0_val, "joinvalue1"); + EXPECT_EQ(*r1_val, "joinvalue2"); + EXPECT_EQ(*r2_val, "joinvalue3"); + EXPECT_EQ(*r3_val, "joinvalue4"); +} + +TEST_F(WebDiscoveryPayloadGeneratorTest, GenerateAlivePayload) { + std::string date_hour = "2023051509"; + + auto alive_payload = GenerateAlivePayload(*server_config_.get(), date_hour); + + const auto* action = alive_payload.FindString("action"); + const auto* inner_payload = alive_payload.FindDict("payload"); + + ASSERT_TRUE(action && inner_payload); + + EXPECT_EQ(*action, "alive"); + const auto* ctry = inner_payload->FindString("ctry"); + const auto* ts = inner_payload->FindString("t"); + const auto status = inner_payload->FindBool("status"); + + ASSERT_TRUE(ctry && ts && status); + EXPECT_EQ(*ctry, "us"); + EXPECT_EQ(*ts, date_hour); + EXPECT_EQ(*status, true); +} + +TEST_F(WebDiscoveryPayloadGeneratorTest, ExcludePrivateResult) { + GURL test_url("https://example.com/search"); + auto scrape_result = std::make_unique(test_url, "test_id"); + + std::vector result_dicts1; + for (int i = 0; i < 5; i++) { + base::Value::Dict result_dict; + std::string url = "https://example.com/result"; + if (i == 1) { + url = "https://423947892374892879.com/example"; + } else { + url += base::NumberToString(i == 0 ? 0 : i - 1); + } + result_dict.Set("u", url); + result_dicts1.push_back(std::move(result_dict)); + } + scrape_result->fields["#results"] = std::move(result_dicts1); + std::vector result_dicts2; + base::Value::Dict qurl_dict; + qurl_dict.Set("qurl", "https://example.com/test1"); + result_dicts2.push_back(std::move(qurl_dict)); + scrape_result->fields["qurl"] = std::move(result_dicts2); + + auto payloads = GenerateQueryPayloadsHelper(std::move(scrape_result)); + ASSERT_EQ(payloads.size(), 1u); + + const auto* payload = &payloads[0]; + const auto* inner_payload = payload->FindDict("payload"); + const auto* r_dict = inner_payload->FindDict("r"); + ASSERT_TRUE(inner_payload && r_dict); + + ASSERT_EQ(r_dict->size(), 4u); + + for (int i = 0; i < 4; i++) { + const auto* ri_dict = r_dict->FindDict(base::NumberToString(i)); + ASSERT_TRUE(ri_dict); + + const auto* url = ri_dict->FindString("u"); + ASSERT_TRUE(url); + + EXPECT_EQ(*url, "https://example.com/result" + base::NumberToString(i)); + } +} + +TEST_F(WebDiscoveryPayloadGeneratorTest, ShouldDropSearchResult) { + GURL test_url("https://example.com/search"); + auto scrape_result = std::make_unique(test_url, "test_id"); + + std::vector result_dicts; + for (int i = 0; i < 3; i++) { + base::Value::Dict result_dict; + result_dict.Set("u", + "https://example.com/result" + base::NumberToString(i)); + result_dicts.push_back(std::move(result_dict)); + } + scrape_result->fields["#results"] = std::move(result_dicts); + + std::vector qurl_dicts; + base::Value::Dict qurl_dict; + qurl_dict.Set("qurl", "https://example.com/test1"); + qurl_dicts.push_back(std::move(qurl_dict)); + scrape_result->fields["qurl"] = std::move(qurl_dicts); + + auto payloads = GenerateQueryPayloadsHelper(std::move(scrape_result)); + ASSERT_EQ(payloads.size(), 0u); +} + +TEST_F(WebDiscoveryPayloadGeneratorTest, ContentMissing) { + GURL test_url("https://example.com/search"); + auto scrape_result = std::make_unique(test_url, "test_id"); + + std::vector result_dicts; + for (int i = 0; i < 9; i++) { + base::Value::Dict result_dict; + if (i < 5) { + result_dict.Set("x", ""); + } else { + result_dict.Set("x", base::Value()); + } + result_dicts.push_back(std::move(result_dict)); + } + scrape_result->fields["#results"] = std::move(result_dicts); + + std::vector qurl_dicts; + base::Value::Dict qurl_dict; + qurl_dict.Set("qurl", "https://example.com/test1"); + qurl_dicts.push_back(std::move(qurl_dict)); + scrape_result->fields["qurl"] = std::move(qurl_dicts); + + auto payloads = GenerateQueryPayloadsHelper(std::move(scrape_result)); + ASSERT_EQ(payloads.size(), 0u); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/pref_names.h b/components/web_discovery/browser/pref_names.h new file mode 100644 index 000000000000..5c957a40b6f4 --- /dev/null +++ b/components/web_discovery/browser/pref_names.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PREF_NAMES_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PREF_NAMES_H_ + +namespace web_discovery { + +// Profile prefs +inline constexpr char kWebDiscoveryNativeEnabled[] = + "brave.web_discovery.wdp_native_enabled"; + +// The following pref values are used for generating +// anonymous signatures for user submissions. +// Since they are not used for encrypting sensitive data, +// they do not require secure storage. +inline constexpr char kCredentialRSAPrivateKey[] = + "brave.web_discovery.rsa_priv_key"; +inline constexpr char kCredentialRSAPublicKey[] = + "brave.web_discovery.rsa_pub_key"; +inline constexpr char kAnonymousCredentialsDict[] = + "brave.web_discovery.anon_creds"; + +inline constexpr char kScheduledDoubleFetches[] = + "brave.web_discovery.scheduled_double_fetches"; +inline constexpr char kScheduledReports[] = + "brave.web_discovery.scheduled_reports"; +inline constexpr char kUsedBasenameCounts[] = + "brave.web_discovery.used_basename_counts"; +inline constexpr char kPageCounts[] = "brave.web_discovery.page_counts"; + +// Local state +inline constexpr char kPatternsRetrievalTime[] = + "brave.web_discovery.patterns_retrieval_time"; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PREF_NAMES_H_ diff --git a/components/web_discovery/browser/privacy_guard.cc b/components/web_discovery/browser/privacy_guard.cc new file mode 100644 index 000000000000..889af91a7fe4 --- /dev/null +++ b/components/web_discovery/browser/privacy_guard.cc @@ -0,0 +1,259 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/privacy_guard.h" + +#include "base/logging.h" +#include "base/strings/strcat.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "brave/components/web_discovery/browser/hash_detection.h" +#include "brave/components/web_discovery/browser/regex_util.h" +#include "brave/components/web_discovery/browser/util.h" +#include "url/url_constants.h" +#include "url/url_util.h" + +namespace web_discovery { + +namespace { + +constexpr size_t kMaxSearchEngineRefLength = 8; +constexpr size_t kMaxQueryLength = 50; +constexpr size_t kMaxQueryNumberLength = 7; +constexpr size_t kMaxQuerySplitLength = 7; +constexpr size_t kMaxQueryWordLength = 20; +constexpr size_t kHashCheckMinimumLength = 13; +constexpr double kHashCheckThresholdMultiplier = 1.5; + +constexpr size_t kMaxQueryStringLength = 30; +constexpr size_t kMaxQueryStringParts = 4; +constexpr size_t kMaxQueryStringOrPathNumberLength = 12; +constexpr size_t kMaxPathPartLength = 18; +constexpr size_t kMinPathPartHashCheckLength = 13; +constexpr size_t kMinSegmentHashCheckLength = 16; + +constexpr size_t kMaxDotSplitDomainSize = 6; +constexpr size_t kMaxHyphenSplitDomainSize = 4; +constexpr size_t kMaxDomainNumberLength = 5; + +constexpr char kDefaultSearchPrefix[] = "search?q="; + +constexpr char kOnionSiteSuffix[] = ".onion"; +constexpr char kLocalDomainSuffix[] = ".local"; +constexpr char kLocalhost[] = "localhost"; + +constexpr char kGoogleHostSubstring[] = "google"; +constexpr char kGoogleURLQueryParam[] = "url"; +constexpr char kMaskedURLSuffix[] = "/ (PROTECTED)"; + +bool ContainsForbiddenKeywords(RegexUtil& regex_util, const GURL& url) { + auto path_and_query = + base::StrCat({url.path_piece(), "?", url.query_piece()}); + if (regex_util.CheckPathAndQueryStringKeywords(path_and_query)) { + return true; + } + if (!url.ref_piece().empty() && + regex_util.CheckQueryStringOrRefKeywords("#" + url.ref())) { + return true; + } + if (!url.query_piece().empty() && + regex_util.CheckQueryStringOrRefKeywords("?" + url.query())) { + return true; + } + return false; +} + +bool IsPrivateDomainLikely(RegexUtil& regex_util, const std::string_view host) { + auto dot_split = + base::SplitString(host, ".", base::WhitespaceHandling::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL); + if (dot_split.size() > kMaxDotSplitDomainSize) { + return true; + } + if (regex_util.CheckForLongNumber(host, kMaxDomainNumberLength)) { + return true; + } + auto hyphen_split = + base::SplitString(host, "-", base::WhitespaceHandling::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL); + if (hyphen_split.size() > kMaxHyphenSplitDomainSize) { + return true; + } + return false; +} + +} // namespace + +bool IsPrivateURLLikely(RegexUtil& regex_util, + const GURL& url, + const PatternsURLDetails* matching_url_details) { + if (!url.SchemeIs("https")) { + VLOG(1) << "Ignoring URL due to non-HTTPS scheme"; + return true; + } + if (url.HostIsIPAddress()) { + VLOG(1) << "Ignoring URL due to IP address host"; + return true; + } + if (url.has_username() || url.has_password()) { + VLOG(1) << "Ignoring URL due to inclusion of credentials"; + return true; + } + if (url.has_port() && url.port_piece() != "443") { + VLOG(1) << "Ignoring URL due to non-standard port"; + return true; + } + if (matching_url_details && matching_url_details->is_search_engine) { + if (url.has_ref() && url.ref_piece().length() > kMaxSearchEngineRefLength) { + VLOG(1) << "Ignoring search engine URL due to long ref"; + return true; + } + } + auto host_piece = url.host_piece(); + if (host_piece.ends_with(kOnionSiteSuffix) || + host_piece.ends_with(kLocalDomainSuffix) || host_piece == kLocalhost) { + VLOG(1) << "Ignoring URL due a local host or onion site"; + return true; + } + if (IsPrivateDomainLikely(regex_util, url.host_piece())) { + VLOG(1) << "Ignoring URL due likely private domain"; + return true; + } + return false; +} + +bool IsPrivateQueryLikely(RegexUtil& regex_util, const std::string& query) { + if (query.length() > kMaxQueryLength) { + VLOG(1) << "Ignoring query due to long length"; + return true; + } + auto split = + base::SplitString(query, " ", base::WhitespaceHandling::KEEP_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); + if (split.size() > kMaxQuerySplitLength) { + VLOG(1) << "Ignoring query due to long split length"; + return true; + } + if (regex_util.CheckForLongNumber(query, kMaxQueryNumberLength)) { + VLOG(1) << "Ignoring query due to long number"; + return true; + } + if (regex_util.CheckForEmail(query)) { + VLOG(1) << "Ignoring query due to inclusion of email"; + return true; + } + if (regex_util.CheckQueryHTTPCredentials(query)) { + VLOG(1) << "Ignoring query due to potential inclusion of HTTP credentials"; + return true; + } + for (const auto& word : split) { + if (word.length() > kMaxQueryWordLength) { + VLOG(1) << "Ignoring query due to long word"; + return true; + } + } + if (query.length() >= kHashCheckMinimumLength) { + if (IsHashLikely(regex_util, query, kHashCheckThresholdMultiplier)) { + VLOG(1) << "Ignoring query due to potential inclusion of hash"; + return true; + } + } + return false; +} + +GURL GeneratePrivateSearchURL(const GURL& original_url, + const std::string& query, + const PatternsURLDetails& matching_url_details) { + url::RawCanonOutputT query_encoded; + url::EncodeURIComponent(query, &query_encoded); + std::string query_encoded_str(query_encoded.view()); + base::ReplaceSubstringsAfterOffset(&query_encoded_str, 0, "%20", "+"); + + return GURL( + base::StrCat({original_url.scheme(), url::kStandardSchemeSeparator, + original_url.host(), "/", + matching_url_details.search_template_prefix.value_or( + kDefaultSearchPrefix), + query_encoded_str})); +} + +bool ShouldDropLongURL(RegexUtil& regex_util, const GURL& url) { + if (regex_util.CheckForEmail(url.spec())) { + return true; + } + if (!url.query_piece().empty()) { + if (url.query_piece().size() > kMaxQueryStringLength) { + return true; + } + auto query_parts = base::SplitString( + url.query_piece(), "&;", base::WhitespaceHandling::KEEP_WHITESPACE, + base::SplitResult::SPLIT_WANT_ALL); + if (query_parts.size() > kMaxQueryStringParts) { + return true; + } + if (regex_util.CheckForLongNumber(url.query_piece(), + kMaxQueryStringOrPathNumberLength)) { + return true; + } + } + if (!url.path_piece().empty()) { + if (regex_util.CheckForLongNumber(url.path_piece(), + kMaxQueryStringOrPathNumberLength)) { + return true; + } + } + auto path_parts = base::SplitString(url.path_piece(), "/._ -:+;", + base::WhitespaceHandling::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL); + for (const auto& path_part : path_parts) { + if (path_part.length() > kMaxPathPartLength) { + return true; + } + if (path_part.length() >= kMinPathPartHashCheckLength && + IsHashLikely(regex_util, path_part)) { + return true; + } + } + auto path_segments = base::SplitString( + url.path_piece(), "/", base::WhitespaceHandling::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL); + for (const auto& path_segment : path_segments) { + std::string alphanumeric_path_segment = path_segment; + regex_util.TransformToAlphanumeric(alphanumeric_path_segment); + if (alphanumeric_path_segment.length() >= kMinSegmentHashCheckLength && + IsHashLikely(regex_util, alphanumeric_path_segment)) { + return true; + } + } + return ContainsForbiddenKeywords(regex_util, url); +} + +std::optional MaskURL(RegexUtil& regex_util, const GURL& url) { + if (!url.SchemeIsHTTPOrHTTPS() || !url.is_valid()) { + return std::nullopt; + } + + if (!ShouldDropLongURL(regex_util, url)) { + return url.spec(); + } + + if (url.host_piece().find(kGoogleHostSubstring) != std::string::npos && + url.has_query()) { + auto google_url_param = + ExtractValueFromQueryString(url.query_piece(), kGoogleURLQueryParam); + if (google_url_param) { + GURL decoded_embedded_url(*google_url_param); + if (!decoded_embedded_url.is_valid()) { + return std::nullopt; + } + return MaskURL(regex_util, decoded_embedded_url); + } + } + + return base::StrCat({url.scheme(), url::kStandardSchemeSeparator, url.host(), + kMaskedURLSuffix}); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/privacy_guard.h b/components/web_discovery/browser/privacy_guard.h new file mode 100644 index 000000000000..36c2fcc971b4 --- /dev/null +++ b/components/web_discovery/browser/privacy_guard.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PRIVACY_GUARD_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PRIVACY_GUARD_H_ + +#include + +#include "brave/components/web_discovery/browser/patterns.h" +#include "brave/components/web_discovery/browser/regex_util.h" +#include "url/gurl.h" + +namespace web_discovery { + +// Checks if a URL is likely to be private based on various criteria. +// If true, the page should not be investigated or reported. +bool IsPrivateURLLikely(RegexUtil& regex_util, + const GURL& url, + const PatternsURLDetails* matching_url_details); + +// Determines if a search query is likely to contain private information. +// If true, the search query should not be investigated or reported. +bool IsPrivateQueryLikely(RegexUtil& regex_util, const std::string& query); + +// Generates a simple search URL (without additional query parameters) +// based on the original search URL and query. Used for the double fetch +// to ensure that the user's profile is not involved in the query. +GURL GeneratePrivateSearchURL(const GURL& original_url, + const std::string& query, + const PatternsURLDetails& matching_url_details); + +// Checks if a URL should be dropped due to its length or content. +// Currently only used for determining whether to mask a URL +// in the function below. +bool ShouldDropLongURL(RegexUtil& regex_util, const GURL& url); + +// Masks a URL to protect privacy. Returns nullopt if URL is invalid. +std::optional MaskURL(RegexUtil& regex_util, const GURL& url); + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_PRIVACY_GUARD_H_ diff --git a/components/web_discovery/browser/privacy_guard_unittest.cc b/components/web_discovery/browser/privacy_guard_unittest.cc new file mode 100644 index 000000000000..e67871a298d5 --- /dev/null +++ b/components/web_discovery/browser/privacy_guard_unittest.cc @@ -0,0 +1,169 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/privacy_guard.h" + +#include "brave/components/web_discovery/browser/patterns.h" +#include "brave/components/web_discovery/browser/regex_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace web_discovery { + +class WebDiscoveryPrivacyGuardTest : public testing::Test { + public: + ~WebDiscoveryPrivacyGuardTest() override = default; + + // testing::Test: + void SetUp() override { + search_engine_pattern_.is_search_engine = true; + search_engine_pattern_.search_template_prefix = "find?testquery="; + } + + protected: + PatternsURLDetails search_engine_pattern_; + RegexUtil regex_util_; +}; + +TEST_F(WebDiscoveryPrivacyGuardTest, IsPrivateURLLikely) { + EXPECT_FALSE(IsPrivateURLLikely(regex_util_, + GURL("https://www.search1.com/search?q=test"), + &search_engine_pattern_)); + EXPECT_FALSE(IsPrivateURLLikely( + regex_util_, + GURL("https://search2.com/search?query=testing+a+nice+query"), + &search_engine_pattern_)); + EXPECT_FALSE(IsPrivateURLLikely( + regex_util_, + GURL( + "https://search2.com/search?query=quick+brown+fox+jumped&country=us"), + &search_engine_pattern_)); + EXPECT_FALSE(IsPrivateURLLikely( + regex_util_, GURL("https://www.website.com/page/test"), nullptr)); + + EXPECT_TRUE(IsPrivateURLLikely( + regex_util_, GURL("http://www.website.com/page/test"), nullptr)); + EXPECT_TRUE(IsPrivateURLLikely( + regex_util_, GURL("https://88.88.88.88/page/test"), nullptr)); + EXPECT_TRUE(IsPrivateURLLikely( + regex_util_, GURL("https://website.com:8443/page/test"), nullptr)); + EXPECT_TRUE(IsPrivateURLLikely( + regex_util_, GURL("https://user:pass@website.com/page/test"), nullptr)); + EXPECT_TRUE(IsPrivateURLLikely( + regex_util_, GURL("https://www.search1.com/search?q=test#ABCDEFGHIJK"), + &search_engine_pattern_)); + + EXPECT_TRUE(IsPrivateURLLikely( + regex_util_, GURL("https://a.nested.sub.domain.website.co.uk/test/page"), + &search_engine_pattern_)); + EXPECT_TRUE(IsPrivateURLLikely( + regex_util_, GURL("https://abc192738284732929abc.com/test/page"), + &search_engine_pattern_)); + EXPECT_TRUE(IsPrivateURLLikely( + regex_util_, GURL("https://a-long-hyphenated-web-site.com/test/page"), + &search_engine_pattern_)); +} + +TEST_F(WebDiscoveryPrivacyGuardTest, IsPrivateQueryLikely) { + EXPECT_FALSE(IsPrivateQueryLikely(regex_util_, "test")); + EXPECT_FALSE(IsPrivateQueryLikely(regex_util_, "99 cake recipes")); + EXPECT_FALSE(IsPrivateQueryLikely(regex_util_, "grapefruit and pineapple")); + EXPECT_FALSE(IsPrivateQueryLikely(regex_util_, "a quick brown fox")); + + EXPECT_TRUE(IsPrivateQueryLikely( + regex_util_, + "ABC123ABC123ABC123ABC123ABC123ABC123ABC123ABC123ABC123ABC123")); + EXPECT_TRUE(IsPrivateQueryLikely( + regex_util_, + "a long query that is potentially private and should not be considered")); + EXPECT_TRUE( + IsPrivateQueryLikely(regex_util_, "aliases for me@testemail.com")); + EXPECT_TRUE( + IsPrivateQueryLikely(regex_util_, "access site with user:pass@site.com")); + EXPECT_TRUE(IsPrivateQueryLikely(regex_util_, "php $P$MArzfx58u")); + EXPECT_TRUE(IsPrivateQueryLikely( + regex_util_, "Hippopotomonstrosesquippedaliophobia symptoms")); +} + +TEST_F(WebDiscoveryPrivacyGuardTest, GeneratePrivateSearchURL) { + GURL original_url("https://example.com/search?q=aaa&country=us&f=1"); + + EXPECT_EQ(GeneratePrivateSearchURL(original_url, "a simple test query", + search_engine_pattern_) + .spec(), + "https://example.com/find?testquery=a+simple+test+query"); + EXPECT_EQ( + GeneratePrivateSearchURL(original_url, "another simple test query 123", + PatternsURLDetails()) + .spec(), + "https://example.com/search?q=another+simple+test+query+123"); + EXPECT_EQ( + GeneratePrivateSearchURL(original_url, + "special chars @#$%^&=", search_engine_pattern_) + .spec(), + "https://example.com/find?testquery=special+chars+%40%23%24%25%5E%26%3D"); +} + +TEST_F(WebDiscoveryPrivacyGuardTest, ShouldDropLongURL) { + EXPECT_FALSE(ShouldDropLongURL( + regex_util_, GURL("https://www.search1.com/search?q=test"))); + EXPECT_FALSE(ShouldDropLongURL( + regex_util_, + GURL("https://search2.com/search?query=testing+a+nice+query"))); + EXPECT_FALSE(ShouldDropLongURL( + regex_util_, + GURL("https://search2.com/search?query=quick+fox&country=us&d=1"))); + EXPECT_FALSE(ShouldDropLongURL(regex_util_, + GURL("https://www.website.com/page/test"))); + + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, + GURL("https://www.website.com/page/test?id=12823871923991"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, GURL("https://www.website.com/page/test1283192831292"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, GURL("https://www.website.com/page/1283192831292?q=1"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, + GURL("https://www.website.com/page/test?a=1&b=2&c=3&d=4&e=5"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, + GURL("https://www.website.com/page/" + "test?query=a+super+long+query+string+that+is+too+long"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, GURL("https://www.website.com/page/ayLxezLhK1Lh1H1"))); + EXPECT_TRUE(ShouldDropLongURL(regex_util_, + GURL("https://www.website.com/page/WebLogic"))); + EXPECT_TRUE(ShouldDropLongURL(regex_util_, + GURL("https://www.website.com/page/admin"))); + EXPECT_TRUE(ShouldDropLongURL(regex_util_, + GURL("https://www.website.com/page/edit/"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, GURL("https://www.website.com/page/doc?share=1"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, GURL("https://www.website.com/page/doc?user=abc"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, GURL("https://www.website.com/page/doc#logout"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, GURL("https://www.website.com/page/doc?password=abc"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, GURL("https://user:pass@www.website.com/page/test"))); + EXPECT_TRUE(ShouldDropLongURL( + regex_util_, GURL("https://www.website.com/page/test?e=test@test.com"))); +} + +TEST_F(WebDiscoveryPrivacyGuardTest, MaskURL) { + GURL url("https://www.website.com/page/test"); + auto masked_url = MaskURL(regex_util_, url); + ASSERT_TRUE(masked_url); + EXPECT_EQ(*masked_url, url); + + masked_url = MaskURL(regex_util_, GURL("https://www.website.com/page/admin")); + ASSERT_TRUE(masked_url); + EXPECT_EQ(*masked_url, "https://www.website.com/ (PROTECTED)"); + + EXPECT_FALSE(MaskURL(regex_util_, GURL("file:///etc"))); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/regex_util.cc b/components/web_discovery/browser/regex_util.cc new file mode 100644 index 000000000000..88f19f14aa69 --- /dev/null +++ b/components/web_discovery/browser/regex_util.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "brave/components/web_discovery/browser/regex_util.h" + +#include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" + +namespace { + +constexpr char kLongNumberRegexPrefix[] = "[0-9]{"; +constexpr char kLongNumberRegexSuffix[] = ",}"; +constexpr char kEmailRegex[] = + "[a-z0-9\\-_@]+(@|%40|%(25)+40)[a-z0-9\\-_]+\\.[a-z0-9\\-_]"; +constexpr char kHttpPasswordRegex[] = "[^:]+:[^@]+@"; +constexpr char kNotAlphanumericRegex[] = "[^a-zA-Z0-9]"; +constexpr char kPunctuationRegex[] = "[!\"'()*,-./:;?[\\]^_`{|}~%$=&+#]"; + +constexpr std::array kPathAndQueryStringCheckRegexes = { + "(?i)\\/admin([\\/\\?#=]|$)", + "(?i)\\/wp-admin([\\/\\?#=]|$)", + "(?i)\\/edit([\\/\\?#=]|$)", + "(?i)[&\\?#\\/]share([\\/\\?#=]|$)", + "(?i)[&\\?#\\/;]sharing([\\/\\?#=]|$)", + "(?i)[&\\?#\\/;]logout([\\/\\?#=]|$)", + "(?i)WebLogic", + "(?i)[&\\?#\\/;]token([\\/\\?#=_;]|$)", + "(?i)[&\\?#\\/;]trk([\\/\\?#=_]|$)", + "[&\\?#\\/=;](http|https)(:\\/|\\%3A\\%2F)"}; + +constexpr std::array kQueryStringAndRefCheckRegexes = { + "(?i)[&\\?#_\\-;]user", "(?i)[&\\?#_\\-;]token", + "(?i)[&\\?#_\\-;]auth", "(?i)[&\\?#_\\-;]uid", + "(?i)[&\\?#_\\-;]email", "(?i)[&\\?#_\\-;]usr", + "(?i)[&\\?#_\\-;]pin", "(?i)[&\\?#_\\-;]pwd", + "(?i)[&\\?#_\\-;]password", "(?i)[&\\?#;]u[=#]", + "(?i)[&\\?#;]url[=#]", "(?i)[&\\?#_\\-;]http", + "(?i)[&\\?#_\\-;]ref[=#]", "(?i)[&\\?#_\\-;]red[=#]", + "(?i)[&\\?#_\\-;]trk", "(?i)[&\\?#_\\-;]track", + "(?i)[&\\?#_\\-;]shar", "(?i)[&\\?#_\\-;]login", + "(?i)[&\\?#_\\-;]logout", "(?i)[&\\?#_\\-;]session", +}; + +} // anonymous namespace + +namespace web_discovery { + +RegexUtil::RegexUtil() = default; +RegexUtil::~RegexUtil() = default; + +bool RegexUtil::CheckForEmail(const std::string_view str) { + if (!email_regex_) { + email_regex_.emplace(kEmailRegex); + } + return re2::RE2::PartialMatch(str, *email_regex_); +} + +bool RegexUtil::CheckForLongNumber(const std::string_view str, + size_t max_length) { + if (!long_number_regexes_.contains(max_length)) { + auto regex_str = base::StrCat({kLongNumberRegexPrefix, + base::NumberToString(max_length + 1), + kLongNumberRegexSuffix}); + long_number_regexes_[max_length] = std::make_unique(regex_str); + } + return re2::RE2::PartialMatch(str, *long_number_regexes_[max_length]); +} + +void RegexUtil::RemovePunctuation(std::string& str) { + if (!punctuation_regex_) { + punctuation_regex_.emplace(kPunctuationRegex); + } + re2::RE2::GlobalReplace(&str, *punctuation_regex_, ""); +} + +void RegexUtil::TransformToAlphanumeric(std::string& str) { + if (!non_alphanumeric_regex_) { + non_alphanumeric_regex_.emplace(kNotAlphanumericRegex); + } + re2::RE2::GlobalReplace(&str, *non_alphanumeric_regex_, ""); +} + +bool RegexUtil::CheckPathAndQueryStringKeywords( + const std::string_view path_and_query) { + if (path_and_query_string_keyword_regexes_.empty()) { + for (const auto& regex_str : kPathAndQueryStringCheckRegexes) { + path_and_query_string_keyword_regexes_.emplace_back(regex_str); + } + } + for (const auto& regex : path_and_query_string_keyword_regexes_) { + if (re2::RE2::PartialMatch(path_and_query, regex)) { + return true; + } + } + return false; +} + +bool RegexUtil::CheckQueryStringOrRefKeywords(const std::string_view str) { + if (query_string_and_ref_keyword_regexes_.empty()) { + for (const auto& regex_str : kQueryStringAndRefCheckRegexes) { + query_string_and_ref_keyword_regexes_.emplace_back(regex_str); + } + } + for (const auto& regex : query_string_and_ref_keyword_regexes_) { + if (re2::RE2::PartialMatch(str, regex)) { + return true; + } + } + return false; +} + +bool RegexUtil::CheckQueryHTTPCredentials(const std::string_view str) { + if (!http_password_regex_) { + http_password_regex_.emplace(kHttpPasswordRegex); + } + return re2::RE2::PartialMatch(str, *http_password_regex_); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/regex_util.h b/components/web_discovery/browser/regex_util.h new file mode 100644 index 000000000000..e08dad02fd6e --- /dev/null +++ b/components/web_discovery/browser/regex_util.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_REGEX_UTIL_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_REGEX_UTIL_H_ + +#include +#include +#include +#include + +#include "base/containers/flat_map.h" +#include "third_party/re2/src/re2/re2.h" + +namespace web_discovery { + +// Lazily creates and caches pre-compiled regexes, mainly used for +// privacy risk assessment of page URLs/contents. +class RegexUtil { + public: + RegexUtil(); + ~RegexUtil(); + + RegexUtil(const RegexUtil&) = delete; + RegexUtil& operator=(const RegexUtil&) = delete; + + bool CheckForEmail(const std::string_view str); + bool CheckForLongNumber(const std::string_view str, size_t max_length); + bool CheckPathAndQueryStringKeywords(const std::string_view path_and_query); + bool CheckQueryStringOrRefKeywords(const std::string_view str); + bool CheckQueryHTTPCredentials(const std::string_view str); + void RemovePunctuation(std::string& str); + void TransformToAlphanumeric(std::string& str); + + private: + std::optional email_regex_; + // key is long number map length + base::flat_map> long_number_regexes_; + std::deque path_and_query_string_keyword_regexes_; + std::deque query_string_and_ref_keyword_regexes_; + std::optional http_password_regex_; + std::optional punctuation_regex_; + std::optional non_alphanumeric_regex_; +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_REGEX_UTIL_H_ diff --git a/components/web_discovery/browser/reporter.cc b/components/web_discovery/browser/reporter.cc new file mode 100644 index 000000000000..fa8b1a5ba8fa --- /dev/null +++ b/components/web_discovery/browser/reporter.cc @@ -0,0 +1,283 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/reporter.h" + +#include + +#include "base/containers/span_writer.h" +#include "base/json/json_writer.h" +#include "base/numerics/byte_conversions.h" +#include "base/rand_util.h" +#include "base/task/thread_pool.h" +#include "brave/components/web_discovery/browser/pref_names.h" +#include "brave/components/web_discovery/browser/signature_basename.h" +#include "brave/components/web_discovery/browser/util.h" +#include "crypto/sha2.h" +#include "services/network/public/cpp/resource_request_body.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "third_party/zlib/google/compression_utils_portable.h" +#include "third_party/zlib/zlib.h" + +namespace web_discovery { + +namespace { + +constexpr net::NetworkTrafficAnnotationTag kSubmitNetworkTrafficAnnotation = + net::DefineNetworkTrafficAnnotation("wdp_submit", R"( + semantics { + sender: "Brave Web Discovery Submission" + description: + "Sends search engine results & page interaction metrics + that are deemed private by risk assessment heuristics." + trigger: + "Requests are automatically sent every minute " + "while Brave is running, and as content is collected." + data: "Search engine results & page interaction metrics" + destination: WEBSITE + } + policy { + cookies_allowed: NO + setting: + "Users can opt-in or out via brave://settings/search" + })"); + +constexpr base::TimeDelta kRequestMaxAge = base::Hours(36); +constexpr base::TimeDelta kMinRequestInterval = + base::Minutes(1) - base::Seconds(5); +constexpr base::TimeDelta kMaxRequestInterval = + base::Minutes(1) + base::Seconds(5); +constexpr size_t kMaxRetries = 10; + +constexpr char kTypeField[] = "type"; +constexpr char kWdpType[] = "wdp"; +constexpr char kChannelField[] = "channel"; +constexpr char kBraveChannel[] = "brave"; +constexpr char kReporterVersionField[] = "ver"; +constexpr char kCurrentReporterVersion[] = "1.0"; +constexpr char kAntiDuplicatesField[] = "anti-duplicates"; +constexpr char kTimestampField[] = "ts"; +constexpr int kMaxAntiDuplicatesNonce = 10000000; +constexpr char kSenderField[] = "sender"; +constexpr char kHpnSenderValue[] = "hpnv2"; + +constexpr uint8_t kSignedMessageId = 0x03; +constexpr uint8_t kCompressedMessageId = 0x80; +// id byte + basename count + signature +constexpr size_t kSignedMessageMetadataSize = 1 + 8 + 389; +constexpr size_t kMaxCompressedMessageSize = 32767; + +constexpr char kSubmitPath[] = "/"; +constexpr char kMessageContentType[] = "application/octet-stream"; +constexpr char kKeyDateHeader[] = "Key-Date"; +constexpr char kEncryptionHeader[] = "Encryption"; + +base::Value GenerateFinalPayload(const base::Value::Dict& pre_payload) { + base::Value::Dict result = pre_payload.Clone(); + + result.Set(kTypeField, kWdpType); + result.Set(kReporterVersionField, kCurrentReporterVersion); + result.Set(kSenderField, kHpnSenderValue); + result.Set(kTimestampField, FormatServerDate(base::Time::Now())); + result.Set(kAntiDuplicatesField, base::RandInt(0, kMaxAntiDuplicatesNonce)); + result.Set(kChannelField, kBraveChannel); + + return base::Value(std::move(result)); +} + +std::optional CompressAndEncrypt( + std::vector full_signed_message, + std::string server_pub_key) { + uLongf compressed_data_size = compressBound(full_signed_message.size()); + std::vector compressed_data(compressed_data_size + 2); + if (zlib_internal::CompressHelper( + zlib_internal::ZLIB, compressed_data.data() + 2, + &compressed_data_size, full_signed_message.data(), + full_signed_message.size(), Z_DEFAULT_COMPRESSION, nullptr, + nullptr) != Z_OK) { + VLOG(1) << "Failed to compress payload"; + return std::nullopt; + } + compressed_data.resize(compressed_data_size + 2); + if (compressed_data_size > kMaxCompressedMessageSize) { + VLOG(1) << "Compressed payload exceeds limit of " + << kMaxCompressedMessageSize << " bytes"; + return std::nullopt; + } + base::ranges::copy(base::U16ToBigEndian(compressed_data_size), + compressed_data.begin()); + compressed_data[0] |= kCompressedMessageId; + return DeriveAESKeyAndEncrypt(server_pub_key, compressed_data); +} + +} // namespace + +Reporter::Reporter(PrefService* profile_prefs, + network::SharedURLLoaderFactory* shared_url_loader_factory, + CredentialSigner* credential_signer, + RegexUtil* regex_util, + const ServerConfigLoader* server_config_loader) + : profile_prefs_(profile_prefs), + shared_url_loader_factory_(shared_url_loader_factory), + credential_signer_(credential_signer), + regex_util_(regex_util), + server_config_loader_(server_config_loader), + pool_sequenced_task_runner_( + base::ThreadPool::CreateSequencedTaskRunner({})), + request_queue_(profile_prefs, + kScheduledReports, + kRequestMaxAge, + kMinRequestInterval, + kMaxRequestInterval, + kMaxRetries, + base::BindRepeating(&Reporter::PrepareRequest, + base::Unretained(this))) { + submit_url_ = GURL(GetAnonymousHPNHost() + kSubmitPath); +} + +Reporter::~Reporter() = default; + +void Reporter::ScheduleSend(base::Value::Dict payload) { + request_queue_.ScheduleRequest(base::Value(std::move(payload))); +} + +void Reporter::PrepareRequest(const base::Value& request_data) { + VLOG(1) << "Preparing request"; + if (!credential_signer_->CredentialExistsForToday()) { + // Backoff until credential is available to today + VLOG(1) << "Credential does not exist for today"; + request_queue_.NotifyRequestComplete(false); + return; + } + const auto* payload_dict = request_data.GetIfDict(); + if (!payload_dict) { + // Drop request due to bad data + VLOG(1) << "Payload is not a dictionary"; + request_queue_.NotifyRequestComplete(true); + return; + } + auto basename_result = GenerateBasename( + profile_prefs_, server_config_loader_->GetLastServerConfig(), + *regex_util_, *payload_dict); + if (!basename_result) { + // Drop request due to exceeded basename quota + VLOG(1) << "Failed to generate basename"; + request_queue_.NotifyRequestComplete(true); + return; + } + auto final_payload = GenerateFinalPayload(*payload_dict); + + std::string final_payload_json; + if (!base::JSONWriter::Write(final_payload, &final_payload_json)) { + request_queue_.NotifyRequestComplete(true); + return; + } + + auto payload_hash = crypto::SHA256HashString(final_payload_json); + credential_signer_->Sign( + std::vector(payload_hash.begin(), payload_hash.end()), + basename_result->basename, + base::BindOnce(&Reporter::OnRequestSigned, base::Unretained(this), + final_payload_json, basename_result->count_tag_hash, + basename_result->count)); +} + +void Reporter::OnRequestSigned( + std::string final_payload_json, + uint32_t count_tag_hash, + size_t basename_count, + std::optional> signature) { + if (!signature) { + request_queue_.NotifyRequestComplete(false); + return; + } + const auto& server_config = server_config_loader_->GetLastServerConfig(); + auto pub_key = + server_config.pub_keys.find(FormatServerDate(base::Time::Now())); + if (pub_key == server_config.pub_keys.end()) { + VLOG(1) << "No ECDH server public key available"; + request_queue_.NotifyRequestComplete(false); + return; + } + std::vector full_signed_message(kSignedMessageMetadataSize + + final_payload_json.size()); + base::SpanWriter message_writer(full_signed_message); + if (!message_writer.WriteU8BigEndian(kSignedMessageId) || + !message_writer.Write(base::span( + reinterpret_cast(final_payload_json.data()), + final_payload_json.size())) || + !message_writer.Write(base::DoubleToBigEndian(basename_count)) || + !message_writer.Write(*signature)) { + VLOG(1) << "Failed to pack signed message"; + request_queue_.NotifyRequestComplete(true); + return; + } + pool_sequenced_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&CompressAndEncrypt, full_signed_message, pub_key->second), + base::BindOnce(&Reporter::OnRequestCompressedAndEncrypted, + + weak_ptr_factory_.GetWeakPtr(), count_tag_hash, + basename_count)); +} + +void Reporter::OnRequestCompressedAndEncrypted( + uint32_t count_tag_hash, + size_t basename_count, + std::optional result) { + if (!result) { + request_queue_.NotifyRequestComplete(true); + return; + } + auto request = CreateResourceRequest(submit_url_); + request->method = net::HttpRequestHeaders::kPostMethod; + request->headers.SetHeader(kKeyDateHeader, + FormatServerDate(base::Time::Now())); + request->headers.SetHeader(kEncryptionHeader, + result->encoded_public_component_and_iv); + request->headers.SetHeader(kVersionHeader, + base::NumberToString(kCurrentVersion)); + + VLOG(1) << "Sending message"; + url_loader_ = network::SimpleURLLoader::Create( + std::move(request), kSubmitNetworkTrafficAnnotation); + url_loader_->AttachStringForUpload( + std::string(result->data.begin(), result->data.end()), + kMessageContentType); + url_loader_->DownloadHeadersOnly( + shared_url_loader_factory_.get(), + base::BindOnce(&Reporter::OnRequestComplete, base::Unretained(this), + count_tag_hash, basename_count)); +} + +void Reporter::OnRequestComplete( + uint32_t count_tag_hash, + size_t basename_count, + scoped_refptr headers) { + auto result = ValidateResponse(headers); + VLOG(1) << "Submission result: " << result; + if (result) { + SaveBasenameCount(profile_prefs_, count_tag_hash, basename_count); + } + request_queue_.NotifyRequestComplete(result); +} + +bool Reporter::ValidateResponse( + scoped_refptr headers) { + if (!headers) { + return false; + } + auto response_code = headers->response_code(); + if (response_code < 200 || response_code >= 300) { + if (response_code >= 500) { + // Only retry failures due to server error + return false; + } + } + return true; +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/reporter.h b/components/web_discovery/browser/reporter.h new file mode 100644 index 000000000000..f79e14fcdbc7 --- /dev/null +++ b/components/web_discovery/browser/reporter.h @@ -0,0 +1,90 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_REPORTER_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_REPORTER_H_ + +#include +#include +#include +#include + +#include "base/memory/raw_ptr.h" +#include "base/values.h" +#include "brave/components/web_discovery/browser/credential_signer.h" +#include "brave/components/web_discovery/browser/ecdh_aes.h" +#include "brave/components/web_discovery/browser/regex_util.h" +#include "brave/components/web_discovery/browser/request_queue.h" +#include "brave/components/web_discovery/browser/server_config_loader.h" +#include "net/http/http_response_headers.h" + +class PrefService; + +namespace network { +class SharedURLLoaderFactory; +class SimpleURLLoader; +} // namespace network + +namespace web_discovery { + +// Handles all functions required for reporting generated payloads: +// - zlib compression +// - ECDH key derivation + key exchange +// - AES encryption (to prevent eavesdropping by the server proxy) +// - signing the request using anonymous credentials from the +// `CredentialManager` (to prevent Sybil attacks on the server) +// - performing the request for submission +// Uses `RequestQueue` to persist and schedule submissions. Reports +// will be processed on somewhat random intervals averaging to a minute. +class Reporter { + public: + Reporter(PrefService* profile_prefs, + network::SharedURLLoaderFactory* shared_url_loader_factory, + CredentialSigner* credential_signer, + RegexUtil* regex_util, + const ServerConfigLoader* server_config_loader); + ~Reporter(); + + Reporter(const Reporter&) = delete; + Reporter& operator=(const Reporter&) = delete; + + // Schedule a generated payload for submission. + void ScheduleSend(base::Value::Dict payload); + + private: + void PrepareRequest(const base::Value& request_data); + void OnRequestSigned(std::string final_payload_json, + uint32_t count_tag_hash, + size_t basename_count, + std::optional> signature); + void OnRequestCompressedAndEncrypted(uint32_t count_tag_hash, + size_t basename_count, + std::optional result); + void OnRequestComplete(uint32_t count_tag_hash, + size_t basename_count, + scoped_refptr headers); + bool ValidateResponse(scoped_refptr headers); + + GURL submit_url_; + + raw_ptr profile_prefs_; + raw_ptr shared_url_loader_factory_; + + raw_ptr credential_signer_; + raw_ptr regex_util_; + raw_ptr server_config_loader_; + + scoped_refptr pool_sequenced_task_runner_; + + RequestQueue request_queue_; + + std::unique_ptr url_loader_; + + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_REPORTER_H_ diff --git a/components/web_discovery/browser/reporter_unittest.cc b/components/web_discovery/browser/reporter_unittest.cc new file mode 100644 index 000000000000..12b04a6247db --- /dev/null +++ b/components/web_discovery/browser/reporter_unittest.cc @@ -0,0 +1,259 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/reporter.h" + +#include +#include + +#include "base/base64.h" +#include "base/test/task_environment.h" +#include "brave/components/web_discovery/browser/regex_util.h" +#include "brave/components/web_discovery/browser/server_config_loader.h" +#include "brave/components/web_discovery/browser/util.h" +#include "brave/components/web_discovery/browser/wdp_service.h" +#include "components/prefs/testing_pref_service.h" +#include "net/http/http_status_code.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace web_discovery { + +namespace { +constexpr char kTestPubKey[] = + "BECQDFoOR0DE3wLaDidGAC/2Mpgjasf9QgJDGGLTkTdll+pW2S/" + "RgX0pkFyDjQZc6efyX3RGQKJ2cq8HOB8vZOo="; +} + +class WebDiscoveryReporterTest : public testing::Test { + public: + WebDiscoveryReporterTest() + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), + shared_url_loader_factory_( + base::MakeRefCounted( + &url_loader_factory_)) {} + ~WebDiscoveryReporterTest() override = default; + + // testing::Test: + void SetUp() override { + WDPService::RegisterProfilePrefs(profile_prefs_.registry()); + auto server_config = std::make_unique(); + + auto action_config = std::make_unique(); + action_config->keys.push_back("q->url"); + action_config->period = 24; + action_config->limit = 3; + server_config->source_map_actions["query"] = std::move(action_config); + + for (size_t i = 0; i < 3; i++) { + base::Time date = base::Time::Now() + base::Days(i); + server_config->pub_keys[FormatServerDate(date)] = kTestPubKey; + } + + server_config_loader_ = std::make_unique( + nullptr, base::FilePath(), nullptr, base::DoNothing(), + base::DoNothing()); + server_config_loader_->SetLastServerConfigForTesting( + std::move(server_config)); + + url_loader_factory_.SetInterceptor(base::BindRepeating( + &WebDiscoveryReporterTest::HandleRequest, base::Unretained(this))); + + AddCredentialForToday(); + SetupReporter(); + } + + protected: + class TestCredentialSigner : public CredentialSigner { + public: + bool CredentialExistsForToday() override { + std::string today = FormatServerDate(base::Time::Now()); + return allowed_credentials_.contains(today); + } + + bool Sign(std::vector msg, + std::vector basename, + SignCallback callback) override { + if (CredentialExistsForToday()) { + std::vector dummy_signature( + {static_cast(sign_count_ + 1)}); + std::move(callback).Run(std::move(dummy_signature)); + sign_count_++; + } else { + std::move(callback).Run(std::nullopt); + } + return true; + } + + size_t sign_count_ = 0; + base::flat_set allowed_credentials_; + }; + + void SetupReporter() { + reporter_ = std::make_unique( + &profile_prefs_, shared_url_loader_factory_.get(), &credential_signer_, + ®ex_util_, server_config_loader_.get()); + } + + void AddCredentialForToday() { + std::string today = FormatServerDate(base::Time::Now()); + credential_signer_.allowed_credentials_.insert(today); + } + + base::Value::Dict GenerateTestPayload() { + base::Value::Dict payload; + base::Value::Dict inner_payload; + inner_payload.Set("q", "test query"); + payload.Set("payload", std::move(inner_payload)); + payload.Set("action", "query"); + return payload; + } + + base::test::TaskEnvironment task_environment_; + std::unique_ptr reporter_; + TestCredentialSigner credential_signer_; + size_t report_requests_made_ = 0; + net::HttpStatusCode submit_status_code_ = net::HTTP_OK; + + private: + void HandleRequest(const network::ResourceRequest& request) { + url_loader_factory_.ClearResponses(); + + EXPECT_EQ(request.url.spec(), GetAnonymousHPNHost() + "/"); + EXPECT_EQ(request.method, net::HttpRequestHeaders::kPostMethod); + std::string key_date, encryption, version; + request.headers.GetHeader("Key-Date", &key_date); + request.headers.GetHeader("Encryption", &encryption); + request.headers.GetHeader(kVersionHeader, &version); + EXPECT_EQ(key_date, FormatServerDate(base::Time::Now())); + auto decoded_pubkey_and_iv = base::Base64Decode(encryption); + ASSERT_TRUE(decoded_pubkey_and_iv); + EXPECT_EQ(decoded_pubkey_and_iv->size(), 78u); + EXPECT_EQ(version, base::NumberToString(kCurrentVersion)); + + std::string response; + const auto* elements = request.request_body->elements(); + ASSERT_EQ(elements->size(), 1u); + ASSERT_EQ(elements->at(0).type(), network::DataElement::Tag::kBytes); + auto body = elements->at(0).As().bytes(); + EXPECT_FALSE(body.empty()); + + url_loader_factory_.AddResponse(request.url.spec(), "", + submit_status_code_); + report_requests_made_++; + } + + std::unique_ptr server_config_loader_; + RegexUtil regex_util_; + TestingPrefServiceSimple profile_prefs_; + network::TestURLLoaderFactory url_loader_factory_; + scoped_refptr shared_url_loader_factory_; +}; + +TEST_F(WebDiscoveryReporterTest, BasicReport) { + reporter_->ScheduleSend(GenerateTestPayload()); + reporter_->ScheduleSend(GenerateTestPayload()); + EXPECT_EQ(report_requests_made_, 0u); + EXPECT_EQ(credential_signer_.sign_count_, 0u); + + task_environment_.FastForwardBy(base::Seconds(30)); + + EXPECT_EQ(report_requests_made_, 0u); + EXPECT_EQ(credential_signer_.sign_count_, 0u); + + task_environment_.FastForwardBy(base::Seconds(60)); + + EXPECT_EQ(report_requests_made_, 1u); + EXPECT_EQ(credential_signer_.sign_count_, 1u); + + task_environment_.FastForwardBy(base::Seconds(80)); + + EXPECT_EQ(report_requests_made_, 2u); + EXPECT_EQ(credential_signer_.sign_count_, 2u); + report_requests_made_ = 0; + credential_signer_.sign_count_ = 0; + + task_environment_.FastForwardBy(base::Minutes(5)); + + EXPECT_EQ(report_requests_made_, 0u); + EXPECT_EQ(credential_signer_.sign_count_, 0u); +} + +TEST_F(WebDiscoveryReporterTest, LoadReportFromStorage) { + reporter_->ScheduleSend(GenerateTestPayload()); + EXPECT_EQ(report_requests_made_, 0u); + EXPECT_EQ(credential_signer_.sign_count_, 0u); + + SetupReporter(); + + task_environment_.FastForwardBy(base::Seconds(30)); + + EXPECT_EQ(report_requests_made_, 0u); + EXPECT_EQ(credential_signer_.sign_count_, 0u); + + task_environment_.FastForwardBy(base::Seconds(60)); + + EXPECT_EQ(report_requests_made_, 1u); + EXPECT_EQ(credential_signer_.sign_count_, 1u); +} + +TEST_F(WebDiscoveryReporterTest, CredentialUnavailableRetry) { + task_environment_.FastForwardBy(base::Days(1)); + + reporter_->ScheduleSend(GenerateTestPayload()); + EXPECT_EQ(report_requests_made_, 0u); + EXPECT_EQ(credential_signer_.sign_count_, 0u); + + task_environment_.FastForwardBy(base::Seconds(150)); + EXPECT_EQ(report_requests_made_, 0u); + EXPECT_EQ(credential_signer_.sign_count_, 0u); + + AddCredentialForToday(); + task_environment_.FastForwardBy(base::Seconds(120)); + + EXPECT_EQ(report_requests_made_, 1u); + EXPECT_EQ(credential_signer_.sign_count_, 1u); + report_requests_made_ = 0; + credential_signer_.sign_count_ = 0; + + task_environment_.FastForwardBy(base::Minutes(5)); + + EXPECT_EQ(report_requests_made_, 0u); + EXPECT_EQ(credential_signer_.sign_count_, 0u); +} + +TEST_F(WebDiscoveryReporterTest, ServerUnavailableRetry) { + submit_status_code_ = net::HTTP_INTERNAL_SERVER_ERROR; + reporter_->ScheduleSend(GenerateTestPayload()); + + task_environment_.FastForwardBy(base::Seconds(80)); + EXPECT_GE(report_requests_made_, 1u); + EXPECT_GE(credential_signer_.sign_count_, 1u); + + size_t prev_report_requests_made = report_requests_made_; + size_t prev_sign_count = credential_signer_.sign_count_; + task_environment_.FastForwardBy(base::Seconds(100)); + + EXPECT_GT(report_requests_made_, prev_report_requests_made); + EXPECT_GT(credential_signer_.sign_count_, prev_sign_count); + report_requests_made_ = 0; + credential_signer_.sign_count_ = 0; + + submit_status_code_ = net::HTTP_OK; + task_environment_.FastForwardBy(base::Seconds(100)); + + EXPECT_EQ(report_requests_made_, 1u); + EXPECT_EQ(credential_signer_.sign_count_, 1u); + report_requests_made_ = 0; + credential_signer_.sign_count_ = 0; + + task_environment_.FastForwardBy(base::Minutes(5)); + + EXPECT_EQ(report_requests_made_, 0u); + EXPECT_EQ(credential_signer_.sign_count_, 0u); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/request_queue.cc b/components/web_discovery/browser/request_queue.cc new file mode 100644 index 000000000000..f5743a3500c6 --- /dev/null +++ b/components/web_discovery/browser/request_queue.cc @@ -0,0 +1,120 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/request_queue.h" + +#include + +#include "base/rand_util.h" +#include "brave/components/web_discovery/browser/util.h" +#include "components/prefs/scoped_user_pref_update.h" + +namespace web_discovery { + +namespace { + +constexpr char kRequestTimeKey[] = "request_time"; +constexpr char kRetriesKey[] = "retries"; +constexpr char kDataKey[] = "data"; + +} // namespace + +RequestQueue::RequestQueue( + PrefService* profile_prefs, + const char* list_pref_name, + base::TimeDelta request_max_age, + base::TimeDelta min_request_interval, + base::TimeDelta max_request_interval, + size_t max_retries, + base::RepeatingCallback start_request_callback) + : profile_prefs_(profile_prefs), + list_pref_name_(list_pref_name), + backoff_entry_(&kBackoffPolicy), + request_max_age_(request_max_age), + min_request_interval_(min_request_interval), + max_request_interval_(max_request_interval), + max_retries_(max_retries), + start_request_callback_(start_request_callback) { + StartFetchTimer(false); +} + +RequestQueue::~RequestQueue() = default; + +void RequestQueue::ScheduleRequest(base::Value request_data) { + base::Value::Dict fetch_dict; + fetch_dict.Set(kDataKey, std::move(request_data)); + fetch_dict.Set(kRequestTimeKey, + static_cast(base::Time::Now().ToTimeT())); + + ScopedListPrefUpdate update(profile_prefs_, list_pref_name_); + update->Append(std::move(fetch_dict)); + + if (!fetch_timer_.IsRunning()) { + StartFetchTimer(false); + } +} + +std::optional RequestQueue::NotifyRequestComplete(bool success) { + backoff_entry_.InformOfRequest(success); + + ScopedListPrefUpdate update(profile_prefs_, list_pref_name_); + auto& request_dict = update->front().GetDict(); + + std::optional removed_value; + bool use_backoff_delta = false; + bool should_remove = success; + + if (!success) { + use_backoff_delta = true; + auto retries = request_dict.FindInt(kRetriesKey); + if (retries && static_cast(*retries + 1) >= max_retries_) { + should_remove = true; + } else { + request_dict.Set(kRetriesKey, retries.value_or(0) + 1); + } + } + + if (should_remove) { + auto* data = request_dict.Find(kDataKey); + removed_value = data ? data->Clone() : base::Value(); + update->erase(update->begin()); + } + + StartFetchTimer(use_backoff_delta); + return removed_value; +} + +void RequestQueue::OnFetchTimer() { + ScopedListPrefUpdate update(profile_prefs_, list_pref_name_); + for (auto it = update->begin(); it != update->end();) { + const auto* fetch_dict = it->GetIfDict(); + const auto request_time = + fetch_dict ? fetch_dict->FindDouble(kRequestTimeKey) : std::nullopt; + const auto* data = fetch_dict ? fetch_dict->Find(kDataKey) : nullptr; + if (!request_time || + (base::Time::Now() - base::Time::FromTimeT(static_cast( + *request_time))) > request_max_age_ || + !data) { + it = update->erase(it); + continue; + } + start_request_callback_.Run(*data); + return; + } +} + +void RequestQueue::StartFetchTimer(bool use_backoff_delta) { + base::TimeDelta delta; + if (use_backoff_delta) { + delta = backoff_entry_.GetTimeUntilRelease(); + } else { + delta = base::RandTimeDelta(min_request_interval_, max_request_interval_); + } + fetch_timer_.Start( + FROM_HERE, delta, + base::BindOnce(&RequestQueue::OnFetchTimer, base::Unretained(this))); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/request_queue.h b/components/web_discovery/browser/request_queue.h new file mode 100644 index 000000000000..fd385bcee22a --- /dev/null +++ b/components/web_discovery/browser/request_queue.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_REQUEST_QUEUE_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_REQUEST_QUEUE_H_ + +#include "base/functional/callback.h" +#include "base/memory/raw_ptr.h" +#include "base/timer/timer.h" +#include "base/values.h" +#include "net/base/backoff_entry.h" + +class PrefService; + +namespace web_discovery { + +// Persists and schedules requests on randomized intervals within +// an interval range. If request failures exceed the threshold defined in +// `max_retries`, the request will be dropped from the list. If a persisted +// request age exceeds `request_max_age`, the request will be dropped. +class RequestQueue { + public: + RequestQueue( + PrefService* profile_prefs, + const char* list_pref_name, + base::TimeDelta request_max_age, + base::TimeDelta min_request_interval, + base::TimeDelta max_request_interval, + size_t max_retries, + base::RepeatingCallback start_request_callback); + ~RequestQueue(); + + RequestQueue(const RequestQueue&) = delete; + RequestQueue& operator=(const RequestQueue&) = delete; + + // Persist and schedule a request. The arbitrary data will be passed + // to `start_request_callback` on the scheduled interval. + void ScheduleRequest(base::Value request_data); + // Returns data value if request is deleted from queue, due to the retry limit + // or success + std::optional NotifyRequestComplete(bool success); + + private: + void OnFetchTimer(); + void StartFetchTimer(bool use_backoff_delta); + + raw_ptr profile_prefs_; + const char* list_pref_name_; + + net::BackoffEntry backoff_entry_; + + base::TimeDelta request_max_age_; + base::TimeDelta min_request_interval_; + base::TimeDelta max_request_interval_; + size_t max_retries_; + base::RepeatingCallback start_request_callback_; + + base::OneShotTimer fetch_timer_; +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_REQUEST_QUEUE_H_ diff --git a/components/web_discovery/browser/rsa.cc b/components/web_discovery/browser/rsa.cc new file mode 100644 index 000000000000..73f8be74ff57 --- /dev/null +++ b/components/web_discovery/browser/rsa.cc @@ -0,0 +1,121 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/rsa.h" + +#include + +#include "base/base64.h" +#include "third_party/boringssl/src/include/openssl/bn.h" +#include "third_party/boringssl/src/include/openssl/bytestring.h" +#include "third_party/boringssl/src/include/openssl/mem.h" +#include "third_party/boringssl/src/include/openssl/rsa.h" + +namespace web_discovery { + +namespace { + +constexpr size_t kRsaKeySize = 2048; + +} // namespace + +RSAKeyInfo::RSAKeyInfo() = default; +RSAKeyInfo::~RSAKeyInfo() = default; + +std::unique_ptr GenerateRSAKeyPair() { + bssl::UniquePtr ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr)); + BIGNUM* bn = BN_new(); + if (!ctx || !bn || !BN_set_word(bn, RSA_F4)) { + return nullptr; + } + + if (!EVP_PKEY_keygen_init(ctx.get()) || + !EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx.get(), bn) || + !EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), kRsaKeySize)) { + return nullptr; + } + + auto info = std::make_unique(); + + EVP_PKEY* private_key = nullptr; + if (!EVP_PKEY_keygen(ctx.get(), &private_key)) { + return nullptr; + } + + info->key_pair = bssl::UniquePtr(private_key); + + CBB pub_cbb; + uint8_t* pub_der; + size_t pub_der_len; + if (!CBB_init(&pub_cbb, 0) || + !EVP_marshal_public_key(&pub_cbb, private_key) || + !CBB_finish(&pub_cbb, &pub_der, &pub_der_len)) { + CBB_cleanup(&pub_cbb); + return nullptr; + } + + info->public_key_b64 = base::Base64Encode( + base::span(static_cast(pub_der), pub_der_len)); + OPENSSL_free(pub_der); + + CBB priv_cbb; + uint8_t* priv_der; + size_t priv_der_len; + if (!CBB_init(&priv_cbb, 0) || + !EVP_marshal_private_key(&priv_cbb, private_key) || + !CBB_finish(&priv_cbb, &priv_der, &priv_der_len)) { + CBB_cleanup(&priv_cbb); + return nullptr; + } + + info->private_key_b64 = base::Base64Encode( + base::span(static_cast(priv_der), priv_der_len)); + OPENSSL_free(priv_der); + + return info; +} + +EVPKeyPtr ImportRSAKeyPair(const std::string& private_key_b64) { + std::string decoded_key; + if (!base::Base64Decode(private_key_b64, &decoded_key)) { + return nullptr; + } + + const unsigned char* pkey_data = + reinterpret_cast(decoded_key.data()); + return EVPKeyPtr( + d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &pkey_data, decoded_key.length())); +} + +std::optional RSASign(const EVPKeyPtr& key, + base::span message) { + CHECK(key); + bssl::ScopedEVP_MD_CTX ctx; + + if (!EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, + key.get())) { + return std::nullopt; + } + + size_t sig_len; + // Write max size of signature to sig_len + if (!EVP_DigestSign(ctx.get(), nullptr, &sig_len, message.data(), + message.size())) { + return std::nullopt; + } + + std::vector sig(sig_len); + // Write actual signature to sig + if (!EVP_DigestSign(ctx.get(), sig.data(), &sig_len, message.data(), + message.size())) { + return std::nullopt; + } + + // Truncate signature to actual sig_len + sig.resize(sig_len); + return base::Base64Encode(sig); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/rsa.h b/components/web_discovery/browser/rsa.h new file mode 100644 index 000000000000..9a261b91273c --- /dev/null +++ b/components/web_discovery/browser/rsa.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_RSA_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_RSA_H_ + +#include +#include +#include + +#include "base/containers/span.h" +#include "third_party/boringssl/src/include/openssl/evp.h" + +namespace web_discovery { + +using EVPKeyPtr = bssl::UniquePtr; + +struct RSAKeyInfo { + RSAKeyInfo(); + ~RSAKeyInfo(); + EVPKeyPtr key_pair; + std::string private_key_b64; + std::string public_key_b64; +}; + +std::unique_ptr GenerateRSAKeyPair(); + +EVPKeyPtr ImportRSAKeyPair(const std::string& private_key_b64); + +std::optional RSASign(const EVPKeyPtr& key, + base::span message); + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_RSA_H_ diff --git a/components/web_discovery/browser/server_config_loader.cc b/components/web_discovery/browser/server_config_loader.cc new file mode 100644 index 000000000000..d1e73f8acd8c --- /dev/null +++ b/components/web_discovery/browser/server_config_loader.cc @@ -0,0 +1,444 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/server_config_loader.h" + +#include + +#include "base/barrier_callback.h" +#include "base/base64.h" +#include "base/containers/fixed_flat_set.h" +#include "base/files/file_util.h" +#include "base/functional/bind.h" +#include "base/json/json_reader.h" +#include "base/location.h" +#include "base/rand_util.h" +#include "base/task/thread_pool.h" +#include "base/values.h" +#include "brave/components/web_discovery/browser/pref_names.h" +#include "brave/components/web_discovery/browser/util.h" +#include "components/prefs/pref_service.h" +#include "net/traffic_annotation/network_traffic_annotation.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "third_party/zlib/google/compression_utils.h" + +namespace web_discovery { + +namespace { + +constexpr base::TimeDelta kMinReloadInterval = base::Hours(1); +constexpr base::TimeDelta kMaxReloadInterval = base::Hours(4); + +constexpr base::TimeDelta kPatternsMaxAge = base::Hours(2); +constexpr base::TimeDelta kPatternsRequestLatestDelay = base::Minutes(3); +constexpr base::TimeDelta kPatternsRequestInitDelay = base::Seconds(15); + +constexpr size_t kPatternsMaxFileSize = 128000; + +constexpr net::NetworkTrafficAnnotationTag kNetworkTrafficAnnotation = + net::DefineNetworkTrafficAnnotation("wdp_config", R"( + semantics { + sender: "Brave Web Discovery Server Configuration Fetch" + description: + "Requests server configuration needed to send Web Discovery " + "measurements to Brave servers." + trigger: + "Requests are automatically sent at intervals " + "while Brave is running." + data: "Configuration attributes" + destination: WEBSITE + } + policy { + cookies_allowed: NO + setting: + "Users can opt-in or out via brave://settings/search" + })"); + +constexpr char kGroupPubKeysFieldName[] = "groupPubKeys"; +constexpr char kPubKeysFieldName[] = "pubKeys"; +constexpr char kMinVersionFieldName[] = "minVersion"; +constexpr char kKeysFieldName[] = "keys"; +constexpr char kLimitFieldName[] = "limit"; +constexpr char kPeriodFieldName[] = "period"; +constexpr char kSourceMapFieldName[] = "sourceMap"; +constexpr char kSourceMapActionsFieldName[] = "actions"; +constexpr char kLocationFieldName[] = "location"; + +constexpr char kCollectorConfigPathWithFields[] = + "/config?fields=minVersion,groupPubKeys,pubKeys,sourceMap"; +constexpr char kQuorumConfigPath[] = "/config"; +constexpr char kPatternsFilename[] = "wdp_patterns.json"; + +constexpr char kOmittedLocationValue[] = "--"; +constexpr auto kAllowedReportLocations = + base::MakeFixedFlatSet( + {"ar", "at", "au", "be", "br", "ca", "ch", "cn", "cz", "de", + "dk", "es", "fi", "fr", "gb", "gr", "hu", "in", "it", "jp", + "mx", "nl", "no", "pl", "ro", "ru", "se", "ua", "us"}); + +KeyMap ParseKeys(const base::Value::Dict& encoded_keys) { + KeyMap map; + for (const auto [date, key_b64] : encoded_keys) { + std::vector decoded_data; + // Decode to check for valid base64 + if (!base::Base64Decode(key_b64.GetString())) { + continue; + } + map[date] = key_b64.GetString(); + } + return map; +} + +base::flat_map> +ParseSourceMapActionConfigs(const base::Value::Dict& configs_dict) { + base::flat_map> map; + for (const auto [action, config_dict_val] : configs_dict) { + auto* config_dict = config_dict_val.GetIfDict(); + if (!config_dict) { + continue; + } + auto& action_config = map[action]; + if (!action_config) { + action_config = std::make_unique(); + } + auto* keys_list = config_dict->FindList(kKeysFieldName); + if (keys_list) { + for (const auto& key_val : *keys_list) { + if (key_val.is_string()) { + action_config->keys.push_back(key_val.GetString()); + } + } + } + auto limit = config_dict->FindInt(kLimitFieldName); + auto period = config_dict->FindInt(kPeriodFieldName); + + action_config->limit = limit && limit > 0 ? *limit : 1; + action_config->period = period && period > 0 ? *period : 24; + } + return map; +} + +std::optional GunzipContents(std::string gzipped_contents) { + std::string result; + if (!compression::GzipUncompress(gzipped_contents, &result)) { + return std::nullopt; + } + return result; +} + +bool WritePatternsFile(base::FilePath patterns_path, std::string contents) { + return base::WriteFile(patterns_path, contents); +} + +std::optional ReadPatternsFile(base::FilePath patterns_path) { + std::string contents; + if (!base::ReadFileToStringWithMaxSize(patterns_path, &contents, + kPatternsMaxFileSize)) { + return std::nullopt; + } + return contents; +} + +} // namespace + +SourceMapActionConfig::SourceMapActionConfig() = default; +SourceMapActionConfig::~SourceMapActionConfig() = default; + +ServerConfig::ServerConfig() = default; +ServerConfig::~ServerConfig() = default; + +ServerConfigLoader::ServerConfigLoader( + PrefService* local_state, + base::FilePath user_data_dir, + network::SharedURLLoaderFactory* shared_url_loader_factory, + base::RepeatingClosure config_callback, + base::RepeatingClosure patterns_callback) + : local_state_(local_state), + pool_sequenced_task_runner_( + base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})), + shared_url_loader_factory_(shared_url_loader_factory), + config_callback_(config_callback), + patterns_callback_(patterns_callback), + config_backoff_entry_(&kBackoffPolicy), + patterns_backoff_entry_(&kBackoffPolicy) { + collector_config_url_ = + GURL(GetAnonymousHPNHost() + kCollectorConfigPathWithFields); + quorum_config_url_ = GURL(GetQuorumHost() + kQuorumConfigPath); + patterns_url_ = GetPatternsEndpoint(); + + patterns_path_ = user_data_dir.AppendASCII(kPatternsFilename); +} + +ServerConfigLoader::~ServerConfigLoader() = default; + +const ServerConfig& ServerConfigLoader::GetLastServerConfig() const { + CHECK(last_loaded_server_config_); + return *last_loaded_server_config_; +} + +const PatternsGroup& ServerConfigLoader::GetLastPatterns() const { + CHECK(last_loaded_patterns_); + return *last_loaded_patterns_; +} + +void ServerConfigLoader::SetLastServerConfigForTesting( + std::unique_ptr server_config) { + last_loaded_server_config_ = std::move(server_config); +} + +void ServerConfigLoader::SetLastPatternsForTesting( + std::unique_ptr patterns) { + last_loaded_patterns_ = std::move(patterns); +} + +void ServerConfigLoader::LoadConfigs() { + if (collector_config_url_loader_ || quorum_config_url_loader_) { + // Another request is in progress + return; + } + auto collector_resource_request = + CreateResourceRequest(collector_config_url_); + auto quorum_resource_request = CreateResourceRequest(quorum_config_url_); + + collector_config_url_loader_ = network::SimpleURLLoader::Create( + std::move(collector_resource_request), kNetworkTrafficAnnotation); + quorum_config_url_loader_ = network::SimpleURLLoader::Create( + std::move(quorum_resource_request), kNetworkTrafficAnnotation); + + auto callback = base::BarrierCallback>( + 2, base::BindOnce(&ServerConfigLoader::OnConfigResponses, + base::Unretained(this))); + + collector_config_url_loader_->DownloadToString( + shared_url_loader_factory_.get(), callback, kMaxResponseSize); + quorum_config_url_loader_->DownloadToString(shared_url_loader_factory_.get(), + callback, kMaxResponseSize); +} + +void ServerConfigLoader::OnConfigResponses( + std::vector> response_bodies) { + CHECK_EQ(response_bodies.size(), 2u); + base::Time update_time = base::Time::Now(); + bool result = ProcessConfigResponses(response_bodies[0], response_bodies[1]); + + config_backoff_entry_.InformOfRequest(result); + + collector_config_url_loader_ = nullptr; + quorum_config_url_loader_ = nullptr; + + if (!result) { + update_time += config_backoff_entry_.GetTimeUntilRelease(); + } else { + update_time += base::RandTimeDelta(kMinReloadInterval, kMaxReloadInterval); + + SchedulePatternsRequest(); + } + + config_update_timer_.Start( + FROM_HERE, update_time, + base::BindOnce(&ServerConfigLoader::LoadConfigs, base::Unretained(this))); +} + +bool ServerConfigLoader::ProcessConfigResponses( + const std::optional& collector_response_body, + const std::optional& quorum_response_body) { + auto* collector_response_info = collector_config_url_loader_->ResponseInfo(); + auto* quorum_response_info = quorum_config_url_loader_->ResponseInfo(); + if (!collector_response_body || !collector_response_info || + !quorum_response_body || !collector_response_info || + collector_response_info->headers->response_code() != 200 || + quorum_response_info->headers->response_code() != 200) { + VLOG(1) << "Failed to fetch server config"; + return false; + } + + auto collector_parsed_json = base::JSONReader::ReadAndReturnValueWithError( + *collector_response_body, base::JSON_PARSE_RFC); + auto quorum_parsed_json = base::JSONReader::ReadAndReturnValueWithError( + *quorum_response_body, base::JSON_PARSE_RFC); + + if (!collector_parsed_json.has_value() || !quorum_parsed_json.has_value()) { + VLOG(1) << "Failed to parse server config json"; + return false; + } + + const auto* collector_root = collector_parsed_json.value().GetIfDict(); + const auto* quorum_root = quorum_parsed_json.value().GetIfDict(); + if (!collector_root || !quorum_root) { + VLOG(1) << "Failed to parse server config: not a dict"; + return false; + } + + const auto min_version = collector_root->FindInt(kMinVersionFieldName); + if (min_version && *min_version > kCurrentVersion) { + VLOG(1) << "Server minimum version is higher than current version, failing"; + return false; + } + + auto config = std::make_unique(); + + const auto* group_pub_keys = collector_root->FindDict(kGroupPubKeysFieldName); + if (!group_pub_keys) { + VLOG(1) << "Failed to retrieve groupPubKeys from server config"; + return false; + } + const auto* pub_keys = collector_root->FindDict(kPubKeysFieldName); + if (!pub_keys) { + VLOG(1) << "Failed to retrieve pubKeys from server config"; + return false; + } + const auto* source_map = collector_root->FindDict(kSourceMapFieldName); + const auto* source_map_actions = + source_map ? source_map->FindDict(kSourceMapActionsFieldName) : nullptr; + if (!source_map_actions) { + VLOG(1) << "Failed to retrieve sourceMap from server config"; + return false; + } + + const auto* location = quorum_root->FindString(kLocationFieldName); + if (location && kAllowedReportLocations.contains(*location)) { + config->location = *location; + } else { + config->location = kOmittedLocationValue; + } + + config->group_pub_keys = ParseKeys(*group_pub_keys); + config->pub_keys = ParseKeys(*pub_keys); + config->source_map_actions = ParseSourceMapActionConfigs(*source_map_actions); + + last_loaded_server_config_ = std::move(config); + config_callback_.Run(); + return true; +} + +void ServerConfigLoader::LoadStoredPatterns() { + pool_sequenced_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce(&ReadPatternsFile, patterns_path_), + base::BindOnce(&ServerConfigLoader::OnPatternsFileLoaded, + weak_ptr_factory_.GetWeakPtr())); +} + +void ServerConfigLoader::OnPatternsFileLoaded( + std::optional patterns_json) { + if (!patterns_json) { + VLOG(1) << "Failed to load local patterns file"; + local_state_->ClearPref(kPatternsRetrievalTime); + SchedulePatternsRequest(); + return; + } + auto parsed_patterns = ParsePatterns(*patterns_json); + if (!parsed_patterns) { + local_state_->ClearPref(kPatternsRetrievalTime); + SchedulePatternsRequest(); + return; + } + last_loaded_patterns_ = std::move(parsed_patterns); + patterns_callback_.Run(); +} + +void ServerConfigLoader::SchedulePatternsRequest() { + base::Time update_time = base::Time::Now(); + base::TimeDelta time_since_last_retrieval = + base::Time::Now() - local_state_->GetTime(kPatternsRetrievalTime); + if (time_since_last_retrieval >= kPatternsMaxAge) { + update_time += kPatternsRequestInitDelay; + } else { + if (!patterns_first_request_made_) { + LoadStoredPatterns(); + } + update_time += kPatternsMaxAge - time_since_last_retrieval + + base::RandTimeDelta({}, kPatternsRequestLatestDelay); + } + patterns_first_request_made_ = true; + patterns_update_timer_.Start( + FROM_HERE, update_time, + base::BindOnce(&ServerConfigLoader::RequestPatterns, + base::Unretained(this))); +} + +void ServerConfigLoader::RequestPatterns() { + if (patterns_url_loader_) { + return; + } + auto resource_request = CreateResourceRequest(patterns_url_); + + patterns_url_loader_ = network::SimpleURLLoader::Create( + std::move(resource_request), kNetworkTrafficAnnotation); + + patterns_url_loader_->DownloadToString( + shared_url_loader_factory_.get(), + base::BindOnce(&ServerConfigLoader::OnPatternsResponse, + base::Unretained(this)), + kMaxResponseSize); +} + +void ServerConfigLoader::HandlePatternsStatus(bool result) { + patterns_url_loader_ = nullptr; + patterns_backoff_entry_.InformOfRequest(result); + + if (result) { + SchedulePatternsRequest(); + return; + } + + patterns_update_timer_.Start( + FROM_HERE, + base::Time::Now() + patterns_backoff_entry_.GetTimeUntilRelease(), + base::BindOnce(&ServerConfigLoader::RequestPatterns, + base::Unretained(this))); +} + +void ServerConfigLoader::OnPatternsResponse( + std::optional response_body) { + auto* response_info = patterns_url_loader_->ResponseInfo(); + if (!response_body || !response_info || + response_info->headers->response_code() != 200) { + VLOG(1) << "Failed to retrieve patterns file"; + HandlePatternsStatus(false); + return; + } + pool_sequenced_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce(&GunzipContents, *response_body), + base::BindOnce(&ServerConfigLoader::OnPatternsGunzip, + weak_ptr_factory_.GetWeakPtr())); +} + +void ServerConfigLoader::OnPatternsGunzip( + std::optional patterns_json) { + if (!patterns_json) { + VLOG(1) << "Failed to decompress patterns file"; + HandlePatternsStatus(false); + return; + } + auto parsed_patterns = ParsePatterns(*patterns_json); + if (!parsed_patterns) { + HandlePatternsStatus(false); + return; + } + pool_sequenced_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&WritePatternsFile, patterns_path_, *patterns_json), + base::BindOnce(&ServerConfigLoader::OnPatternsWritten, + weak_ptr_factory_.GetWeakPtr(), + std::move(parsed_patterns))); +} + +void ServerConfigLoader::OnPatternsWritten( + std::unique_ptr parsed_group, + bool result) { + if (!result) { + VLOG(1) << "Failed to write patterns file"; + HandlePatternsStatus(false); + return; + } + local_state_->SetTime(kPatternsRetrievalTime, base::Time::Now()); + HandlePatternsStatus(true); + last_loaded_patterns_ = std::move(parsed_group); + patterns_callback_.Run(); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/server_config_loader.h b/components/web_discovery/browser/server_config_loader.h new file mode 100644 index 000000000000..856073249b34 --- /dev/null +++ b/components/web_discovery/browser/server_config_loader.h @@ -0,0 +1,143 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_SERVER_CONFIG_LOADER_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_SERVER_CONFIG_LOADER_H_ + +#include +#include +#include +#include + +#include "base/containers/flat_map.h" +#include "base/files/file_path.h" +#include "base/memory/raw_ptr.h" +#include "base/timer/wall_clock_timer.h" +#include "brave/components/web_discovery/browser/patterns.h" +#include "net/base/backoff_entry.h" +#include "url/gurl.h" + +class PrefService; + +namespace network { +class SharedURLLoaderFactory; +class SimpleURLLoader; +} // namespace network + +namespace web_discovery { + +using KeyMap = base::flat_map; + +struct SourceMapActionConfig { + SourceMapActionConfig(); + ~SourceMapActionConfig(); + + SourceMapActionConfig(const SourceMapActionConfig&) = delete; + SourceMapActionConfig& operator=(const SourceMapActionConfig&) = delete; + + std::vector keys; + size_t limit; + size_t period; +}; + +struct ServerConfig { + ServerConfig(); + ~ServerConfig(); + + ServerConfig(const ServerConfig&) = delete; + ServerConfig& operator=(const ServerConfig&) = delete; + + KeyMap group_pub_keys; + KeyMap pub_keys; + + base::flat_map> + source_map_actions; + + std::string location; +}; + +// Handles retrieval, updating and caching of the following server +// configurations: +// - HPN server config: contains public keys, and "source maps" used +// for generating basenames. +// - "quorum" config: contains the country code of the user +// - patterns: contains the rules for scraping/submission of certain pages +class ServerConfigLoader { + public: + ServerConfigLoader(PrefService* local_state, + base::FilePath user_data_dir, + network::SharedURLLoaderFactory* shared_url_loader_factory, + base::RepeatingClosure config_callback, + base::RepeatingClosure patterns_callback); + ~ServerConfigLoader(); + + ServerConfigLoader(const ServerConfigLoader&) = delete; + ServerConfigLoader& operator=(const ServerConfigLoader&) = delete; + + // Loads all three server configurations. Update requests will be scheduled + // once complete. + void LoadConfigs(); + + // Returns the last loaded server config, which is a combination of the + // HPN and "quorum" configs. May only call after the config_callback is + // triggered. + const ServerConfig& GetLastServerConfig() const; + // Returns the pattern config. May only call after the patterns_callback is + // triggered. + const PatternsGroup& GetLastPatterns() const; + + void SetLastServerConfigForTesting( + std::unique_ptr server_config); + void SetLastPatternsForTesting(std::unique_ptr patterns); + + private: + void OnConfigResponses( + std::vector> response_bodies); + bool ProcessConfigResponses( + const std::optional& collector_response_body, + const std::optional& quorum_response_body); + + void LoadStoredPatterns(); + void OnPatternsFileLoaded(std::optional patterns_json); + void SchedulePatternsRequest(); + void RequestPatterns(); + void OnPatternsResponse(std::optional response_body); + void OnPatternsGunzip(std::optional patterns_json); + void OnPatternsWritten(std::unique_ptr parsed_group, + bool result); + void HandlePatternsStatus(bool result); + + raw_ptr local_state_; + + scoped_refptr pool_sequenced_task_runner_; + + GURL collector_config_url_; + GURL quorum_config_url_; + GURL patterns_url_; + base::FilePath patterns_path_; + raw_ptr shared_url_loader_factory_; + + base::RepeatingClosure config_callback_; + base::RepeatingClosure patterns_callback_; + + std::unique_ptr collector_config_url_loader_; + std::unique_ptr quorum_config_url_loader_; + std::unique_ptr patterns_url_loader_; + net::BackoffEntry config_backoff_entry_; + net::BackoffEntry patterns_backoff_entry_; + + base::WallClockTimer config_update_timer_; + base::WallClockTimer patterns_update_timer_; + bool patterns_first_request_made_ = false; + + std::unique_ptr last_loaded_server_config_; + std::unique_ptr last_loaded_patterns_; + + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_SERVER_CONFIG_LOADER_H_ diff --git a/components/web_discovery/browser/server_config_loader_unittest.cc b/components/web_discovery/browser/server_config_loader_unittest.cc new file mode 100644 index 000000000000..cef07c5d7c90 --- /dev/null +++ b/components/web_discovery/browser/server_config_loader_unittest.cc @@ -0,0 +1,281 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/server_config_loader.h" + +#include + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/path_service.h" +#include "base/test/task_environment.h" +#include "brave/components/constants/brave_paths.h" +#include "brave/components/web_discovery/browser/util.h" +#include "brave/components/web_discovery/browser/wdp_service.h" +#include "components/prefs/testing_pref_service.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace web_discovery { + +class WebDiscoveryServerConfigLoaderTest : public testing::Test { + public: + WebDiscoveryServerConfigLoaderTest() + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), + shared_url_loader_factory_( + base::MakeRefCounted( + &url_loader_factory_)) {} + ~WebDiscoveryServerConfigLoaderTest() override = default; + + // testing::Test: + void SetUp() override { + WDPService::RegisterLocalStatePrefs(local_state_.registry()); + + ASSERT_TRUE(install_dir_.CreateUniqueTempDir()); + + base::FilePath data_path = + base::PathService::CheckedGet(brave::DIR_TEST_DATA) + .AppendASCII("web_discovery"); + ASSERT_TRUE(base::ReadFileToString(data_path.AppendASCII("hpn-config.json"), + &hpn_config_contents_)); + ASSERT_TRUE(base::ReadFileToString( + data_path.AppendASCII("quorum-config.json"), &quorum_config_contents_)); + ASSERT_TRUE(base::ReadFileToString(data_path.AppendASCII("patterns.gz"), + &patterns_gz_contents_)); + + url_loader_factory_.SetInterceptor( + base::BindRepeating(&WebDiscoveryServerConfigLoaderTest::HandleRequest, + base::Unretained(this))); + SetupServerConfigLoader(); + } + + protected: + void SetupServerConfigLoader() { + server_config_loader_ = std::make_unique( + &local_state_, install_dir_.GetPath(), shared_url_loader_factory_.get(), + base::BindRepeating( + &WebDiscoveryServerConfigLoaderTest::HandleConfigReady, + base::Unretained(this)), + base::BindRepeating( + &WebDiscoveryServerConfigLoaderTest::HandlePatternsReady, + base::Unretained(this))); + } + + void ResetCounts() { + hpn_config_requests_made_ = 0; + quorum_config_requests_made_ = 0; + patterns_requests_made_ = 0; + config_ready_calls_made_ = 0; + patterns_ready_calls_made_ = 0; + } + + bool PatternsFileExists() { + return base::PathExists( + install_dir_.GetPath().AppendASCII("wdp_patterns.json")); + } + + base::test::TaskEnvironment task_environment_; + std::unique_ptr server_config_loader_; + base::ScopedTempDir install_dir_; + + size_t hpn_config_requests_made_ = 0; + size_t quorum_config_requests_made_ = 0; + size_t patterns_requests_made_ = 0; + + size_t config_ready_calls_made_ = 0; + size_t patterns_ready_calls_made_ = 0; + + net::HttpStatusCode hpn_config_status_code_ = net::HTTP_OK; + net::HttpStatusCode quorum_config_status_code_ = net::HTTP_OK; + net::HttpStatusCode patterns_status_code_ = net::HTTP_OK; + + private: + void HandleRequest(const network::ResourceRequest& request) { + url_loader_factory_.ClearResponses(); + + ASSERT_EQ(request.method, net::HttpRequestHeaders::kGetMethod); + if (request.url.spec().starts_with(GetAnonymousHPNHost() + "/config")) { + hpn_config_requests_made_++; + url_loader_factory_.AddResponse(request.url.spec(), hpn_config_contents_, + hpn_config_status_code_); + } else if (request.url.spec() == GetQuorumHost() + "/config") { + quorum_config_requests_made_++; + url_loader_factory_.AddResponse(request.url.spec(), + quorum_config_contents_, + quorum_config_status_code_); + } else if (request.url.spec() == GetPatternsEndpoint()) { + patterns_requests_made_++; + url_loader_factory_.AddResponse(request.url.spec(), patterns_gz_contents_, + patterns_status_code_); + } else { + FAIL(); + } + } + + void HandleConfigReady() { config_ready_calls_made_++; } + + void HandlePatternsReady() { patterns_ready_calls_made_++; } + + std::string patterns_gz_contents_; + std::string quorum_config_contents_; + std::string hpn_config_contents_; + + TestingPrefServiceSimple local_state_; + network::TestURLLoaderFactory url_loader_factory_; + scoped_refptr shared_url_loader_factory_; +}; + +TEST_F(WebDiscoveryServerConfigLoaderTest, LoadConfigs) { + EXPECT_EQ(hpn_config_requests_made_, 0u); + EXPECT_EQ(quorum_config_requests_made_, 0u); + EXPECT_EQ(patterns_requests_made_, 0u); + EXPECT_EQ(config_ready_calls_made_, 0u); + EXPECT_EQ(patterns_ready_calls_made_, 0u); + + task_environment_.FastForwardBy(base::Seconds(40)); + + server_config_loader_->LoadConfigs(); + task_environment_.RunUntilIdle(); + + EXPECT_EQ(hpn_config_requests_made_, 1u); + EXPECT_EQ(quorum_config_requests_made_, 1u); + EXPECT_EQ(patterns_requests_made_, 0u); + EXPECT_EQ(config_ready_calls_made_, 1u); + EXPECT_EQ(patterns_ready_calls_made_, 0u); + + EXPECT_FALSE(PatternsFileExists()); + + const auto& server_config = server_config_loader_->GetLastServerConfig(); + EXPECT_EQ(server_config.location, "ca"); + EXPECT_EQ(server_config.group_pub_keys.size(), 4u); + EXPECT_EQ(server_config.pub_keys.size(), 4u); + EXPECT_EQ(server_config.source_map_actions.size(), 27u); + + task_environment_.FastForwardBy(base::Seconds(40)); + EXPECT_EQ(hpn_config_requests_made_, 1u); + EXPECT_EQ(quorum_config_requests_made_, 1u); + EXPECT_EQ(patterns_requests_made_, 1u); + EXPECT_EQ(config_ready_calls_made_, 1u); + EXPECT_EQ(patterns_ready_calls_made_, 1u); + + EXPECT_TRUE(PatternsFileExists()); + + const auto& patterns = server_config_loader_->GetLastPatterns(); + EXPECT_EQ(patterns.normal_patterns.size(), 9u); + EXPECT_EQ(patterns.strict_patterns.size(), 8u); +} + +TEST_F(WebDiscoveryServerConfigLoaderTest, ReloadConfigs) { + server_config_loader_->LoadConfigs(); + task_environment_.FastForwardBy(base::Seconds(40)); + + for (size_t i = 0; i < 3; i++) { + ResetCounts(); + task_environment_.FastForwardBy(base::Hours(4)); + + EXPECT_GE(hpn_config_requests_made_, 1u); + EXPECT_GE(quorum_config_requests_made_, 1u); + EXPECT_GE(patterns_requests_made_, 1u); + EXPECT_GE(config_ready_calls_made_, 1u); + EXPECT_GE(patterns_ready_calls_made_, 1u); + EXPECT_TRUE(PatternsFileExists()); + } +} + +TEST_F(WebDiscoveryServerConfigLoaderTest, LoadPatternsFromStorage) { + server_config_loader_->LoadConfigs(); + task_environment_.FastForwardBy(base::Seconds(40)); + EXPECT_TRUE(PatternsFileExists()); + + ResetCounts(); + SetupServerConfigLoader(); + server_config_loader_->LoadConfigs(); + task_environment_.FastForwardBy(base::Seconds(40)); + + EXPECT_EQ(hpn_config_requests_made_, 1u); + EXPECT_EQ(quorum_config_requests_made_, 1u); + EXPECT_EQ(patterns_requests_made_, 0u); + EXPECT_EQ(config_ready_calls_made_, 1u); + EXPECT_EQ(patterns_ready_calls_made_, 1u); + EXPECT_TRUE(PatternsFileExists()); + + task_environment_.AdvanceClock(base::Hours(3)); + ResetCounts(); + SetupServerConfigLoader(); + server_config_loader_->LoadConfigs(); + task_environment_.FastForwardBy(base::Seconds(40)); + + EXPECT_EQ(hpn_config_requests_made_, 1u); + EXPECT_EQ(quorum_config_requests_made_, 1u); + EXPECT_EQ(patterns_requests_made_, 1u); + EXPECT_EQ(config_ready_calls_made_, 1u); + EXPECT_EQ(patterns_ready_calls_made_, 1u); + EXPECT_TRUE(PatternsFileExists()); +} + +TEST_F(WebDiscoveryServerConfigLoaderTest, ConfigRetry) { + hpn_config_status_code_ = net::HTTP_INTERNAL_SERVER_ERROR; + server_config_loader_->LoadConfigs(); + task_environment_.FastForwardBy(base::Seconds(40)); + + EXPECT_GE(hpn_config_requests_made_, 1u); + EXPECT_GE(quorum_config_requests_made_, 1u); + EXPECT_EQ(patterns_requests_made_, 0u); + EXPECT_EQ(config_ready_calls_made_, 0u); + EXPECT_EQ(patterns_ready_calls_made_, 0u); + + ResetCounts(); + hpn_config_status_code_ = net::HTTP_OK; + quorum_config_status_code_ = net::HTTP_INTERNAL_SERVER_ERROR; + task_environment_.FastForwardBy(base::Seconds(40)); + + EXPECT_GE(hpn_config_requests_made_, 1u); + EXPECT_GE(quorum_config_requests_made_, 1u); + EXPECT_EQ(patterns_requests_made_, 0u); + EXPECT_EQ(config_ready_calls_made_, 0u); + EXPECT_EQ(patterns_ready_calls_made_, 0u); + EXPECT_FALSE(PatternsFileExists()); + + ResetCounts(); + quorum_config_status_code_ = net::HTTP_OK; + task_environment_.FastForwardBy(base::Seconds(90)); + + EXPECT_EQ(hpn_config_requests_made_, 1u); + EXPECT_EQ(quorum_config_requests_made_, 1u); + EXPECT_EQ(config_ready_calls_made_, 1u); + + task_environment_.FastForwardBy(base::Seconds(40)); + EXPECT_EQ(patterns_requests_made_, 1u); + EXPECT_EQ(patterns_ready_calls_made_, 1u); + EXPECT_TRUE(PatternsFileExists()); +} + +TEST_F(WebDiscoveryServerConfigLoaderTest, PatternsRetry) { + patterns_status_code_ = net::HTTP_INTERNAL_SERVER_ERROR; + server_config_loader_->LoadConfigs(); + task_environment_.FastForwardBy(base::Seconds(40)); + + EXPECT_EQ(hpn_config_requests_made_, 1u); + EXPECT_EQ(quorum_config_requests_made_, 1u); + EXPECT_GE(patterns_requests_made_, 1u); + EXPECT_EQ(config_ready_calls_made_, 1u); + EXPECT_EQ(patterns_ready_calls_made_, 0u); + EXPECT_FALSE(PatternsFileExists()); + + ResetCounts(); + patterns_status_code_ = net::HTTP_OK; + task_environment_.FastForwardBy(base::Seconds(40)); + + EXPECT_EQ(hpn_config_requests_made_, 0u); + EXPECT_EQ(quorum_config_requests_made_, 0u); + EXPECT_EQ(patterns_requests_made_, 1u); + EXPECT_EQ(config_ready_calls_made_, 0u); + EXPECT_EQ(patterns_ready_calls_made_, 1u); + EXPECT_TRUE(PatternsFileExists()); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/signature_basename.cc b/components/web_discovery/browser/signature_basename.cc new file mode 100644 index 000000000000..06cc4127ebe7 --- /dev/null +++ b/components/web_discovery/browser/signature_basename.cc @@ -0,0 +1,239 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/signature_basename.h" + +#include +#include + +#include "base/hash/hash.h" +#include "base/json/json_writer.h" +#include "base/rand_util.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "brave/components/web_discovery/browser/payload_generator.h" +#include "brave/components/web_discovery/browser/pref_names.h" +#include "brave/components/web_discovery/browser/server_config_loader.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "crypto/sha2.h" + +namespace web_discovery { + +namespace { + +constexpr char kUrlNormalizationFunc[] = "url"; +constexpr char kFlattenObjNormalizationFunc[] = "obj"; +constexpr size_t kMsInHour = 60 * 60 * 1000; + +constexpr char kExpiresAtKey[] = "expires_at"; +constexpr char kUsedCountsKey[] = "counts"; + +void RecurseFlattenObject(const base::Value& value, + const base::Value::List& parent_keys, + base::Value::List& output) { + if (value.is_dict()) { + const auto& dict = value.GetDict(); + base::flat_set keys; + // insert into set so we can sort keys + for (const auto [key, _] : dict) { + keys.insert(key); + } + for (const auto& key : keys) { + base::Value::List next_parent_keys = parent_keys.Clone(); + next_parent_keys.Append(key); + RecurseFlattenObject(*dict.Find(key), next_parent_keys, output); + } + } else if (value.is_list()) { + const auto& list = value.GetList(); + for (size_t i = 0; i < list.size(); i++) { + base::Value::List next_parent_keys = parent_keys.Clone(); + next_parent_keys.Append(base::NumberToString(i)); + RecurseFlattenObject(list[i], next_parent_keys, output); + } + } else { + base::Value::List flattened_value; + flattened_value.Append(parent_keys.Clone()); + flattened_value.Append(value.Clone()); + output.Append(std::move(flattened_value)); + } +} + +base::Value FlattenObject(const base::Value& obj) { + base::Value::List result; + RecurseFlattenObject(obj, base::Value::List(), result); + return base::Value(std::move(result)); +} + +base::Value CleanURL(RegexUtil& regex_util, const base::Value& url) { + if (!url.is_string()) { + return base::Value(); + } + auto url_str = base::ToLowerASCII(url.GetString()); + base::RemoveChars(url_str, " ", &url_str); + base::ReplaceSubstringsAfterOffset(&url_str, 0, "https://", ""); + base::ReplaceSubstringsAfterOffset(&url_str, 0, "http://", ""); + base::ReplaceSubstringsAfterOffset(&url_str, 0, "www.", ""); + + regex_util.RemovePunctuation(url_str); + return base::Value(std::move(url_str)); +} + +int GetPeriodHoursSinceEpoch(size_t period_hours) { + auto hours_since_epoch = + base::Time::Now().InMillisecondsSinceUnixEpoch() / kMsInHour; + auto epoch_period_hours = period_hours * (hours_since_epoch / period_hours); + return epoch_period_hours; +} + +std::optional GetBasenameCount(PrefService* profile_prefs, + uint32_t count_tag_hash, + const SourceMapActionConfig& action_config, + size_t period_hours) { + // clean up expired counts + ScopedDictPrefUpdate update(profile_prefs, kUsedBasenameCounts); + base::Time now = base::Time::Now(); + for (auto it = update->begin(); it != update->end();) { + const auto* value_dict = it->second.GetIfDict(); + if (!value_dict) { + it = update->erase(it); + continue; + } + const auto expire_time = value_dict->FindDouble(kExpiresAtKey); + if (!expire_time || + now >= base::Time::FromTimeT(static_cast(*expire_time))) { + it = update->erase(it); + continue; + } + it++; + } + + auto count_tag_hash_str = base::NumberToString(count_tag_hash); + auto* count_dict = update->EnsureDict(count_tag_hash_str); + if (!count_dict->contains(kExpiresAtKey)) { + auto expire_time = + base::Time::FromMillisecondsSinceUnixEpoch(static_cast( + (period_hours + action_config.period) * kMsInHour)); + count_dict->Set(kExpiresAtKey, static_cast(expire_time.ToTimeT())); + } + + auto* used_counts_list = count_dict->EnsureList(kUsedCountsKey); + if (used_counts_list->size() >= action_config.limit) { + VLOG(1) << "No basename counts left"; + return std::nullopt; + } + + while (true) { + auto count = base::RandInt(0, action_config.limit - 1); + if (base::ranges::find(used_counts_list->begin(), used_counts_list->end(), + count) != used_counts_list->end()) { + continue; + } + return count; + } +} + +} // namespace + +BasenameResult::BasenameResult(std::vector basename, + size_t count, + uint32_t count_tag_hash) + : basename(basename), count(count), count_tag_hash(count_tag_hash) {} + +BasenameResult::~BasenameResult() = default; + +std::optional GenerateBasename( + PrefService* profile_prefs, + const ServerConfig& server_config, + RegexUtil& regex_util, + const base::Value::Dict& payload) { + const std::string* action = payload.FindString(kActionKey); + std::string json; + base::JSONWriter::Write(payload, &json); + if (!action || action->empty()) { + VLOG(1) << "No action"; + return std::nullopt; + } + const auto action_config = server_config.source_map_actions.find(*action); + if (action_config == server_config.source_map_actions.end()) { + VLOG(1) << "No action config for " << action; + return std::nullopt; + } + const auto* inner_payload = payload.FindDict(kInnerPayloadKey); + if (!inner_payload) { + VLOG(1) << "No inner payload"; + return std::nullopt; + } + base::Value::List tag_list; + tag_list.Append(*action); + tag_list.Append(static_cast(action_config->second->period)); + tag_list.Append(static_cast(action_config->second->limit)); + + base::Value::List key_values; + for (const auto& key : action_config->second->keys) { + auto parts = base::SplitStringUsingSubstr( + key, "->", base::WhitespaceHandling::TRIM_WHITESPACE, + base::SPLIT_WANT_ALL); + if (parts.empty()) { + continue; + } + base::Value value; + if (parts[0].empty()) { + value = base::Value(inner_payload->Clone()); + } else if (const auto* found_value = + inner_payload->FindByDottedPath(parts[0])) { + value = found_value->Clone(); + } + if (parts.size() > 1) { + if (parts[1] == kUrlNormalizationFunc) { + value = CleanURL(regex_util, value); + } else if (parts[1] == kFlattenObjNormalizationFunc) { + value = FlattenObject(value); + } + } + key_values.Append(std::move(value)); + } + + auto period_hours = GetPeriodHoursSinceEpoch(action_config->second->period); + tag_list.Append(std::move(key_values)); + tag_list.Append(period_hours); + + std::string interim_tag_json; + if (!base::JSONWriter::Write(base::Value(tag_list.Clone()), + &interim_tag_json)) { + return std::nullopt; + } + auto count_tag_hash = base::PersistentHash(interim_tag_json); + auto basename_count = GetBasenameCount(profile_prefs, count_tag_hash, + *action_config->second, period_hours); + if (!basename_count) { + VLOG(1) << "No basename count available"; + return std::nullopt; + } + tag_list.Append(*basename_count); + + std::string tag_json; + if (!base::JSONWriter::Write(base::Value(std::move(tag_list)), &tag_json)) { + return std::nullopt; + } + + auto tag_hash = crypto::SHA256HashString(tag_json); + std::vector tag_hash_vector(tag_hash.begin(), tag_hash.end()); + return std::make_optional(tag_hash_vector, *basename_count, + count_tag_hash); +} + +void SaveBasenameCount(PrefService* profile_prefs, + uint32_t count_tag_hash, + size_t count) { + ScopedDictPrefUpdate update(profile_prefs, kUsedBasenameCounts); + + auto count_tag_hash_str = base::NumberToString(count_tag_hash); + auto* count_dict = update->EnsureDict(count_tag_hash_str); + + auto* used_counts_list = count_dict->EnsureList(kUsedCountsKey); + used_counts_list->Append(static_cast(count)); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/signature_basename.h b/components/web_discovery/browser/signature_basename.h new file mode 100644 index 000000000000..1a07cf027e25 --- /dev/null +++ b/components/web_discovery/browser/signature_basename.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_SIGNATURE_BASENAME_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_SIGNATURE_BASENAME_H_ + +#include +#include + +#include "base/values.h" +#include "brave/components/web_discovery/browser/regex_util.h" +#include "brave/components/web_discovery/browser/server_config_loader.h" + +class PrefService; + +namespace web_discovery { + +struct BasenameResult { + BasenameResult(std::vector basename, + size_t count, + uint32_t count_tag_hash); + ~BasenameResult(); + + BasenameResult(const BasenameResult&) = delete; + BasenameResult& operator=(const BasenameResult&) = delete; + + std::vector basename; + // The count index for a given "pre-tag". It should be under the limit for a + // given action + size_t count; + uint32_t count_tag_hash; +}; + +// Generates a basename used for the signature. The basename is a sha hash +// of the message "action" (i.e. "query"), the settings for that action +// (defined in the server's "source map"), cherry-picked attributes from the +// payload and the count index for the given message. The count will be under +// the limit defined for the action; the function will return nullopt if the +// limit for the action is exceeded. +std::optional GenerateBasename( + PrefService* profile_prefs, + const ServerConfig& server_config, + RegexUtil& regex_util, + const base::Value::Dict& payload); + +// Saves the count returned from `GenerateBasename` in the prefs. +// This ensures that the count index cannot be used for future messages +// within the defined action limit period (default is 24 hours). +// This should be called after a submission is successfully sent to +// the server. +void SaveBasenameCount(PrefService* profile_prefs, + uint32_t count_tag_hash, + size_t count); + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_SIGNATURE_BASENAME_H_ diff --git a/components/web_discovery/browser/signature_basename_unittest.cc b/components/web_discovery/browser/signature_basename_unittest.cc new file mode 100644 index 000000000000..3e0f0f9a7c40 --- /dev/null +++ b/components/web_discovery/browser/signature_basename_unittest.cc @@ -0,0 +1,249 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/signature_basename.h" + +#include +#include +#include + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/test/task_environment.h" +#include "brave/components/web_discovery/browser/regex_util.h" +#include "brave/components/web_discovery/browser/server_config_loader.h" +#include "brave/components/web_discovery/browser/wdp_service.h" +#include "components/prefs/testing_pref_service.h" +#include "crypto/sha2.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace web_discovery { + +namespace { + +constexpr size_t kMsInHour = 60 * 60 * 1000; + +int GetPeriodHoursSinceEpoch(size_t period_hours) { + auto hours_since_epoch = + base::Time::Now().InMillisecondsSinceUnixEpoch() / kMsInHour; + auto epoch_period_hours = period_hours * (hours_since_epoch / period_hours); + return epoch_period_hours; +} + +base::TimeDelta TimeUntilNextPeriod(int period, int epoch_period_hours) { + return base::Time::FromMillisecondsSinceUnixEpoch( + (epoch_period_hours + period) * static_cast(kMsInHour)) - + base::Time::Now(); +} + +std::vector GenerateExpectedBasename(std::string action, + int period, + int limit, + base::Value::List key_list, + size_t actual_count, + int epoch_period_hours) { + base::Value::List expected_tag_list; + expected_tag_list.Append(action); + expected_tag_list.Append(period); + expected_tag_list.Append(limit); + expected_tag_list.Append(std::move(key_list)); + expected_tag_list.Append(static_cast(epoch_period_hours)); + expected_tag_list.Append(static_cast(actual_count)); + + std::string tag_json; + EXPECT_TRUE(base::JSONWriter::Write(base::Value(std::move(expected_tag_list)), + &tag_json)); + + auto tag_hash = crypto::SHA256HashString(tag_json); + return std::vector(tag_hash.begin(), tag_hash.end()); +} + +base::Value::Dict GeneratePayload(std::string action, + base::Value::Dict inner_payload) { + base::Value::Dict payload; + payload.Set("action", action); + payload.Set("payload", std::move(inner_payload)); + return payload; +} + +} // namespace + +class WebDiscoverySignatureBasenameTest : public testing::Test { + public: + WebDiscoverySignatureBasenameTest() + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} + ~WebDiscoverySignatureBasenameTest() override = default; + + // testing::Test: + void SetUp() override { + WDPService::RegisterProfilePrefs(profile_prefs_.registry()); + + auto action_config = std::make_unique(); + action_config->keys.push_back("q->url"); + action_config->period = 24; + action_config->limit = 3; + server_config_.source_map_actions["query"] = std::move(action_config); + + action_config = std::make_unique(); + action_config->keys.push_back("field->obj"); + action_config->period = 12; + action_config->limit = 1; + server_config_.source_map_actions["img"] = std::move(action_config); + + action_config = std::make_unique(); + action_config->keys.push_back("field"); + action_config->period = 12; + action_config->limit = 1; + server_config_.source_map_actions["basic"] = std::move(action_config); + } + + protected: + base::test::TaskEnvironment task_environment_; + ServerConfig server_config_; + TestingPrefServiceSimple profile_prefs_; + RegexUtil regex_util_; +}; + +TEST_F(WebDiscoverySignatureBasenameTest, BasenameForURL) { + base::flat_set used_counts; + + base::Value::List key_list; + key_list.Append("examplecomtesttestpage"); + + base::Value::Dict inner_payload; + inner_payload.Set("q", "https://www.EXample.com/test test/page"); + auto payload = GeneratePayload("query", std::move(inner_payload)); + + auto epoch_period_hours = GetPeriodHoursSinceEpoch(24); + for (size_t i = 0; i < 3; i++) { + auto actual_basename = + GenerateBasename(&profile_prefs_, server_config_, regex_util_, payload); + ASSERT_TRUE(actual_basename); + EXPECT_LT(actual_basename->count, 3u); + EXPECT_FALSE(used_counts.contains(actual_basename->count)); + + auto expected_basename = + GenerateExpectedBasename("query", 24, 3, key_list.Clone(), + actual_basename->count, epoch_period_hours); + + EXPECT_EQ(actual_basename->basename, expected_basename); + used_counts.insert(actual_basename->count); + + SaveBasenameCount(&profile_prefs_, actual_basename->count_tag_hash, + actual_basename->count); + } + + EXPECT_FALSE( + GenerateBasename(&profile_prefs_, server_config_, regex_util_, payload)); +} + +TEST_F(WebDiscoverySignatureBasenameTest, BasenameNotSaved) { + base::Value::Dict inner_payload; + inner_payload.Set("q", "https://www.example.com/test/page"); + auto payload = GeneratePayload("query", std::move(inner_payload)); + + for (size_t i = 0; i < 10; i++) { + EXPECT_TRUE(GenerateBasename(&profile_prefs_, server_config_, regex_util_, + payload)); + } +} + +TEST_F(WebDiscoverySignatureBasenameTest, BasenameLimitExpiry) { + base::Value::Dict inner_payload; + inner_payload.Set("q", "https://www.example.com/test/page"); + auto payload = GeneratePayload("query", std::move(inner_payload)); + + for (size_t i = 0; i < 3; i++) { + auto epoch_period_hours = GetPeriodHoursSinceEpoch(24); + for (size_t j = 0; j < 3; j++) { + auto basename = GenerateBasename(&profile_prefs_, server_config_, + regex_util_, payload); + ASSERT_TRUE(basename); + SaveBasenameCount(&profile_prefs_, basename->count_tag_hash, + basename->count); + } + + auto time_until_next_period = TimeUntilNextPeriod(24, epoch_period_hours); + task_environment_.AdvanceClock(time_until_next_period / 2); + EXPECT_FALSE(GenerateBasename(&profile_prefs_, server_config_, regex_util_, + payload)); + task_environment_.AdvanceClock(time_until_next_period / 2); + } +} + +TEST_F(WebDiscoverySignatureBasenameTest, BasenameForFlattenedObj) { + auto field_obj = base::JSONReader::Read(R"({ + "this": { + "is": { + "test": "object" + } + }, + "example1": [ 1, 2 ], + "example2": { "abc": "def" } + })"); + ASSERT_TRUE(field_obj); + auto expected_flattened_obj = base::JSONReader::Read(R"([ + [ + [["example1", "0"], 1], + [["example1", "1"], 2], + [["example2", "abc"], "def"], + [["this", "is", "test"], "object"] + ] + ])"); + ASSERT_TRUE(expected_flattened_obj); + + base::Value::Dict inner_payload; + inner_payload.Set("field", std::move(*field_obj)); + auto payload = GeneratePayload("img", std::move(inner_payload)); + + auto actual_basename = + GenerateBasename(&profile_prefs_, server_config_, regex_util_, payload); + ASSERT_TRUE(actual_basename); + EXPECT_EQ(actual_basename->count, 0u); + + auto epoch_period_hours = GetPeriodHoursSinceEpoch(24); + auto expected_basename = GenerateExpectedBasename( + "img", 12, 1, expected_flattened_obj->GetList().Clone(), 0u, + epoch_period_hours); + + EXPECT_EQ(actual_basename->basename, expected_basename); + + SaveBasenameCount(&profile_prefs_, actual_basename->count_tag_hash, + actual_basename->count); + + EXPECT_FALSE( + GenerateBasename(&profile_prefs_, server_config_, regex_util_, payload)); +} + +TEST_F(WebDiscoverySignatureBasenameTest, BasenameSimple) { + base::Value::List key_list; + key_list.Append("test"); + + base::Value::Dict inner_payload; + inner_payload.Set("field", "test"); + auto payload = GeneratePayload("basic", std::move(inner_payload)); + + auto actual_basename = + GenerateBasename(&profile_prefs_, server_config_, regex_util_, payload); + ASSERT_TRUE(actual_basename); + EXPECT_EQ(actual_basename->count, 0u); + + auto epoch_period_hours = GetPeriodHoursSinceEpoch(24); + auto expected_basename = GenerateExpectedBasename( + "basic", 12, 1, std::move(key_list), 0u, epoch_period_hours); + + EXPECT_EQ(actual_basename->basename, expected_basename); +} + +TEST_F(WebDiscoverySignatureBasenameTest, BasenameNoAction) { + base::Value::Dict inner_payload; + inner_payload.Set("field", "test"); + auto payload = GeneratePayload("bad_action", std::move(inner_payload)); + + ASSERT_FALSE( + GenerateBasename(&profile_prefs_, server_config_, regex_util_, payload)); +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/util.cc b/components/web_discovery/browser/util.cc new file mode 100644 index 000000000000..acb38bdf63e2 --- /dev/null +++ b/components/web_discovery/browser/util.cc @@ -0,0 +1,85 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/util.h" + +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "brave/brave_domains/service_domains.h" +#include "url/url_util.h" + +namespace web_discovery { + +namespace { +constexpr char kCollectorHostPrefix[] = "collector.wdp"; +constexpr char kQuorumHostPrefix[] = "quorum.wdp"; +constexpr char kPatternsHostPrefix[] = "patterns.wdp"; +constexpr char kPatternsPath[] = "/patterns.gz"; +} // namespace + +std::string GetDirectHPNHost() { + // TODO(djandries): Replace with non-proxied endpoint once available + return GetAnonymousHPNHost(); +} + +std::string GetAnonymousHPNHost() { + auto* cmd_line = base::CommandLine::ForCurrentProcess(); + if (cmd_line->HasSwitch(kCollectorHostSwitch)) { + return cmd_line->GetSwitchValueASCII(kCollectorHostSwitch); + } + return base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator, + brave_domains::GetServicesDomain(kCollectorHostPrefix)}); +} + +std::string GetQuorumHost() { + return base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator, + brave_domains::GetServicesDomain(kQuorumHostPrefix)}); +} + +GURL GetPatternsEndpoint() { + return GURL(base::StrCat( + {url::kHttpsScheme, url::kStandardSchemeSeparator, + brave_domains::GetServicesDomain(kPatternsHostPrefix), kPatternsPath})); +} + +std::unique_ptr CreateResourceRequest(GURL url) { + auto resource_request = std::make_unique(); + resource_request->url = url; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; + return resource_request; +} + +std::string FormatServerDate(const base::Time& date) { + base::Time::Exploded exploded; + date.UTCExplode(&exploded); + return base::StringPrintf("%04d%02d%02d", exploded.year, exploded.month, + exploded.day_of_month); +} + +std::string DecodeURLComponent(const std::string_view value) { + url::RawCanonOutputT result; + url::DecodeURLEscapeSequences(value, url::DecodeURLMode::kUTF8OrIsomorphic, + &result); + return base::UTF16ToUTF8(result.view()); +} + +std::optional ExtractValueFromQueryString( + const std::string_view query_string, + const std::string_view key) { + url::Component query_slice(0, query_string.length()); + url::Component key_slice; + url::Component value_slice; + while (url::ExtractQueryKeyValue(query_string, &query_slice, &key_slice, + &value_slice)) { + if (query_string.substr(key_slice.begin, key_slice.len) != key) { + continue; + } + return DecodeURLComponent( + query_string.substr(value_slice.begin, value_slice.len)); + } + return std::nullopt; +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/util.h b/components/web_discovery/browser/util.h new file mode 100644 index 000000000000..9792b4622d55 --- /dev/null +++ b/components/web_discovery/browser/util.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_UTIL_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_UTIL_H_ + +#include +#include +#include + +#include "base/time/time.h" +#include "net/base/backoff_entry.h" +#include "services/network/public/cpp/resource_request.h" +#include "url/gurl.h" + +namespace web_discovery { + +inline constexpr size_t kMaxResponseSize = 16 * 1024; +inline constexpr char kCollectorHostSwitch[] = "wdp-collector-host"; +inline constexpr char kVersionHeader[] = "Version"; +inline constexpr int kCurrentVersion = 1; + +// The default backoff policy to use for scheduling retry requests. +inline constexpr net::BackoffEntry::Policy kBackoffPolicy = { + .num_errors_to_ignore = 0, + .initial_delay_ms = 10 * 1000, + .multiply_factor = 2.0, + .jitter_factor = 0.1, + .maximum_backoff_ms = 10 * 60 * 1000, + .entry_lifetime_ms = -1, + .always_use_initial_delay = false}; + +// Returns the non-proxied HPN host, used for acquiring anonymous credentials. +std::string GetDirectHPNHost(); +// Returns the proxied HPN host, used for retrieving server config and page +// content submission. +std::string GetAnonymousHPNHost(); +// Returns the "quorum" host, used for location config and page event +// submission. +std::string GetQuorumHost(); +// Returns the full URL for the patterns config. +GURL GetPatternsEndpoint(); + +// Creates a new ResourceRequest with the given URL and credentials omitted. +std::unique_ptr CreateResourceRequest(GURL url); + +// Formats a given date as a string in the format "YYYYMMDD", in the UTC +// timezone. +std::string FormatServerDate(const base::Time& date); + +// Decodes URL-encoded components, converting escape sequences to their +// corresponding characters. +std::string DecodeURLComponent(const std::string_view value); + +// Extracts the value associated with a given key from a URL query string. +std::optional ExtractValueFromQueryString( + const std::string_view query_string, + const std::string_view key); + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_UTIL_H_ diff --git a/components/web_discovery/browser/wdp_service.cc b/components/web_discovery/browser/wdp_service.cc new file mode 100644 index 000000000000..3df1c42fe1df --- /dev/null +++ b/components/web_discovery/browser/wdp_service.cc @@ -0,0 +1,283 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/browser/wdp_service.h" + +#include + +#include "base/feature_list.h" +#include "base/functional/bind.h" +#include "base/strings/stringprintf.h" +#include "brave/components/constants/pref_names.h" +#include "brave/components/web_discovery/browser/content_scraper.h" +#include "brave/components/web_discovery/browser/payload_generator.h" +#include "brave/components/web_discovery/browser/pref_names.h" +#include "brave/components/web_discovery/browser/privacy_guard.h" +#include "brave/components/web_discovery/browser/server_config_loader.h" +#include "brave/components/web_discovery/common/features.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/render_frame_host.h" +#include "extensions/buildflags/buildflags.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/service_manager/public/cpp/interface_provider.h" + +namespace web_discovery { + +namespace { +constexpr base::TimeDelta kAliveCheckInterval = base::Minutes(1); +constexpr size_t kMinPageCountForAliveMessage = 2; +} // namespace + +WDPService::WDPService( + PrefService* local_state, + PrefService* profile_prefs, + base::FilePath user_data_dir, + scoped_refptr shared_url_loader_factory) + : local_state_(local_state), + profile_prefs_(profile_prefs), + user_data_dir_(user_data_dir), + shared_url_loader_factory_(shared_url_loader_factory) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (profile_prefs_->GetBoolean(kWebDiscoveryExtensionEnabled)) { + profile_prefs_->ClearPref(kWebDiscoveryExtensionEnabled); + profile_prefs_->SetBoolean(kWebDiscoveryNativeEnabled, true); + } +#endif + + pref_change_registrar_.Init(profile_prefs); + pref_change_registrar_.Add(kWebDiscoveryNativeEnabled, + base::BindRepeating(&WDPService::OnEnabledChange, + base::Unretained(this))); + + if (profile_prefs_->GetBoolean(kWebDiscoveryNativeEnabled)) { + Start(); + } +} + +WDPService::~WDPService() = default; + +void WDPService::RegisterLocalStatePrefs(PrefRegistrySimple* registry) { + registry->RegisterTimePref(kPatternsRetrievalTime, {}); +} + +void WDPService::RegisterProfilePrefs(PrefRegistrySimple* registry) { + registry->RegisterBooleanPref(kWebDiscoveryNativeEnabled, false); + registry->RegisterDictionaryPref(kAnonymousCredentialsDict); + registry->RegisterStringPref(kCredentialRSAPrivateKey, {}); + registry->RegisterStringPref(kCredentialRSAPublicKey, {}); + registry->RegisterListPref(kScheduledDoubleFetches); + registry->RegisterListPref(kScheduledReports); + registry->RegisterDictionaryPref(kUsedBasenameCounts); + registry->RegisterDictionaryPref(kPageCounts); +} + +void WDPService::SetExtensionPrefIfNativeDisabled(PrefService* profile_prefs) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (!base::FeatureList::IsEnabled(features::kWebDiscoveryNative) && + profile_prefs->GetBoolean(kWebDiscoveryNativeEnabled)) { + profile_prefs->SetBoolean(kWebDiscoveryExtensionEnabled, true); + } +#endif +} + +void WDPService::Start() { + if (!server_config_loader_) { + server_config_loader_ = std::make_unique( + local_state_, user_data_dir_, shared_url_loader_factory_.get(), + base::BindRepeating(&WDPService::OnConfigChange, + base::Unretained(this)), + base::BindRepeating(&WDPService::OnPatternsLoaded, + base::Unretained(this))); + server_config_loader_->LoadConfigs(); + } + if (!credential_manager_) { + credential_manager_ = std::make_unique( + profile_prefs_, shared_url_loader_factory_.get(), + server_config_loader_.get()); + } +} + +void WDPService::Stop() { + alive_message_timer_.Stop(); + reporter_ = nullptr; + double_fetcher_ = nullptr; + content_scraper_ = nullptr; + server_config_loader_ = nullptr; + credential_manager_ = nullptr; + + profile_prefs_->ClearPref(kWebDiscoveryNativeEnabled); + profile_prefs_->ClearPref(kAnonymousCredentialsDict); + profile_prefs_->ClearPref(kCredentialRSAPrivateKey); + profile_prefs_->ClearPref(kCredentialRSAPublicKey); + profile_prefs_->ClearPref(kScheduledDoubleFetches); + profile_prefs_->ClearPref(kScheduledReports); + profile_prefs_->ClearPref(kUsedBasenameCounts); + profile_prefs_->ClearPref(kPageCounts); +} + +void WDPService::OnEnabledChange() { + if (profile_prefs_->GetBoolean(kWebDiscoveryNativeEnabled)) { + Start(); + } else { + Stop(); + } +} + +void WDPService::OnConfigChange() { + credential_manager_->JoinGroups(); +} + +void WDPService::OnPatternsLoaded() { + if (!content_scraper_) { + content_scraper_ = std::make_unique( + server_config_loader_.get(), ®ex_util_); + } + if (!double_fetcher_) { + double_fetcher_ = std::make_unique( + profile_prefs_.get(), shared_url_loader_factory_.get(), + base::BindRepeating(&WDPService::OnDoubleFetched, + base::Unretained(this))); + } + if (!reporter_) { + reporter_ = std::make_unique( + profile_prefs_.get(), shared_url_loader_factory_.get(), + credential_manager_.get(), ®ex_util_, server_config_loader_.get()); + } + MaybeSendAliveMessage(); +} + +void WDPService::OnDoubleFetched(const GURL& url, + const base::Value& associated_data, + std::optional response_body) { + if (!response_body) { + return; + } + auto prev_scrape_result = PageScrapeResult::FromValue(associated_data); + if (!prev_scrape_result) { + return; + } + content_scraper_->ParseAndScrapePage( + url, true, std::move(prev_scrape_result), *response_body, + base::BindOnce(&WDPService::OnContentScraped, base::Unretained(this), + true)); +} + +void WDPService::DidFinishLoad(const GURL& url, + content::RenderFrameHost* render_frame_host) { + if (!content_scraper_) { + return; + } + const auto* matching_url_details = + server_config_loader_->GetLastPatterns().GetMatchingURLPattern(url, + false); + if (!matching_url_details || !matching_url_details->is_search_engine) { + if (!current_page_count_hour_key_.empty()) { + ScopedDictPrefUpdate page_count_update(profile_prefs_, kPageCounts); + auto existing_count = + page_count_update->FindInt(current_page_count_hour_key_).value_or(0); + page_count_update->Set(current_page_count_hour_key_, existing_count + 1); + } + } + if (!matching_url_details) { + return; + } + VLOG(1) << "URL matched pattern " << matching_url_details->id << ": " << url; + if (IsPrivateURLLikely(regex_util_, url, matching_url_details)) { + return; + } + mojo::Remote remote; + render_frame_host->GetRemoteInterfaces()->GetInterface( + remote.BindNewPipeAndPassReceiver()); + auto remote_id = document_extractor_remotes_.Add(std::move(remote)); + content_scraper_->ScrapePage(url, false, + document_extractor_remotes_.Get(remote_id), + base::BindOnce(&WDPService::OnContentScraped, + base::Unretained(this), false)); +} + +void WDPService::OnContentScraped(bool is_strict, + std::unique_ptr result) { + if (!result) { + return; + } + const auto& patterns = server_config_loader_->GetLastPatterns(); + auto* original_url_details = + patterns.GetMatchingURLPattern(result->url, is_strict); + if (!original_url_details) { + return; + } + if (!is_strict && original_url_details->is_search_engine) { + auto* strict_url_details = + patterns.GetMatchingURLPattern(result->url, true); + if (strict_url_details) { + auto url = result->url; + if (!result->query) { + return; + } + if (IsPrivateQueryLikely(regex_util_, *result->query)) { + return; + } + url = GeneratePrivateSearchURL(url, *result->query, *strict_url_details); + VLOG(1) << "Double fetching search page: " << url; + double_fetcher_->ScheduleDoubleFetch(url, result->SerializeToValue()); + } + } + auto payloads = GenerateQueryPayloads( + server_config_loader_->GetLastServerConfig(), regex_util_, + original_url_details, std::move(result)); + for (auto& payload : payloads) { + reporter_->ScheduleSend(std::move(payload)); + } +} + +bool WDPService::UpdatePageCountStartTime() { + auto now = base::Time::Now(); + if (!current_page_count_start_time_.is_null() && + (now - current_page_count_start_time_) < base::Hours(1)) { + return false; + } + base::Time::Exploded exploded; + now.UTCExplode(&exploded); + exploded.millisecond = 0; + exploded.second = 0; + exploded.minute = 0; + if (!base::Time::FromUTCExploded(exploded, ¤t_page_count_start_time_)) { + return false; + } + current_page_count_hour_key_ = + base::StringPrintf("%04d%02d%02d%02d", exploded.year, exploded.month, + exploded.day_of_month, exploded.hour); + return true; +} + +void WDPService::MaybeSendAliveMessage() { + if (!alive_message_timer_.IsRunning()) { + alive_message_timer_.Start( + FROM_HERE, kAliveCheckInterval, + base::BindRepeating(&WDPService::MaybeSendAliveMessage, + base::Unretained(this))); + } + if (!UpdatePageCountStartTime()) { + return; + } + ScopedDictPrefUpdate update(profile_prefs_, kPageCounts); + for (auto it = update->begin(); it != update->end();) { + if (it->first == current_page_count_hour_key_) { + it++; + continue; + } + if (it->second.is_int() && static_cast(it->second.GetInt()) >= + kMinPageCountForAliveMessage) { + reporter_->ScheduleSend(GenerateAlivePayload( + server_config_loader_->GetLastServerConfig(), it->first)); + } + it = update->erase(it); + } +} + +} // namespace web_discovery diff --git a/components/web_discovery/browser/wdp_service.h b/components/web_discovery/browser/wdp_service.h new file mode 100644 index 000000000000..6549dcf59356 --- /dev/null +++ b/components/web_discovery/browser/wdp_service.h @@ -0,0 +1,106 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_WDP_SERVICE_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_WDP_SERVICE_H_ + +#include +#include +#include + +#include "base/files/file_path.h" +#include "base/memory/raw_ptr.h" +#include "brave/components/web_discovery/browser/content_scraper.h" +#include "brave/components/web_discovery/browser/credential_manager.h" +#include "brave/components/web_discovery/browser/double_fetcher.h" +#include "brave/components/web_discovery/browser/regex_util.h" +#include "brave/components/web_discovery/browser/reporter.h" +#include "brave/components/web_discovery/browser/server_config_loader.h" +#include "components/keyed_service/core/keyed_service.h" +#include "components/prefs/pref_change_registrar.h" +#include "mojo/public/cpp/bindings/remote_set.h" + +class PrefRegistrySimple; +class PrefService; + +namespace content { +class RenderFrameHost; +} + +namespace network { +class SharedURLLoaderFactory; +} // namespace network + +namespace web_discovery { + +// The main service for the native re-implementation of Web Discovery Project. +// Handles scraping and reporting of relevant pages for opted-in users. +class WDPService : public KeyedService { + public: + WDPService( + PrefService* local_state, + PrefService* profile_prefs, + base::FilePath user_data_dir, + scoped_refptr shared_url_loader_factory); + ~WDPService() override; + + WDPService(const WDPService&) = delete; + WDPService& operator=(const WDPService&) = delete; + + static void RegisterLocalStatePrefs(PrefRegistrySimple* registry); + static void RegisterProfilePrefs(PrefRegistrySimple* registry); + + // Sets the extension preference to true if the preference for the native + // implementation is set to true and the feature is disabled. + // Relevant for a Griffin/variations rollback. + static void SetExtensionPrefIfNativeDisabled(PrefService* profile_prefs); + + // Called by `WebDiscoveryTabHelper` to notify on a page load. + void DidFinishLoad(const GURL& url, + content::RenderFrameHost* render_frame_host); + + private: + void Start(); + void Stop(); + + void OnEnabledChange(); + + void OnConfigChange(); + void OnPatternsLoaded(); + void OnContentScraped(bool is_strict, + std::unique_ptr result); + void OnDoubleFetched(const GURL& url, + const base::Value& associated_data, + std::optional response_body); + + bool UpdatePageCountStartTime(); + void MaybeSendAliveMessage(); + + raw_ptr local_state_; + raw_ptr profile_prefs_; + PrefChangeRegistrar pref_change_registrar_; + + base::FilePath user_data_dir_; + + RegexUtil regex_util_; + + scoped_refptr shared_url_loader_factory_; + + mojo::RemoteSet document_extractor_remotes_; + + std::unique_ptr server_config_loader_; + std::unique_ptr credential_manager_; + std::unique_ptr content_scraper_; + std::unique_ptr double_fetcher_; + std::unique_ptr reporter_; + + base::Time current_page_count_start_time_; + std::string current_page_count_hour_key_; + base::RepeatingTimer alive_message_timer_; +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_WDP_SERVICE_H_ diff --git a/components/web_discovery/browser/web_discovery_tab_helper.cc b/components/web_discovery/browser/web_discovery_tab_helper.cc new file mode 100644 index 000000000000..023e260fe501 --- /dev/null +++ b/components/web_discovery/browser/web_discovery_tab_helper.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "brave/components/web_discovery/browser/web_discovery_tab_helper.h" + +#include "brave/components/web_discovery/browser/wdp_service.h" +#include "content/public/browser/navigation_handle.h" + +namespace web_discovery { + +WebDiscoveryTabHelper::WebDiscoveryTabHelper(content::WebContents* web_contents, + WDPService* wdp_service) + : content::WebContentsObserver(web_contents), + content::WebContentsUserData(*web_contents), + wdp_service_(wdp_service) { + CHECK(wdp_service); +} + +WebDiscoveryTabHelper::~WebDiscoveryTabHelper() = default; + +void WebDiscoveryTabHelper::DidFinishLoad( + content::RenderFrameHost* render_frame_host, + const GURL& url) { + if (!render_frame_host->IsInPrimaryMainFrame()) { + return; + } + wdp_service_->DidFinishLoad(url, render_frame_host); +} + +WEB_CONTENTS_USER_DATA_KEY_IMPL(WebDiscoveryTabHelper); + +} // namespace web_discovery diff --git a/components/web_discovery/browser/web_discovery_tab_helper.h b/components/web_discovery/browser/web_discovery_tab_helper.h new file mode 100644 index 000000000000..1d3ee38d84b6 --- /dev/null +++ b/components/web_discovery/browser/web_discovery_tab_helper.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_WEB_DISCOVERY_TAB_HELPER_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_WEB_DISCOVERY_TAB_HELPER_H_ + +#include "base/memory/raw_ptr.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" + +namespace content { +class RenderFrameHost; +} // namespace content + +namespace web_discovery { + +class WDPService; + +class WebDiscoveryTabHelper + : public content::WebContentsObserver, + public content::WebContentsUserData { + public: + WebDiscoveryTabHelper(content::WebContents* web_contents, + WDPService* wdp_service); + ~WebDiscoveryTabHelper() override; + + WebDiscoveryTabHelper(const WebDiscoveryTabHelper&) = delete; + WebDiscoveryTabHelper& operator=(const WebDiscoveryTabHelper&) = delete; + + private: + friend class content::WebContentsUserData; + + // content::WebContentsObserver: + void DidFinishLoad(content::RenderFrameHost* render_frame_host, + const GURL& url) override; + + raw_ptr wdp_service_; + + WEB_CONTENTS_USER_DATA_KEY_DECL(); +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_BROWSER_WEB_DISCOVERY_TAB_HELPER_H_ diff --git a/components/web_discovery/common/BUILD.gn b/components/web_discovery/common/BUILD.gn new file mode 100644 index 000000000000..69a2e888bad0 --- /dev/null +++ b/components/web_discovery/common/BUILD.gn @@ -0,0 +1,26 @@ +# Copyright (c) 2024 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") +import("//mojo/public/tools/bindings/mojom.gni") + +if (enable_web_discovery_native) { + mojom("mojom") { + sources = [ "web_discovery.mojom" ] + deps = [ "//mojo/public/mojom/base" ] + } +} + +static_library("common") { + sources = [ + "features.cc", + "features.h", + ] + + deps = [ + "//base", + "//brave/components/web_discovery/common/buildflags", + ] +} diff --git a/components/web_discovery/common/buildflags/BUILD.gn b/components/web_discovery/common/buildflags/BUILD.gn new file mode 100644 index 000000000000..5d0fc823c167 --- /dev/null +++ b/components/web_discovery/common/buildflags/BUILD.gn @@ -0,0 +1,12 @@ +# Copyright (c) 2024 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") +import("//build/buildflag_header.gni") + +buildflag_header("buildflags") { + header = "buildflags.h" + flags = [ "ENABLE_WEB_DISCOVERY_NATIVE=$enable_web_discovery_native" ] +} diff --git a/components/web_discovery/common/buildflags/buildflags.gni b/components/web_discovery/common/buildflags/buildflags.gni new file mode 100644 index 000000000000..4b6d5e413031 --- /dev/null +++ b/components/web_discovery/common/buildflags/buildflags.gni @@ -0,0 +1,8 @@ +# Copyright (c) 2024 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +declare_args() { + enable_web_discovery_native = is_win || is_mac || is_linux || is_android +} diff --git a/components/web_discovery/common/features.cc b/components/web_discovery/common/features.cc new file mode 100644 index 000000000000..6eec658cfc8b --- /dev/null +++ b/components/web_discovery/common/features.cc @@ -0,0 +1,14 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/common/features.h" + +namespace web_discovery::features { + +BASE_FEATURE(kWebDiscoveryNative, + "BraveWebDiscoveryNative", + base::FEATURE_DISABLED_BY_DEFAULT); + +} // namespace web_discovery::features diff --git a/components/web_discovery/common/features.h b/components/web_discovery/common/features.h new file mode 100644 index 000000000000..45db0afd2555 --- /dev/null +++ b/components/web_discovery/common/features.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_COMMON_FEATURES_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_COMMON_FEATURES_H_ + +#include "base/feature_list.h" + +namespace web_discovery::features { + +// Enables the native re-implementation of the Web Discovery Project. +// If enabled, the Web Discovery component of the extension should be disabled. +BASE_DECLARE_FEATURE(kWebDiscoveryNative); + +} // namespace web_discovery::features + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_COMMON_FEATURES_H_ diff --git a/components/web_discovery/common/web_discovery.mojom b/components/web_discovery/common/web_discovery.mojom new file mode 100644 index 000000000000..94de01e15d6d --- /dev/null +++ b/components/web_discovery/common/web_discovery.mojom @@ -0,0 +1,36 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +module web_discovery.mojom; + +struct SelectAttributeRequest { + // An optional selector for an element within the current selected element. + // The attribute will be retrieved from the embedded element. + string? sub_selector; + // Arbitrary ID used for storing the scraped result. + string key; + // Name of the attribute to scrape. + string attribute; +}; + +struct SelectRequest { + // The DOM selector for the element to scrape. + string root_selector; + // Scrape requests for the selected element. + array attribute_requests; +}; + +struct AttributeResult { + // The DOM selector for the scraped element. + string root_selector; + // A map of arbitrary IDs to scraped results. Value will be set to + // nullopt if the attribute was not available. + map attribute_values; +}; + +interface DocumentExtractor { + // Extracts DOM attributes from the current page in renderer. + QueryElementAttributes(array requests) => (array results); +}; diff --git a/components/web_discovery/renderer/BUILD.gn b/components/web_discovery/renderer/BUILD.gn new file mode 100644 index 000000000000..b6988bb12d97 --- /dev/null +++ b/components/web_discovery/renderer/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright (c) 2024 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") + +assert(enable_web_discovery_native) + +source_set("renderer") { + sources = [ + "blink_document_extractor.cc", + "blink_document_extractor.h", + ] + + deps = [ + "//base", + "//brave/components/web_discovery/common:mojom", + "//content/public/renderer", + "//mojo/public/cpp/bindings", + "//services/service_manager/public/cpp", + "//third_party/blink/public:blink", + ] +} diff --git a/components/web_discovery/renderer/DEPS b/components/web_discovery/renderer/DEPS new file mode 100644 index 000000000000..082311a6a709 --- /dev/null +++ b/components/web_discovery/renderer/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+content/public/renderer", + "+services/service_manager/public/cpp", + "+third_party/blink/public/web", +] diff --git a/components/web_discovery/renderer/blink_document_extractor.cc b/components/web_discovery/renderer/blink_document_extractor.cc new file mode 100644 index 000000000000..87afbaa33600 --- /dev/null +++ b/components/web_discovery/renderer/blink_document_extractor.cc @@ -0,0 +1,94 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/web_discovery/renderer/blink_document_extractor.h" + +#include +#include + +#include "third_party/blink/public/web/web_element.h" +#include "third_party/blink/public/web/web_local_frame.h" + +namespace web_discovery { + +namespace { + +constexpr char kTextContentAttributeName[] = "textContent"; + +void ProcessAttributeRequests( + std::string root_selector, + const std::vector& requests, + const blink::WebVector& elements, + std::vector& results) { + for (const auto& element : elements) { + auto attributes_result = mojom::AttributeResult::New(); + attributes_result->root_selector = root_selector; + + std::optional sub_element; + const auto* element_to_query = &element; + for (const auto& request : requests) { + if (request->sub_selector) { + auto web_sub_selector = + blink::WebString::FromUTF8(*request->sub_selector); + sub_element = element.QuerySelector(web_sub_selector); + element_to_query = &*sub_element; + } + std::optional attribute_value; + if (!element_to_query->IsNull()) { + if (request->attribute == kTextContentAttributeName) { + attribute_value = element_to_query->TextContent().Utf8(); + } else { + auto attribute_name = blink::WebString::FromUTF8(request->attribute); + auto web_attribute_value = + element_to_query->GetAttribute(attribute_name); + if (!web_attribute_value.IsNull()) { + attribute_value = web_attribute_value.Utf8(); + } + } + } + attributes_result->attribute_values[request->key] = attribute_value; + } + results.push_back(std::move(attributes_result)); + } +} + +} // namespace + +BlinkDocumentExtractor::BlinkDocumentExtractor( + content::RenderFrame* render_frame, + service_manager::BinderRegistry* registry) + : content::RenderFrameObserver(render_frame), render_frame_(render_frame) { + registry->AddInterface(base::BindRepeating( + &BlinkDocumentExtractor::BindReceiver, base::Unretained(this))); +} + +BlinkDocumentExtractor::~BlinkDocumentExtractor() = default; + +void BlinkDocumentExtractor::QueryElementAttributes( + std::vector requests, + QueryElementAttributesCallback callback) { + blink::WebDocument document = render_frame_->GetWebFrame()->GetDocument(); + std::vector results; + for (const auto& request : requests) { + auto selector = blink::WebString::FromUTF8(request->root_selector); + auto elements = document.QuerySelectorAll(selector); + ProcessAttributeRequests(request->root_selector, + request->attribute_requests, elements, results); + } + + std::move(callback).Run(std::move(results)); +} + +void BlinkDocumentExtractor::OnDestruct() { + delete this; +} + +void BlinkDocumentExtractor::BindReceiver( + mojo::PendingReceiver receiver) { + receiver_.reset(); + receiver_.Bind(std::move(receiver)); +} + +} // namespace web_discovery diff --git a/components/web_discovery/renderer/blink_document_extractor.h b/components/web_discovery/renderer/blink_document_extractor.h new file mode 100644 index 000000000000..b502872395c3 --- /dev/null +++ b/components/web_discovery/renderer/blink_document_extractor.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_WEB_DISCOVERY_RENDERER_BLINK_DOCUMENT_EXTRACTOR_H_ +#define BRAVE_COMPONENTS_WEB_DISCOVERY_RENDERER_BLINK_DOCUMENT_EXTRACTOR_H_ + +#include + +#include "brave/components/web_discovery/common/web_discovery.mojom.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_frame_observer.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "services/service_manager/public/cpp/binder_registry.h" + +namespace web_discovery { + +// Extracts attributes from the current page +// for the native re-implementation of Web Discovery. +class BlinkDocumentExtractor : public content::RenderFrameObserver, + public mojom::DocumentExtractor { + public: + BlinkDocumentExtractor(content::RenderFrame* render_frame, + service_manager::BinderRegistry* registry); + ~BlinkDocumentExtractor() override; + + BlinkDocumentExtractor(const BlinkDocumentExtractor&) = delete; + BlinkDocumentExtractor& operator=(const BlinkDocumentExtractor&) = delete; + + // mojom::DocumentExtractor: + void QueryElementAttributes(std::vector requests, + QueryElementAttributesCallback callback) override; + + // RenderFrameObserver: + void OnDestruct() override; + + private: + void BindReceiver(mojo::PendingReceiver receiver); + + raw_ptr render_frame_; + mojo::Receiver receiver_{this}; +}; + +} // namespace web_discovery + +#endif // BRAVE_COMPONENTS_WEB_DISCOVERY_RENDERER_BLINK_DOCUMENT_EXTRACTOR_H_ diff --git a/renderer/sources.gni b/renderer/sources.gni index 0fb4561218a8..7b2f6bb62f51 100644 --- a/renderer/sources.gni +++ b/renderer/sources.gni @@ -9,6 +9,7 @@ import("//brave/components/brave_vpn/common/buildflags/buildflags.gni") import("//brave/components/playlist/common/buildflags/buildflags.gni") import("//brave/components/speedreader/common/buildflags/buildflags.gni") import("//brave/components/tor/buildflags/buildflags.gni") +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") import("//third_party/widevine/cdm/widevine.gni") brave_chrome_renderer_sources = [ @@ -60,6 +61,13 @@ if (enable_ai_chat) { ] } +if (enable_web_discovery_native) { + brave_chrome_renderer_deps += [ + "//brave/components/web_discovery/common", + "//brave/components/web_discovery/renderer", + ] +} + if (enable_playlist) { brave_chrome_renderer_deps += [ "//brave/components/playlist/renderer" ] } diff --git a/script/brave_license_helper.py b/script/brave_license_helper.py index 214d86d3d2d3..657101931c3f 100644 --- a/script/brave_license_helper.py +++ b/script/brave_license_helper.py @@ -42,11 +42,15 @@ def AddBraveCredits(root, prune_paths, special_cases, prune_dirs, # Rust code written by Brave and under the same license as the browser. os.path.join('brave', 'third_party', 'rust', 'adblock_cxx'), + os.path.join('brave', 'third_party', 'rust', 'anonymous_credentials'), + os.path.join('brave', 'third_party', 'rust', + 'anonymous_credentials_cxx'), os.path.join('brave', 'third_party', 'rust', 'brave_news_cxx'), os.path.join('brave', 'third_party', 'rust', 'brave_wallet'), os.path.join('brave', 'third_party', 'rust', 'challenge_bypass_ristretto_cxx'), os.path.join('brave', 'third_party', 'rust', 'constellation_cxx'), + os.path.join('brave', 'third_party', 'rust', 'document_extractor_cxx'), os.path.join('brave', 'third_party', 'rust', 'json_cxx'), os.path.join('brave', 'third_party', 'rust', 'filecoin_cxx'), os.path.join('brave', 'third_party', 'rust', 'skus'), diff --git a/test/BUILD.gn b/test/BUILD.gn index 8df214c359ae..2290534c2db1 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -18,6 +18,7 @@ import("//brave/components/playlist/common/buildflags/buildflags.gni") import("//brave/components/request_otr/common/buildflags/buildflags.gni") import("//brave/components/speedreader/common/buildflags/buildflags.gni") import("//brave/components/tor/buildflags/buildflags.gni") +import("//brave/components/web_discovery/common/buildflags/buildflags.gni") import("//brave/test/testing.gni") import("//brave/updater/config.gni") import("//chrome/common/features.gni") @@ -372,6 +373,10 @@ test("brave_unit_tests") { ] } + if (enable_web_discovery_native) { + deps += [ "//brave/components/web_discovery/browser:unit_tests" ] + } + data = [ "data/" ] if (enable_custom_background) { @@ -1042,6 +1047,10 @@ test("brave_browser_tests") { ] } + if (enable_web_discovery_native) { + deps += [ "//brave/components/web_discovery/browser:browser_tests" ] + } + if (ethereum_remote_client_enabled) { sources += [ "//brave/browser/extensions/brave_wallet_apitest.cc" ] diff --git a/test/data/web_discovery/credential_keys_and_responses.json b/test/data/web_discovery/credential_keys_and_responses.json new file mode 100644 index 000000000000..9fbc3cce99e8 --- /dev/null +++ b/test/data/web_discovery/credential_keys_and_responses.json @@ -0,0 +1,10 @@ +{ + "rsa_priv_key": "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCsD7x4pzqvukjWYyPQiH4KTgxV+ep6hsW+3V3z5vo0M86a4fXcvPnVJ2N4doYz3CsC4YJWftZKiLyY1h/e8ZB9ERd6bkaxRD6lXg26Gry/YPO+gFlhszolLPnFxYaBtTTq4Pt34xseD5jQkiTNXSYNSbpxX/ZNiCn/C55IcUWf7LL6Buufr1ckqz4GIFPclV3+W1+qCb82I3ToMjluKIcApHjXhvZmw7EPogAaxFHXWJmixAE0EVIesBR+DGlPZqqDayiOw2BKZl5zlzvnL04AGyFX7/BWUBZthJuNb6whY20WoU0pZTsf92W0AFthafW4CqPqGXK+QQe4fbtG7f+dAgMBAAECggEAHiUbph/WXldKz5TK/4wKWQ/XhXClrhXSq1/pSAQdreuttOEFzEinlLqz6LULSia2umh8B19td92A/V32c37rC55k+KQ9am1EdICH8yUgEH+R9LxT7JQUCdNZZ1b1+9+dh9Em/Zgidh/RbClOnVRGiGl0asyfQHIsuWx1rMd7pUrvBufy/bEFbcEio7r009qM8CxluS6zRmXO4tWNoZFu42JYjJcEnU+a1mRJ06n1GaVJ0dLd1IA5g4pvVF8wy6R3bQb9xRiwLQMBuG0vLhm69YeyJwHShE5S4WqsubtcxkiZrPak7MLlzwsxk1wU52PbTk1T3geqH5JIerwJ5g7CGQKBgQDq17z9e9OedERboUgXU/nZpSWOqZp5Ukw/dx2xWVtMKhGg6wWoIvadDEeke9RB6JbAlSmBcvT5A8D8P0/1OiatK0i2Yyj1MzuxACnJkYaPjp0Xl4wpZJH1t4QkWEpztPx/JdY6O08WYAbdl0GJhNbL5rqCOS3FZhLpj9OH42IRgwKBgQC7kA0nfifnRRoiL2NwVLeAuG2O4b8JfRPehLw63kl/C4VVbZt0P99EqZwUuKv/nvlrvAfBinwhXvtWhX3GvLUnAQm5eGEt1BMd7GJaIylHFpmlt4+qcoyD+jyd0Jbq4vwiNiupZSZctqRmbu9rLdkoEzSGkkvyY6GQbJxc/ZiAXwKBgAJSb8Px3X3LmIFvbs8MPYQxZdWrR6O7dJWMD/cY8xYltFbq+/tVnSqgXHT75HViX1s4HljxUgrERrw3xAqgsJE1xFpJULZb81MktUUQ80uoFVWOYgxmuiq7zcquNM5AE98N+LhKrdWCzY6TWEqLzbPmbCGtfw5cnANDMMw/K1ERAoGAaLrMvYqR2W8aYpA3ZBfJxxQ0CJ5Av5mZqJxRRkWsoEXck5D6RnULxBk4z9E2KSupdeCuLAGZwkB48xzi2D+yny7TMT7odGCAtCqz2ETd3ZXfAUt36uK/V0o44p4ARvOreabpxlJ2kzpgndm/0gbtxJTEtYem5JeBNVWQEdSAfN0CgYABJjSTY6fDJ1Cj18eiTWY4Tq2phUJQedhZr7/m6e/OZCi40MBiBZ8rdpqqIJzwIVqeei3CTvgZGPa8uKBjMxFZ1ikfNe2+/MA629Cq2FfSNEw1k/+w8uMUMet/Zb+mJ2a6HGZdNfck/5STVYkjKVoIx9jagBBaG1gTbkUcA/XA8Q==", + "rsa_pub_key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArA+8eKc6r7pI1mMj0Ih+Ck4MVfnqeobFvt1d8+b6NDPOmuH13Lz51SdjeHaGM9wrAuGCVn7WSoi8mNYf3vGQfREXem5GsUQ+pV4Nuhq8v2DzvoBZYbM6JSz5xcWGgbU06uD7d+MbHg+Y0JIkzV0mDUm6cV/2TYgp/wueSHFFn+yy+gbrn69XJKs+BiBT3JVd/ltfqgm/NiN06DI5biiHAKR414b2ZsOxD6IAGsRR11iZosQBNBFSHrAUfgxpT2aqg2sojsNgSmZec5c75y9OABshV+/wVlAWbYSbjW+sIWNtFqFNKWU7H/dltABbYWn1uAqj6hlyvkEHuH27Ru3/nQIDAQAB", + "group_pub_key": "I8s5zyMT4UHmqR4a99qBnZWXcQt8l4sePztiSEudSeUZqfnUZaWvV8MmeI/Wo0QwKpFNoxYabxfoqXr26KgDLyRdJc5/pCH4o6v714iE+QqNWQDItrGff9l2UcSYI0QhDugl3PWLCswyqeQjLYZkCIk/Qo4iE5eOmW9cM5TsxQ8DYk2USWuMtz+h72xmfomQo28Fyb1GtdQU4LWxfXMLUR3ttXPoJ/IYTxeZNfFuDWGUa3MWdlAJLgdWNsWaDyPiBgzntlxiw5q5xdCTJkmNcNzbZlVRHeng+ZX41gxY4scIP0+pMVtPQFj4NFAFAxVjMl4Zlrvzuy6+Iut3LYLBxBQG1TiY2dANuXJlvZbOC8jUsgeZvKZnwCG/ljnaZQ0CGyF0/ydNadCsSLPFfTiecSJkXL1Pi5NLxzacG/4QBPIhy4VjU4Gg17HNzbYuKljCvS0jAbYsuDEHqi7Z4MXTpREafV3WTE9yGdB2zE/RMRklUNukcAC1ayAjb4tIw6yD", + "join_responses": { + "20240622": "BAh+HPSF0v2NgWIsqbaFlZokWS5+yuzg0WJAcT1AlAZwIz5/e7n2v7P79pa3LzKjklJ4xUM/WZiujbNZd3gfX5QEFS55KwibK5dEdYMTzDiHWMU4OSIV7p/ZWmjs61Apbx8gnckDONbUOrf4FR2Y46SMp4/VxaZju+S6qBVFdbgqdQQG85r4caFZa56KdERRvy19pfnCdYTDvKedWs7V8xc6LhXjMbFIMlRv0ExlONRTtD3NqOynppR9UCIZmVyaQ+FiBA+uiU9RkwWpAvg2oTAWyS0XbgV6xFe2YlY1mvbpX2ZyEPS+aCmbJo/8/X0piHvcd01D6ytLZfUKQ3qFcoh2mZUGRo7N4bow9J5+dHZkSDQN/r94XLA4uJrTM2dxeFeNAyNlEuSYnxzA9YBRF6YyPuRlWRHXxhSlqO0rbspV+fEX", + "20240623": "BAms4nX6G89VWTmPwwy+sMc9H+ymwfTeBGbtkle0XByTFtkhnki68EGUgfgrYzOQBffl7XlMvIfmtV9nTPr6GF4EFpldOPmsuo9qj6xcL5sSKt5UJh2GrKi2Xd1pXtsU3b0gRy3TCQfxrbLN9UtJTlgazIraaVLOaD9mNiG2A71s8gQk0AdA+70haTw/ATXuEn0nQwVxNs/ZQokykOJ2d3GUmh7VG7Hwdylc/kwQxBqx7G3qILydEifJ6rI9XBFiYjSUBAutyyC9rWtcFLspGoyeb26/8vDtR0xpB6EyNSRmuiYKGHzY4FJPI5LzWrbawR7EDMfQmX2d4UbH3s6tbAK7we8BdyNNdafqz7Lu4FMS8FzI6dVsO/kkxsgK8ihAgCYpBxOTcw5QkXxHzbyMfMv04hJ0GM0xpjAtqe32r197jvC7", + "20240624": "BCHYKUHSD/1fgTW15TKBdgdT15gxg3vVwycHfZfdwhT9DTLp6o/AoAq31yBKjCdS2YOyR+w1jU9XTIA5fhaeWxUEA97oWXnmVn1A+K6laHCRvi2XvBl8JpZHX329Z013j1oKVPhnAoAPpk3l365bQ5YTtl2xTzRtYRGjkGs+VtKvWwQSkXXdd7SotMuCuMmigtm35FYzv1Y6u3Xc5DbTAfL56BFXKGErbIfHXsoO6XsDgp0wDMMsDsLiMFqwSpzNZu48BBc1Cx4hYcLGAiJAHArLDAJhtQReTBQy0rxEjB+/ZIBCB+M54YCaL8w1Ug71oXxzHrMlxq9Ba1R8vSaXxtUhb1MWLRJM7HftXZsekyYF7XzCyjZCij56fl7Sm/N0NPoPnAOQJ3joU8LCUWJKTvaxBE5SpcVXauvY8jm3xUSoEtIz" + } +} diff --git a/test/data/web_discovery/hpn-config.json b/test/data/web_discovery/hpn-config.json new file mode 100644 index 000000000000..c4d335233ca9 --- /dev/null +++ b/test/data/web_discovery/hpn-config.json @@ -0,0 +1,146 @@ +{ + "minVersion": 1, + "groupPubKeys": { + "20240624": "ITmwC7dQpF5vcMFbe8U/k7hdg7ZSWXsOswfkcxm7nJoBsx1c6O+kJcyiXIZR08t5Nf64tUOwptEDqxWkIWLToQ0S2G21Yfh9Vif0+zAb45f1m3+9plm9sKncHFNuoeJZI/f/vQfeo8yd/jpqCKMXNY6VWWlEMESYIeIsLZtUxPUOx6lQG09iRtrvHUHVGlaN5VGAKNoTVFfiCgNl373iZR1k0ZvW0wdA7OkJiTVj3+osawlfpeYhZSmdWo7qsZ3UDkoy9faYFBGsTScQJsLsgQh72gQBa3VgIWPWt/lLn38QbqymAMc/OZsf11hjkXP59t9V/9Ugv7vYFsAxRT9SLQR8eB8sATtrQ1DmqCesvhdCpOIftZOfn70u4S8pYrXGHTyXNZBSYo6CG0nj0cJOfXK9wrm/PeSXB5wdLQSLzqoHrzR8cEuJEbWW1D/4lAVGMGQTMOLLOcxo0n9nH+QG6iNoT1aIaPjmn2QwAx/ohIEH53YKkxp0DNmTDQOETI/k", + "20240625": "DSoyIdA3VA6CjOhWEBh7ZCreAI2nOYD2JUUynaP+prUgM/GOl68av5u+3KFRhrrITKwGDOLzPV3wCM/FRkG1HxINxQRHFNOgcE9qKbc75LAmjIYuoAE+VqBdmvlYXzzsFyKCFhytVsiZ7DLAPX9fmRdkxJidA5KEF8k5EzOKtXkLp/PzWaFqDsif3zSsm/yXkQOtjfd7XhpJ0kmYEuhS0BY9FeuCGvkIhvW1zEf7WDVaty06yAr2QlpsPDHd604fGlrzNh/41PiXsiWtWLgKG1MLRvt6yV0jnfhF2Okawl4ecdl0CkSvxiusZyKgLSKVSiEP00fY2BR1fYyH/DL3fQXrAZo+MhY91V3M55oxsnliSxLRtcU559rNokQuTKFXAHiVTnDPI40Def11OLLPp/ZQgICtUa/azW4cu6sEkY0KE8ebJDplsqM5ehsMh5RZeVm7NJZEret4z6hFtm45jhgyEDoQMyv5Mn86X0q8QtAdM6wKpVRU2ymy9S2DdqKt", + "20240626": "FuqBHl3PCRXlhHSqFSrQBhCOzgmZQDCZLR27U/a+AlUj6H+XXoN5jroQTKJ091Rb7W360aIgS+I4CLCQgZS+ERCEikPh5WNlvLV1UpQ6pDtpCFZagrirsLQLx+CEqEojC436N9vC+OOM9Gg/gZWuBliAQRkDwzlwhJ+at/qFZeojH+NSvCDdgUVIgkw60BxDNFrOFoJknfxXf7B6m9pjYQ+ZDGppdvJMrhMiARX1jMce72QbzSlSxbzRE1PK951gJIKICdDjY51hP7aQM9WqSswd8LNEp741yAVyFd3n4aEVSaYUgzykpzzTIGf7pVOt8CQXqBzJZzTc6/+1WuUyfhDNFEnO3Ep4yAb3H03Mv9SXBSwGaoucaL8kGRZfSyMUC2idJUQKbg6VOPXAEE2LMfSQQzz5Zgl6GSROv45QgRIMhXqDLdiSknFfmUQUKFkMi7oBN2JYesemz8QIIyw0px90wMchsF23n7EdtVVBj0E7HVj/uMISTIxeQY7mv27n", + "20240627": "DTwg0yh8wrUSKWVXlhp879+E64ART4w5Drt47ezgCdIb0HgZeU3iE2pc8kMb+UarK0XWA5uC0jA6vrkXJlcNVAY1sEl7NO/V8eDI5EavwbXiTJzBlCJabHeeanE7yLMTGXofjgxYUvGjAiompViQsaQH+6hAh96LNxBmL8lnGscRjn6t12/YeaiYui/fvmGfjf+05bnz4gcKl8U+3k2lQg+t9jY8qzzNJZOwk4FutovPBWwDcu8Mms2Z2K1+MErJAkGjP2wrNDBqgQm2s0hdQDNhOPGKqLv1CnPsLf+5EZEF6FYvU3e71n0fX9y8pGBMVRvSW0zlqbF9l/W7+oM4+g3WHQrPirJq4v7D7OZ4pjgpLTHjxGms7qhOqJHrEbCzBBtgR1MjKvPj2lIjGld+TE2JznLavqJ9aKhxc5EfoZAczdWSIR+qbFpt7RcyeTpNIdZWQkDG2sEavlFXWfzb7gn5GZwg8YPL4XKoSyErqKl1TvIuGws/oehTzNe4Nz/B" + }, + "pubKeys": { + "20240624": "BECQDFoOR0DE3wLaDidGAC/2Mpgjasf9QgJDGGLTkTdll+pW2S/RgX0pkFyDjQZc6efyX3RGQKJ2cq8HOB8vZOo=", + "20240625": "BEURJ7UWSqV/OsxhCDyoQtAX2F5Vm/CKh6NS8SjIvz5RWtVOkyfEnR+XkdcldmyQk2AwOhLMEV/ZIwbThVmvmrQ=", + "20240626": "BDjEnHSmPhNywOIvDo2kWAzz7S/Cf2xDZEjZTTOp0FCb/Qk6uoSb8R7PF8Ne2RAMkCifeKIUtR1mF/Enmb+VFBM=", + "20240627": "BO7isKljTTltnCBtM4hRUV0fcxlH4kjvWVVummU2irqq6i/jLSgF+lR+/qOL4fqrzCsBJnIn8lTvsEOAfzg9XDM=" + }, + "sourceMap": { + "actions": { + "alive": { + "keys": [ + "t->url" + ] + }, + "page": { + "keys": [ + "url->url" + ] + }, + "query": { + "keys": [ + "q->url" + ] + }, + "query0": { + "keys": [ + "q->url" + ] + }, + "query-am": { + "keys": [ + "q->url" + ] + }, + "img": { + "keys": [ + "q->url" + ] + }, + "img-p": { + "keys": [ + "q->url" + ] + }, + "video-p": { + "keys": [ + "q->url" + ] + }, + "videos-p": { + "keys": [ + "q->url" + ] + }, + "place": { + "keys": [ + "->obj" + ] + }, + "places": { + "keys": [ + "->obj" + ] + }, + "places2": { + "keys": [ + "q->url" + ] + }, + "category-am": { + "keys": [ + "curl" + ] + }, + "ads_A": { + "keys": [ + "q->url" + ] + }, + "ads_B": { + "keys": [ + "q->url" + ] + }, + "ads_C": { + "keys": [ + "q->url" + ] + }, + "snippet": { + "keys": [ + "q->url" + ] + }, + "snippet2": { + "keys": [ + "q->url" + ] + }, + "snippet3": { + "keys": [ + "q->url" + ] + }, + "linkedin": { + "keys": [ + "profileLink->url" + ] + }, + "hw.telemetry.actionstats": {}, + "locdata": { + "keys": [ + "q->url" + ] + }, + "ads_D": { + "keys": [ + "q->url" + ] + }, + "usercontext": { + "limit": 0 + }, + "sq": { + "keys": [ + "oq->url" + ] + }, + "ad-ctr": { + "limit": 0 + }, + "top-stories": { + "keys": [ + "q->url" + ] + } + } + } +} diff --git a/test/data/web_discovery/page.html b/test/data/web_discovery/page.html new file mode 100644 index 000000000000..d987ae3ed875 --- /dev/null +++ b/test/data/web_discovery/page.html @@ -0,0 +1,21 @@ + + + + Test Page To Scrape + + +
+
+
A query
+ Foo1 +
+
+ Foo2 +
+
+ Foo3 + +
+
+ + diff --git a/test/data/web_discovery/patterns.gz b/test/data/web_discovery/patterns.gz new file mode 100644 index 000000000000..966ab4849152 Binary files /dev/null and b/test/data/web_discovery/patterns.gz differ diff --git a/test/data/web_discovery/patterns.json b/test/data/web_discovery/patterns.json new file mode 100644 index 000000000000..cdbfeec9dd70 --- /dev/null +++ b/test/data/web_discovery/patterns.json @@ -0,0 +1,200 @@ +{ + "normal": { + "urlPatterns": [ + "^https://example1.com", + "^https://notasearchengine.biz", + "^https://search.example2.com" + ], + "searchEngines": [ + "0", + "2" + ], + "scrape": { + "0": { + "form .search": { + "q": { + "item": "input", + "type": "searchQuery", + "etype": "value", + "keyName": "q" + } + } + }, + "1": { + ".field1 input": { + "t": { + "type": "other", + "etype": "href", + "keyName": "t" + } + }, + ".field2 input": { + "t": { + "type": "other", + "etype": "href", + "keyName": "t", + "functionsApplied": [ + [ + "parseU", + "qs", + "t" + ] + ] + }, + "b": { + "type": "other", + "etype": "textContent", + "keyName": "b" + } + } + }, + "2": { + "form .search-box": { + "q": { + "item": "input", + "type": "searchQuery", + "etype": "value", + "keyName": "q" + } + } + } + }, + "payloads": { + "1": { + "key1": { + "type": "single", + "results": "single", + "action": "t" + } + } + }, + "idMapping": { + "0": "ex1", + "1": "nase", + "2": "ex2" + } + }, + "strict": { + "urlPatterns": [ + "^https://example1.com.", + "^https://notasearchengine.biz", + "^https://search.example2.com" + ], + "searchEngines": [ + "0", + "2" + ], + "scrape": { + "0": { + "form .search": { + "q": { + "item": "input", + "type": "searchQuery", + "etype": "value", + "keyName": "q" + } + }, + "qurl": { + "qurl": { + "type": "standard", + "etype": "url", + "keyName": "qurl", + "functionsApplied": [ + [ + "maskU", + false, + false + ] + ] + } + }, + "ctry": { + "ctry": { + "type": "standard", + "etype": "ctry", + "keyName": "ctry" + } + } + }, + "1": { + "ctry": { + "ctry": { + "type": "standard", + "etype": "ctry", + "keyName": "ctry" + } + }, + "#content .a1": { + "age": { + "type": "arr", + "etype": "textContent", + "keyName": "age" + } + } + }, + "2": { + "form .search-box": { + "age": { + "item": ".created-at", + "type": "other", + "etype": "value", + "keyName": "age" + } + } + } + }, + "payloads": { + "0": { + "key1": { + "type": "single", + "results": "single", + "action": "query" + } + }, + "1": { + "key1": { + "type": "query", + "results": "clustered", + "action": "age-info", + "fields": [ + [ + "#content .a1", + "age" + ], + [ + "ctry", + "ctry" + ] + ] + } + }, + "2": { + "key2": { + "type": "query", + "results": "clustered", + "action": "age-info", + "fields": [ + [ + "form .search-box", + "age", + "join" + ] + ] + } + } + }, + "queryTemplate": { + "0": { + "prefix": "search?query=" + }, + "1": { + "prefix": "directory?query=" + } + }, + "idMapping": { + "0": "ex1", + "1": "nase", + "2": "ex2" + } + } +} diff --git a/test/data/web_discovery/quorum-config.json b/test/data/web_discovery/quorum-config.json new file mode 100644 index 000000000000..95792257497f --- /dev/null +++ b/test/data/web_discovery/quorum-config.json @@ -0,0 +1 @@ +{"oc":65,"expiry":30,"location":"ca","threshold":5} \ No newline at end of file diff --git a/third_party/rust/anonymous_credentials/v0_1/README.chromium b/third_party/rust/anonymous_credentials/v0_1/README.chromium new file mode 100644 index 000000000000..27bae2a94df4 --- /dev/null +++ b/third_party/rust/anonymous_credentials/v0_1/README.chromium @@ -0,0 +1,8 @@ +Name: anonymous-credentials +URL: https://crates.io/crates/anonymous-credentials +Description: Implementation of Direct Anonymous Attestation for the Web Discovery Project +Version: 0.1.4 +Security Critical: yes +Shipped: yes +License: Mozilla Public License 2.0 +License File: diff --git a/third_party/rust/anonymous_credentials_cxx/v0_1/README.chromium b/third_party/rust/anonymous_credentials_cxx/v0_1/README.chromium new file mode 100644 index 000000000000..f20c1192a5fe --- /dev/null +++ b/third_party/rust/anonymous_credentials_cxx/v0_1/README.chromium @@ -0,0 +1,8 @@ +Name: anonymous-credentials-cxx +URL: https://crates.io/crates/anonymous-credentials-cxx +Description: +Version: 0.1.0 +Security Critical: yes +Shipped: yes +License: Mozilla Public License 2.0 +License File: diff --git a/third_party/rust/brave_miracl/v0_1/BUILD.gn b/third_party/rust/brave_miracl/v0_1/BUILD.gn new file mode 100644 index 000000000000..28a360c7d608 --- /dev/null +++ b/third_party/rust/brave_miracl/v0_1/BUILD.gn @@ -0,0 +1,67 @@ +# Copyright (c) 2024 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +# @generated from third_party/rust/chromium_crates_io/BUILD.gn.hbs by +# tools/crates/gnrt. +# Do not edit! + +import("//build/rust/cargo_crate.gni") + +cargo_crate("lib") { + crate_name = "brave_miracl" + epoch = "0.1" + crate_type = "rlib" + crate_root = "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/lib.rs" + sources = [ + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/aes.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/arch.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/big.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/bls.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/dbig.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecdh.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecp.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecp2.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/eddsa.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp12.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp2.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp4.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/hpke.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/mod.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/mpin.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/pair.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/rom.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/dilithium.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/gcm.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash256.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash384.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash512.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hmac.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/kyber.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/lib.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/main.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/nhs.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/rand.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/sha3.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/share.rs", + "//brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/x509.rs", + ] + inputs = [] + + build_native_rust_unit_tests = false + edition = "2021" + cargo_pkg_version = "0.1.3" + cargo_pkg_authors = "Mike Scott " + cargo_pkg_name = "brave-miracl" + cargo_pkg_description = + "Subset of the MIRACL Core library that includes the bn254 elliptic curve" + library_configs -= [ "//build/config/compiler:chromium_code" ] + library_configs += [ "//build/config/compiler:no_chromium_code" ] + executable_configs -= [ "//build/config/compiler:chromium_code" ] + executable_configs += [ "//build/config/compiler:no_chromium_code" ] + proc_macro_configs -= [ "//build/config/compiler:chromium_code" ] + proc_macro_configs += [ "//build/config/compiler:no_chromium_code" ] + features = [ "std" ] +} diff --git a/third_party/rust/brave_miracl/v0_1/README.chromium b/third_party/rust/brave_miracl/v0_1/README.chromium new file mode 100644 index 000000000000..2220ddbaabca --- /dev/null +++ b/third_party/rust/brave_miracl/v0_1/README.chromium @@ -0,0 +1,9 @@ +Name: brave-miracl +URL: https://crates.io/crates/brave-miracl +Description: Subset of the MIRACL Core library that includes the bn254 elliptic curve +Version: 0.1.3 +Security Critical: yes +Shipped: yes +License: Apache 2.0 +License File: //brave/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/LICENSE +Revision: fb447a11105dae466ddd7f9eb666a30cbceff539 diff --git a/third_party/rust/chromium_crates_io/Cargo.lock b/third_party/rust/chromium_crates_io/Cargo.lock index 281d8105b706..4bf611fee499 100644 --- a/third_party/rust/chromium_crates_io/Cargo.lock +++ b/third_party/rust/chromium_crates_io/Cargo.lock @@ -100,6 +100,24 @@ dependencies = [ "libc", ] +[[package]] +name = "anonymous-credentials" +version = "0.1.4" +dependencies = [ + "brave-miracl", + "lazy_static", + "rand 0.8.5", + "thiserror", +] + +[[package]] +name = "anonymous-credentials-cxx" +version = "0.1.0" +dependencies = [ + "anonymous-credentials", + "cxx", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -251,6 +269,11 @@ dependencies = [ "subtle", ] +[[package]] +name = "brave-miracl" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "brave-news-cxx" version = "1.0.0" @@ -373,10 +396,12 @@ name = "chromium" version = "0.1.0" dependencies = [ "adblock-cxx", + "anonymous-credentials-cxx", "brave-news-cxx", "brave_wallet", "challenge-bypass-ristretto-cxx", "constellation-cxx", + "document-extractor-cxx", "filecoin-cxx", "json-cxx", "skus-cxx", @@ -660,6 +685,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "document-extractor-cxx" +version = "0.1.0" +dependencies = [ + "cxx", + "html5ever", + "kuchikiki", +] + [[package]] name = "dtoa" version = "0.4.8" diff --git a/third_party/rust/chromium_crates_io/Cargo.toml b/third_party/rust/chromium_crates_io/Cargo.toml index ea92188a663d..465ffeafebc2 100644 --- a/third_party/rust/chromium_crates_io/Cargo.toml +++ b/third_party/rust/chromium_crates_io/Cargo.toml @@ -26,6 +26,8 @@ adblock-cxx = { version = "1" } constellation-cxx = "0.1" challenge-bypass-ristretto-cxx = "1" zcash = "1" +anonymous-credentials-cxx = "0.1" +document-extractor-cxx = "0.1" [patch.crates-io.futures-retry_v0_5] path = "../futures_retry/v0_5/crate" @@ -79,6 +81,18 @@ package = "constellation-cxx" path = "../../../components/challenge_bypass_ristretto/rust/cxx" package = "challenge-bypass-ristretto-cxx" +[patch.crates-io.anonymous-credentials_v0_1] +path = "../../../components/web_discovery/browser/anonymous_credentials/rs/lib" +package = "anonymous-credentials" + +[patch.crates-io.anonymous-credentials-cxx_v0_1] +path = "../../../components/web_discovery/browser/anonymous_credentials/rs/cxx" +package = "anonymous-credentials-cxx" + +[patch.crates-io.document-extractor_v0_1] +path = "../../../components/web_discovery/browser/document_extractor/rs" +package = "document-extractor-cxx" + [patch.crates-io.aho_corasick_v1] path = "../../../../third_party/rust/chromium_crates_io/vendor/aho-corasick-1.1.3" package = "aho-corasick" diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.cargo-checksum.json b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.cargo-checksum.json new file mode 100644 index 000000000000..697c9ce2fbb4 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{}} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.cargo_vcs_info.json new file mode 100644 index 000000000000..e273ed2a6c61 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "fb447a11105dae466ddd7f9eb666a30cbceff539" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.gitignore b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.gitignore new file mode 100644 index 000000000000..96ef6c0b944e --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.gitmodules b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.gitmodules new file mode 100644 index 000000000000..987edd9563d6 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/.gitmodules @@ -0,0 +1,3 @@ +[submodule "miracl-core"] + path = miracl-core + url = https://github.com/miracl/core diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/Cargo.toml new file mode 100644 index 000000000000..e0d3f4510d19 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/Cargo.toml @@ -0,0 +1,34 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "brave-miracl" +version = "0.1.3" +authors = ["Mike Scott "] +exclude = [ + "/miracl-core", + "generate.sh", + "renovate.json", +] +description = "Subset of the MIRACL Core library that includes the bn254 elliptic curve" +homepage = "https://github.com/miracl/core" +readme = "README.md" +keywords = ["bn254"] +categories = ["cryptography"] +license = "Apache-2.0" +repository = "https://github.com/brave-experiments/miracl-rs" + +[dependencies] + +[features] +default = ["std"] +std = [] diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/Cargo.toml.orig new file mode 100644 index 000000000000..13ebaef9e368 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/Cargo.toml.orig @@ -0,0 +1,18 @@ +[package] +name = "brave-miracl" +version = "0.1.3" +edition = "2021" +authors = ["Mike Scott "] +description = "Subset of the MIRACL Core library that includes the bn254 elliptic curve" +license = "Apache-2.0" +homepage = "https://github.com/miracl/core" +repository = "https://github.com/brave-experiments/miracl-rs" +keywords = ["bn254"] +categories = ["cryptography"] +exclude = ["/miracl-core", "generate.sh", "renovate.json"] + +[dependencies] + +[features] +default = ["std"] +std = [] diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/LICENSE b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/LICENSE new file mode 100644 index 000000000000..3c8565d62f17 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 Miracl Technologies Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/README.md b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/README.md new file mode 100644 index 000000000000..67423ec88b3f --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/README.md @@ -0,0 +1,15 @@ +# brave-miracl + +[![Crates.io](https://img.shields.io/crates/v/brave-miracl?style=for-the-badge)](https://crates.io/crates/brave-miracl) + +A subset of the [MIRACL Core](https://github.com/miracl/core) library that includes the bn254 elliptic curve. + +## Updating the library code + +MIRACL Core is embedded in this repository as a git submodule. + +The library code can be updated and regenerated by executing `./generate.sh`. + +## License + +MIRACL Core is licensed under the [Apache 2.0 License](LICENSE). diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/aes.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/aes.rs new file mode 100644 index 000000000000..1242e351b807 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/aes.rs @@ -0,0 +1,888 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pub const ECB: usize = 0; +pub const CBC: usize = 1; +pub const CFB1: usize = 2; +pub const CFB2: usize = 3; +pub const CFB4: usize = 5; +pub const OFB1: usize = 14; +pub const OFB2: usize = 15; +pub const OFB4: usize = 17; +pub const OFB8: usize = 21; +pub const OFB16: usize = 29; +pub const CTR1: usize = 30; +pub const CTR2: usize = 31; +pub const CTR4: usize = 33; +pub const CTR8: usize = 37; +pub const CTR16: usize = 45; + +const INCO: [u8; 4] = [0xB, 0xD, 0x9, 0xE]; /* Inverse Coefficients */ + +const PTAB: [u8; 256] = [ + 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53, 95, 225, 56, 72, 216, 115, + 149, 164, 247, 2, 6, 10, 30, 34, 102, 170, 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, + 112, 144, 171, 230, 49, 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205, + 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136, 131, 158, 185, 208, + 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154, 181, 196, 87, 249, 16, 48, 80, 240, + 11, 29, 39, 105, 187, 214, 97, 163, 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, + 233, 32, 96, 160, 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65, 195, + 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, 159, 186, 213, 100, 172, + 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, 155, 182, 193, 88, 232, 35, 101, 175, + 234, 37, 111, 177, 200, 67, 197, 84, 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, + 203, 70, 202, 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, 18, 54, + 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23, 57, 75, 221, 124, 132, 151, + 162, 253, 28, 36, 108, 180, 199, 82, 246, 1, +]; + +const LTAB: [u8; 256] = [ + 0, 255, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3, 100, 4, 224, 14, 52, 141, + 129, 239, 76, 113, 8, 200, 248, 105, 28, 193, 125, 194, 29, 181, 249, 185, 39, 106, 77, 228, + 166, 114, 154, 201, 9, 120, 101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53, 147, 218, + 142, 150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241, 64, 70, 131, 56, 102, 221, 253, + 48, 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16, 126, 110, 72, 195, 163, 182, 30, 66, + 58, 107, 40, 84, 250, 133, 61, 186, 43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243, + 115, 167, 87, 175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232, 44, + 215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81, 160, 127, 12, 246, 111, 23, + 196, 73, 236, 216, 67, 31, 45, 164, 118, 123, 183, 204, 187, 62, 90, 251, 96, 177, 134, 59, 82, + 161, 108, 170, 85, 41, 157, 151, 178, 135, 144, 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, + 91, 209, 83, 57, 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171, 68, 17, 146, + 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165, 103, 74, 237, 222, 197, 49, 254, + 24, 13, 99, 140, 128, 192, 247, 112, 7, +]; + +const FBSUB: [u8; 256] = [ + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, + 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, + 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, + 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, + 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, + 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, + 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, + 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, + 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, + 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, + 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, + 153, 45, 15, 176, 84, 187, 22, +]; + +const RBSUB: [u8; 256] = [ + 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, 124, 227, 57, 130, + 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, + 238, 76, 149, 11, 66, 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, + 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146, 108, + 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, + 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, 193, 175, + 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, + 115, 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26, + 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32, + 154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, + 128, 236, 95, 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160, + 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, + 214, 38, 225, 105, 20, 99, 85, 33, 12, 125, +]; + +const RCO: [u8; 16] = [ + 1, 2, 4, 8, 16, 32, 64, 128, 27, 54, 108, 216, 171, 77, 154, 47, +]; + +const FTABLE: [u32; 256] = [ + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, + 0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, + 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb, + 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, + 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83, + 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, + 0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0xf05050a, 0xb59a9a2f, + 0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, + 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, + 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, + 0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, + 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, + 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, + 0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, + 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0xef3f3fd, 0x6dd2d2bf, + 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, + 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, + 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, + 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, + 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8, + 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, + 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, + 0xb46c6cd8, 0xfa5656ac, 0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, + 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, + 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, + 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c, + 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, + 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, + 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, + 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c, +]; + +const RTABLE: [u32; 256] = [ + 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, + 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, + 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, + 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, + 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, + 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, + 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, + 0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, + 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, + 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x69f715e, 0x51106ebd, + 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060, + 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, + 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, + 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, + 0xb1670a0c, 0xfe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, + 0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, + 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, + 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, + 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, + 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, + 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, + 0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, + 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, + 0x97826cd, 0xf418596e, 0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef, + 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, + 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117, + 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, + 0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, + 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, + 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, + 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc, 0x8b493c28, 0x41950dff, + 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0, +]; + +pub struct AES { + // nk: usize, + nr: usize, + mode: usize, + fkey: [u32; 60], + rkey: [u32; 60], + pub f: [u8; 16], +} + +fn rotl8(x: u32) -> u32 { + ((x) << 8) | ((x) >> 24) +} + +fn rotl16(x: u32) -> u32 { + ((x) << 16) | ((x) >> 16) +} + +fn rotl24(x: u32) -> u32 { + ((x) << 24) | ((x) >> 8) +} + +fn pack(b: [u8; 4]) -> u32 { + /* pack bytes into a 32-bit Word */ + ((b[3] as u32) << 24) | ((b[2] as u32) << 16) | ((b[1] as u32) << 8) | (b[0] as u32) +} + +fn unpack(a: u32) -> [u8; 4] { + /* unpack bytes from a word */ + [ + (a & 0xff) as u8, + ((a >> 8) & 0xff) as u8, + ((a >> 16) & 0xff) as u8, + ((a >> 24) & 0xff) as u8, + ] +} + +fn bmul(x: u8, y: u8) -> u8 { + /* x.y= AntiLog(Log(x) + Log(y)) */ + let ix = (x as usize) & 0xff; + let iy = (y as usize) & 0xff; + let lx = (LTAB[ix] as usize) & 0xff; + let ly = (LTAB[iy] as usize) & 0xff; + + if x != 0 && y != 0 { + PTAB[(lx + ly) % 255] + } else { + 0 + } +} + +fn subbyte(a: u32) -> u32 { + let mut b = unpack(a); + b[0] = FBSUB[b[0] as usize]; + b[1] = FBSUB[b[1] as usize]; + b[2] = FBSUB[b[2] as usize]; + b[3] = FBSUB[b[3] as usize]; + pack(b) +} + +fn product(x: u32, y: u32) -> u8 { + /* dot product of two 4-byte arrays */ + let xb = unpack(x); + let yb = unpack(y); + + bmul(xb[0], yb[0]) ^ bmul(xb[1], yb[1]) ^ bmul(xb[2], yb[2]) ^ bmul(xb[3], yb[3]) +} + +fn invmixcol(x: u32) -> u32 { + /* matrix Multiplication */ + let mut b: [u8; 4] = [0; 4]; + let mut m = pack(INCO); + b[3] = product(m, x); + m = rotl24(m); + b[2] = product(m, x); + m = rotl24(m); + b[1] = product(m, x); + m = rotl24(m); + b[0] = product(m, x); + pack(b) +} + +fn increment(f: &mut [u8; 16]) { + for i in 0..16 { + f[i] += 1; + if f[i] != 0 { + break; + } + } +} + +impl AES { + pub fn new() -> AES { + AES { + // nk: 0, + nr: 0, + mode: 0, + fkey: [0; 60], + rkey: [0; 60], + f: [0; 16], + } + } + + /* reset cipher */ + pub fn reset(&mut self, m: usize, iv: Option<[u8; 16]>) { + /* reset mode, or reset iv */ + self.mode = m; + for i in 0..16 { + self.f[i] = 0 + } + if self.mode != ECB { + if let Some(x) = iv { + for i in 0..16 { + self.f[i] = x[i] + } + } + } + } + + pub fn init(&mut self, m: usize, nkey: usize, key: &[u8], iv: Option<[u8; 16]>) -> bool { + /* Key Scheduler. Create expanded encryption key */ + let mut cipherkey: [u32; 8] = [0; 8]; + let mut b: [u8; 4] = [0; 4]; + let nk = nkey / 4; + if nk != 4 && nk != 6 && nk != 8 { + return false; + } + let nr = 6 + nk; + //self.nk = nk; + self.nr = nr; + self.reset(m, iv); + let n = 4 * (nr + 1); + + let mut j = 0; + for i in 0..nk { + for k in 0..4 { + b[k] = key[j + k] + } + cipherkey[i] = pack(b); + j += 4; + } + + for i in 0..nk { + self.fkey[i] = cipherkey[i] + } + + j = nk; + let mut k = 0; + while j < n { + self.fkey[j] = self.fkey[j - nk] ^ subbyte(rotl24(self.fkey[j - 1])) ^ (RCO[k] as u32); + if nk <= 6 { + for i in 1..nk { + if (i + j) >= n { + break; + } + self.fkey[i + j] = self.fkey[i + j - nk] ^ self.fkey[i + j - 1]; + } + } else { + for i in 1..4 { + if (i + j) >= n { + break; + } + self.fkey[i + j] = self.fkey[i + j - nk] ^ self.fkey[i + j - 1]; + } + + if (j + 4) < n { + self.fkey[j + 4] = self.fkey[j + 4 - nk] ^ subbyte(self.fkey[j + 3]); + } + for i in 5..nk { + if (i + j) >= n { + break; + } + self.fkey[i + j] = self.fkey[i + j - nk] ^ self.fkey[i + j - 1]; + } + } + j += nk; + k += 1; + } + + /* now for the expanded decrypt key in reverse order */ + + for j in 0..4 { + self.rkey[j + n - 4] = self.fkey[j] + } + let mut i = 4; + while i < n - 4 { + let k = n - 4 - i; + for j in 0..4 { + self.rkey[k + j] = invmixcol(self.fkey[i + j]) + } + i += 4; + } + for j in n - 4..n { + self.rkey[j + 4 - n] = self.fkey[j] + } + true + } + + pub fn getreg(&mut self) -> [u8; 16] { + let mut ir: [u8; 16] = [0; 16]; + for i in 0..16 { + ir[i] = self.f[i] + } + ir + } + + /* Encrypt a single block */ + pub fn ecb_encrypt(&mut self, buff: &mut [u8; 16]) { + let mut b: [u8; 4] = [0; 4]; + let mut p: [u32; 4] = [0; 4]; + let mut q: [u32; 4] = [0; 4]; + + let mut j = 0; + for i in 0..4 { + for k in 0..4 { + b[k] = buff[j + k] + } + p[i] = pack(b); + p[i] ^= self.fkey[i]; + j += 4; + } + + let mut k = 4; + + /* State alternates between p and q */ + for _ in 1..self.nr { + q[0] = self.fkey[k] + ^ FTABLE[(p[0] & 0xff) as usize] + ^ rotl8(FTABLE[((p[1] >> 8) & 0xff) as usize]) + ^ rotl16(FTABLE[((p[2] >> 16) & 0xff) as usize]) + ^ rotl24(FTABLE[((p[3] >> 24) & 0xff) as usize]); + + q[1] = self.fkey[k + 1] + ^ FTABLE[(p[1] & 0xff) as usize] + ^ rotl8(FTABLE[((p[2] >> 8) & 0xff) as usize]) + ^ rotl16(FTABLE[((p[3] >> 16) & 0xff) as usize]) + ^ rotl24(FTABLE[((p[0] >> 24) & 0xff) as usize]); + + q[2] = self.fkey[k + 2] + ^ FTABLE[(p[2] & 0xff) as usize] + ^ rotl8(FTABLE[((p[3] >> 8) & 0xff) as usize]) + ^ rotl16(FTABLE[((p[0] >> 16) & 0xff) as usize]) + ^ rotl24(FTABLE[((p[1] >> 24) & 0xff) as usize]); + + q[3] = self.fkey[k + 3] + ^ FTABLE[(p[3] & 0xff) as usize] + ^ rotl8(FTABLE[((p[0] >> 8) & 0xff) as usize]) + ^ rotl16(FTABLE[((p[1] >> 16) & 0xff) as usize]) + ^ rotl24(FTABLE[((p[2] >> 24) & 0xff) as usize]); + + k += 4; + for j in 0..4 { + core::mem::swap(&mut p[j], &mut q[j]); + } + } + + /* Last Round */ + + q[0] = self.fkey[k] + ^ (FBSUB[(p[0] & 0xff) as usize] as u32) + ^ rotl8((FBSUB[((p[1] >> 8) & 0xff) as usize]) as u32) + ^ rotl16((FBSUB[((p[2] >> 16) & 0xff) as usize]) as u32) + ^ rotl24((FBSUB[((p[3] >> 24) & 0xff) as usize]) as u32); + + q[1] = self.fkey[k + 1] + ^ (FBSUB[(p[1] & 0xff) as usize] as u32) + ^ rotl8((FBSUB[((p[2] >> 8) & 0xff) as usize]) as u32) + ^ rotl16((FBSUB[((p[3] >> 16) & 0xff) as usize]) as u32) + ^ rotl24((FBSUB[((p[0] >> 24) & 0xff) as usize]) as u32); + + q[2] = self.fkey[k + 2] + ^ (FBSUB[(p[2] & 0xff) as usize] as u32) + ^ rotl8((FBSUB[((p[3] >> 8) & 0xff) as usize]) as u32) + ^ rotl16((FBSUB[((p[0] >> 16) & 0xff) as usize]) as u32) + ^ rotl24((FBSUB[((p[1] >> 24) & 0xff) as usize]) as u32); + + q[3] = self.fkey[k + 3] + ^ (FBSUB[(p[3] & 0xff) as usize] as u32) + ^ rotl8((FBSUB[((p[0] >> 8) & 0xff) as usize]) as u32) + ^ rotl16((FBSUB[((p[1] >> 16) & 0xff) as usize]) as u32) + ^ rotl24((FBSUB[((p[2] >> 24) & 0xff) as usize]) as u32); + + j = 0; + for i in 0..4 { + b = unpack(q[i]); + for k in 0..4 { + buff[j + k] = b[k] + } + j += 4; + } + } + + /* Decrypt a single block */ + pub fn ecb_decrypt(&mut self, buff: &mut [u8; 16]) { + let mut b: [u8; 4] = [0; 4]; + let mut p: [u32; 4] = [0; 4]; + let mut q: [u32; 4] = [0; 4]; + + let mut j = 0; + for i in 0..4 { + for k in 0..4 { + b[k] = buff[j + k] + } + p[i] = pack(b); + p[i] ^= self.rkey[i]; + j += 4; + } + + let mut k = 4; + + /* State alternates between p and q */ + for _ in 1..self.nr { + q[0] = self.rkey[k] + ^ RTABLE[(p[0] & 0xff) as usize] + ^ rotl8(RTABLE[((p[3] >> 8) & 0xff) as usize]) + ^ rotl16(RTABLE[((p[2] >> 16) & 0xff) as usize]) + ^ rotl24(RTABLE[((p[1] >> 24) & 0xff) as usize]); + + q[1] = self.rkey[k + 1] + ^ RTABLE[(p[1] & 0xff) as usize] + ^ rotl8(RTABLE[((p[0] >> 8) & 0xff) as usize]) + ^ rotl16(RTABLE[((p[3] >> 16) & 0xff) as usize]) + ^ rotl24(RTABLE[((p[2] >> 24) & 0xff) as usize]); + + q[2] = self.rkey[k + 2] + ^ RTABLE[(p[2] & 0xff) as usize] + ^ rotl8(RTABLE[((p[1] >> 8) & 0xff) as usize]) + ^ rotl16(RTABLE[((p[0] >> 16) & 0xff) as usize]) + ^ rotl24(RTABLE[((p[3] >> 24) & 0xff) as usize]); + + q[3] = self.rkey[k + 3] + ^ RTABLE[(p[3] & 0xff) as usize] + ^ rotl8(RTABLE[((p[2] >> 8) & 0xff) as usize]) + ^ rotl16(RTABLE[((p[1] >> 16) & 0xff) as usize]) + ^ rotl24(RTABLE[((p[0] >> 24) & 0xff) as usize]); + + k += 4; + for j in 0..4 { + core::mem::swap(&mut p[j], &mut q[j]); + } + } + + /* Last Round */ + + q[0] = self.rkey[k] + ^ (RBSUB[(p[0] & 0xff) as usize] as u32) + ^ rotl8((RBSUB[((p[3] >> 8) & 0xff) as usize]) as u32) + ^ rotl16((RBSUB[((p[2] >> 16) & 0xff) as usize]) as u32) + ^ rotl24((RBSUB[((p[1] >> 24) & 0xff) as usize]) as u32); + + q[1] = self.rkey[k + 1] + ^ (RBSUB[(p[1] & 0xff) as usize] as u32) + ^ rotl8((RBSUB[((p[0] >> 8) & 0xff) as usize]) as u32) + ^ rotl16((RBSUB[((p[3] >> 16) & 0xff) as usize]) as u32) + ^ rotl24((RBSUB[((p[2] >> 24) & 0xff) as usize]) as u32); + + q[2] = self.rkey[k + 2] + ^ (RBSUB[(p[2] & 0xff) as usize] as u32) + ^ rotl8((RBSUB[((p[1] >> 8) & 0xff) as usize]) as u32) + ^ rotl16((RBSUB[((p[0] >> 16) & 0xff) as usize]) as u32) + ^ rotl24((RBSUB[((p[3] >> 24) & 0xff) as usize]) as u32); + + q[3] = self.rkey[k + 3] + ^ (RBSUB[((p[3]) & 0xff) as usize] as u32) + ^ rotl8((RBSUB[((p[2] >> 8) & 0xff) as usize]) as u32) + ^ rotl16((RBSUB[((p[1] >> 16) & 0xff) as usize]) as u32) + ^ rotl24((RBSUB[((p[0] >> 24) & 0xff) as usize]) as u32); + + j = 0; + for i in 0..4 { + b = unpack(q[i]); + for k in 0..4 { + buff[j + k] = b[k] + } + j += 4; + } + } + + /* Encrypt using selected mode of operation */ + pub fn encrypt(&mut self, buff: &mut [u8; 16]) -> u32 { + let mut st: [u8; 16] = [0; 16]; + + // Supported Modes of Operation + + let mut fell_off: u32 = 0; + + match self.mode { + ECB => { + self.ecb_encrypt(buff); + 0 + } + CBC => { + for j in 0..16 { + buff[j] ^= self.f[j] + } + self.ecb_encrypt(buff); + for j in 0..16 { + self.f[j] = buff[j] + } + 0 + } + + CFB1 | CFB2 | CFB4 => { + let bytes = self.mode - CFB1 + 1; + for j in 0..bytes { + fell_off = (fell_off << 8) | (self.f[j] as u32) + } + for j in 0..16 { + st[j] = self.f[j] + } + for j in bytes..16 { + self.f[j - bytes] = self.f[j] + } + self.ecb_encrypt(&mut st); + for j in 0..bytes { + buff[j] ^= st[j]; + self.f[16 - bytes + j] = buff[j]; + } + fell_off + } + + OFB1 | OFB2 | OFB4 | OFB8 | OFB16 => { + let bytes = self.mode - OFB1 + 1; + for j in 0..16 { + st[j] = self.f[j] + } + self.ecb_encrypt(&mut st); + for j in 0..bytes { + buff[j] ^= st[j] + } + for j in 0..16 { + self.f[j] = st[j] + } + + //self.ecb_encrypt(&mut (self.f)); + //for j in 0..bytes {buff[j]^=self.f[j]} + 0 + } + + CTR1 | CTR2 | CTR4 | CTR8 | CTR16 => { + let bytes = self.mode - CTR1 + 1; + for j in 0..16 { + st[j] = self.f[j] + } + self.ecb_encrypt(&mut st); + for j in 0..bytes { + buff[j] ^= st[j] + } + increment(&mut (self.f)); + 0 + } + + _ => 0, + } + } + + /* Decrypt using selected mode of operation */ + pub fn decrypt(&mut self, buff: &mut [u8; 16]) -> u32 { + let mut st: [u8; 16] = [0; 16]; + + // Supported Modes of Operation + + let mut fell_off: u32 = 0; + + match self.mode { + ECB => { + self.ecb_decrypt(buff); + 0 + } + CBC => { + for j in 0..16 { + st[j] = self.f[j]; + self.f[j] = buff[j]; + } + self.ecb_decrypt(buff); + for j in 0..16 { + buff[j] ^= st[j]; + st[j] = 0; + } + 0 + } + CFB1 | CFB2 | CFB4 => { + let bytes = self.mode - CFB1 + 1; + for j in 0..bytes { + fell_off = (fell_off << 8) | (self.f[j] as u32) + } + for j in 0..16 { + st[j] = self.f[j] + } + for j in bytes..16 { + self.f[j - bytes] = self.f[j] + } + self.ecb_encrypt(&mut st); + for j in 0..bytes { + self.f[16 - bytes + j] = buff[j]; + buff[j] ^= st[j]; + } + fell_off + } + OFB1 | OFB2 | OFB4 | OFB8 | OFB16 => { + let bytes = self.mode - OFB1 + 1; + for j in 0..16 { + st[j] = self.f[j] + } + self.ecb_encrypt(&mut st); + for j in 0..bytes { + buff[j] ^= st[j] + } + for j in 0..16 { + self.f[j] = st[j] + } + // self.ecb_encrypt(A.f[:]); + // for j in 0..bytes {buff[j]^=self.f[j]} + 0 + } + + CTR1 | CTR2 | CTR4 | CTR8 | CTR16 => { + let bytes = self.mode - CTR1 + 1; + for j in 0..16 { + st[j] = self.f[j] + } + self.ecb_encrypt(&mut st); + for j in 0..bytes { + buff[j] ^= st[j] + } + increment(&mut (self.f)); + 0 + } + + _ => 0, + } + } + + /* Clean up and delete left-overs */ + pub fn end(&mut self) { + // clean up + for i in 0..4 * (self.nr + 1) { + self.fkey[i] = 0; + self.rkey[i] = 0 + } + for i in 0..16 { + self.f[i] = 0 + } + } +} + +/* AES encryption/decryption. Encrypt byte array m using key k and returns ciphertext c */ +pub fn cbc_iv0_encrypt(k: &[u8], m: &[u8], c: &mut [u8]) -> usize { + /* AES CBC encryption, with Null IV and key K */ + /* Input is from an octet string m, output is to an octet string c */ + /* Input is padded as necessary to make up a full final block */ + let mut a = AES::new(); + let mut fin = false; + + let mut buff: [u8; 16] = [0; 16]; + + a.init(CBC, k.len(), k, None); + + let mut ipt = 0; + let mut opt = 0; + let mut i; + loop { + i = 0; + while i < 16 { + if ipt < m.len() { + buff[i] = m[ipt]; + i += 1; + ipt += 1; + } else { + fin = true; + break; + } + } + if fin { + break; + } + a.encrypt(&mut buff); + for j in 0..16 { + if opt < c.len() { + c[opt] = buff[j]; + opt += 1; + } + } + } + + /* last block, filled up to i-th index */ + + let padlen = 16 - i; + for j in i..16 { + buff[j] = padlen as u8 + } + + a.encrypt(&mut buff); + + for j in 0..16 { + if opt < c.len() { + c[opt] = buff[j]; + opt += 1; + } + } + a.end(); + opt +} + +/* returns plaintext if all consistent, else returns null string */ +pub fn cbc_iv0_decrypt(k: &[u8], c: &[u8], m: &mut [u8]) -> usize { + /* padding is removed */ + let mut a = AES::new(); + let mut fin = false; + + let mut buff: [u8; 16] = [0; 16]; + + a.init(CBC, k.len(), k, None); + + let mut ipt = 0; + let mut opt = 0; + let mut i; + + if c.is_empty() { + return 0; + } + let mut ch = c[ipt]; + ipt += 1; + + loop { + i = 0; + while i < 16 { + buff[i] = ch; + if ipt >= c.len() { + fin = true; + break; + } else { + ch = c[ipt]; + ipt += 1 + } + i += 1; + } + a.decrypt(&mut buff); + if fin { + break; + } + for j in 0..16 { + if opt < m.len() { + m[opt] = buff[j]; + opt += 1; + } + } + } + + a.end(); + let mut bad = false; + let padlen = buff[15] as usize; + if i != 15 || padlen < 1 || padlen > 16 { + bad = true + } + if padlen >= 2 && padlen <= 16 { + for j in 16 - padlen..16 { + if buff[j] != padlen as u8 { + bad = true + } + } + } + + if !bad { + for i in 0..16 - padlen { + if opt < m.len() { + m[opt] = buff[i]; + opt += 1; + } + } + } + + if bad { + 0 + } else { + opt + } +} + +/* +fn main() +{ + let mut key:[u8;32]=[0;32]; + let mut block:[u8;16]=[0;16]; + let mut iv: [u8;16] = [0;16]; + + for i in 0..32 {key[i]=0} + key[0]=1; + for i in 0..16 {iv[i]=i as u8} + for i in 0..16 {block[i]=i as u8} + + let mut aes=AES::new(); + aes.init(CTR16,32,&key,Some(iv)); + + println!("Plain= "); + for i in 0..16 {print!("{:02x} ",block[i])} + println!(""); + + aes.encrypt(&mut block); + + println!("Encrypt= "); + for i in 0..16 {print!("{:02x} ",block[i])} + println!(""); + + aes.reset(CTR16,Some(iv)); + aes.decrypt(&mut block); + + println!("Decrypt= "); + for i in 0..16 {print!("{:02x} ",block[i])} + println!(""); + + aes.end(); +} +*/ diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/arch.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/arch.rs new file mode 100644 index 000000000000..dce3c15fb6fd --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/arch.rs @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pub type Chunk = i64; +pub type DChunk = i128; +pub const CHUNK: usize = 64; diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/big.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/big.rs new file mode 100644 index 000000000000..3105c65a9d1d --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/big.rs @@ -0,0 +1,1062 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::arch; +use crate::arch::Chunk; + +use crate::arch::DChunk; + +use crate::rand::RAND; +use crate::bn254::dbig::DBIG; + +pub const MODBYTES: usize = 32; +pub const BASEBITS: usize = 56; + +pub const NLEN: usize = 1 + ((8 * MODBYTES - 1) / BASEBITS); +pub const DNLEN: usize = 2 * NLEN; +pub const BMASK: Chunk = (1 << BASEBITS) - 1; +pub const HBITS: usize = BASEBITS / 2; +pub const HMASK: Chunk = (1 << HBITS) - 1; +pub const NEXCESS: isize = 1 << ((arch::CHUNK) - BASEBITS - 1); +pub const BIGBITS: usize = MODBYTES * 8; + +#[derive(Copy)] +pub struct BIG { + pub w: [Chunk; NLEN], +} + +impl Clone for BIG { + fn clone(&self) -> BIG { + *self + } +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for BIG { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for BIG { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +impl BIG { + pub const fn new() -> BIG { + BIG { w: [0; NLEN] } + } + + pub fn new_int(x: isize) -> BIG { + let mut s = BIG::new(); + s.w[0] = x as Chunk; + s + } + + pub fn new_ints(a: &[Chunk]) -> BIG { + let mut s = BIG::new(); + for i in 0..NLEN { + s.w[i] = a[i] + } + s + } + + pub fn new_copy(y: &BIG) -> BIG { + let mut s = BIG::new(); + for i in 0..NLEN { + s.w[i] = y.w[i] + } + s + } + + pub fn new_big(y: &BIG) -> BIG { + let mut s = BIG::new(); + for i in 0..NLEN { + s.w[i] = y.w[i] + } + s + } + + pub fn new_dcopy(y: &DBIG) -> BIG { + let mut s = BIG::new(); + for i in 0..NLEN { + s.w[i] = y.w[i] + } + s + } + + pub fn get(&self, i: usize) -> Chunk { + self.w[i] + } + + pub fn set(&mut self, i: usize, x: Chunk) { + self.w[i] = x; + } + + pub fn xortop(&mut self, x: Chunk) { + self.w[NLEN - 1] ^= x; + } + + pub fn ortop(&mut self, x: Chunk) { + self.w[NLEN - 1] |= x; + } + + /* test for zero */ + pub fn iszilch(&self) -> bool { + let mut d = 0 as Chunk; + for i in 0..NLEN { + d |= self.w[i]; + } + (1 & ((d-1)>>BASEBITS)) != 0 + } + + /* set to zero */ + pub fn zero(&mut self) { + for i in 0..NLEN { + self.w[i] = 0 + } + } + + /* Test for equal to one */ + pub fn isunity(&self) -> bool { + let mut d = 0 as Chunk; + for i in 1..NLEN { + d |= self.w[i]; + } + (1 & ((d-1)>>BASEBITS) & (((self.w[0]^1)-1)>>BASEBITS)) != 0 + } + + /* set to one */ + pub fn one(&mut self) { + self.w[0] = 1; + for i in 1..NLEN { + self.w[i] = 0; + } + } + + /* Copy from another BIG */ + pub fn copy(&mut self, x: &BIG) { + for i in 0..NLEN { + self.w[i] = x.w[i] + } + } + + pub fn dcopy(&mut self, x: &DBIG) { + for i in 0..NLEN { + self.w[i] = x.w[i] + } + } + + /* Get top and bottom half of =x*y+c+r */ + pub fn muladd(a: Chunk, b: Chunk, c: Chunk, r: Chunk) -> (Chunk, Chunk) { + let prod: DChunk = (a as DChunk) * (b as DChunk) + (c as DChunk) + (r as DChunk); + let bot = (prod & (BMASK as DChunk)) as Chunk; + let top = (prod >> BASEBITS) as Chunk; + (top, bot) + } + + /* normalise BIG - force all digits < 2^BASEBITS */ + pub fn norm(&mut self) -> Chunk { + let mut carry = self.w[0]>>BASEBITS; + self.w[0] &= BMASK; + for i in 1..NLEN - 1 { + let d = self.w[i] + carry; + self.w[i] = d & BMASK; + carry = d >> BASEBITS; + } + self.w[NLEN - 1] += carry; + (self.w[NLEN - 1] >> ((8 * MODBYTES) % BASEBITS)) as Chunk + } + + /* Conditional swap of two bigs depending on d using XOR - no branches */ + pub fn cswap(&mut self, b: &mut BIG, d: isize) -> Chunk { + let c = -d as Chunk; + let mut w=0 as Chunk; + let r=self.w[0]^b.w[1]; + let mut ra=r.wrapping_add(r); ra >>= 1; + for i in 0..NLEN { + let mut t = c & (self.w[i] ^ b.w[i]); + t^=r; + let mut e=self.w[i]^t; w^=e; + self.w[i]=e^ra; + e=b.w[i]^t; w^=e; + b.w[i]=e^ra; + } + return w; + } + + pub fn cmove(&mut self, g: &BIG, d: isize) -> Chunk { + let b = -d as Chunk; + let mut w=0 as Chunk; + let r=self.w[0]^g.w[1]; + let mut ra=r.wrapping_add(r); ra >>= 1; + for i in 0..NLEN { + let mut t = b & (self.w[i] ^ g.w[i]); + t^=r; + let e=self.w[i]^t; w^=e; + self.w[i]=e^ra; + } + return w; + } + + /* Shift right by less than a word */ + pub fn fshr(&mut self, k: usize) -> isize { + let n = k; + let w = self.w[0] & ((1 << n) - 1); /* shifted out part */ + for i in 0..NLEN - 1 { + self.w[i] = (self.w[i] >> k) | ((self.w[i + 1] << (BASEBITS - n)) & BMASK); + } + self.w[NLEN - 1] >>= k; + w as isize + } + + /* general shift right */ + pub fn shr(&mut self, k: usize) { + let n = k % BASEBITS; + let m = k / BASEBITS; + for i in 0..NLEN - m - 1 { + self.w[i] = (self.w[m + i] >> n) | ((self.w[m + i + 1] << (BASEBITS - n)) & BMASK) + } + self.w[NLEN - m - 1] = self.w[NLEN - 1] >> n; + for i in NLEN - m..NLEN { + self.w[i] = 0 + } + } + + /* Shift right by less than a word */ + pub fn fshl(&mut self, k: usize) -> isize { + let n = k; + self.w[NLEN - 1] = (self.w[NLEN - 1] << n) | (self.w[NLEN - 2] >> (BASEBITS - n)); + for i in (1..NLEN - 1).rev() { + self.w[i] = ((self.w[i] << k) & BMASK) | (self.w[i - 1] >> (BASEBITS - n)); + } + self.w[0] = (self.w[0] << n) & BMASK; + (self.w[NLEN - 1] >> ((8 * MODBYTES) % BASEBITS)) as isize /* return excess - only used in ff.c */ + } + + /* general shift left */ + pub fn shl(&mut self, k: usize) { + let n = k % BASEBITS; + let m = k / BASEBITS; + + self.w[NLEN - 1] = self.w[NLEN - 1 - m] << n; + if NLEN >= m + 2 { + self.w[NLEN - 1] |= self.w[NLEN - m - 2] >> (BASEBITS - n) + } + for i in (m + 1..NLEN - 1).rev() { + self.w[i] = ((self.w[i - m] << n) & BMASK) | (self.w[i - m - 1] >> (BASEBITS - n)); + } + self.w[m] = (self.w[0] << n) & BMASK; + for i in 0..m { + self.w[i] = 0 + } + } + + /* return number of bits */ + pub fn nbits(&self) -> usize { + let mut k = NLEN - 1; + let mut s = BIG::new_copy(&self); + s.norm(); + while (k as isize) >= 0 && s.w[k] == 0 { + k = k.wrapping_sub(1) + } + if (k as isize) < 0 { + return 0; + } + let mut bts = BASEBITS * k; + let mut c = s.w[k]; + while c != 0 { + c /= 2; + bts += 1; + } + bts + } + + /* Convert to Hex String */ + #[cfg(feature = "std")] + pub fn tostring(&self) -> String { + let mut s = String::new(); + let mut len = self.nbits(); + + if len % 4 == 0 { + len /= 4; + } else { + len /= 4; + len += 1; + } + let mb = (MODBYTES * 2) as usize; + if len < mb { + len = mb + } + + for i in (0..len).rev() { + let mut b = BIG::new_copy(&self); + b.shr(i * 4); + s = s + &format!("{:X}", b.w[0] & 15); + } + s + } + + #[cfg(feature = "std")] + pub fn fromstring(val: String) -> BIG { + let mut res = BIG::new(); + let len = val.len(); + let op = &val[0..1]; + let n = u8::from_str_radix(op, 16).unwrap(); + res.w[0] += n as Chunk; + for i in 1..len { + res.shl(4); + let op = &val[i..i+1]; + let n = u8::from_str_radix(op, 16).unwrap(); + res.w[0] += n as Chunk; + } + res + } + + pub fn add(&mut self, r: &BIG) { + for i in 0..NLEN { + self.w[i] += r.w[i] + } + } + + pub fn or(&mut self, r: &BIG) { + for i in 0..NLEN { + self.w[i] |= r.w[i] + } + } + + pub fn dbl(&mut self) { + for i in 0..NLEN { + self.w[i] += self.w[i] + } + } + + /* return this+x */ + pub fn plus(&self, x: &BIG) -> BIG { + let mut s = BIG::new(); + for i in 0..NLEN { + s.w[i] = self.w[i] + x.w[i]; + } + s.norm(); + s + } + + pub fn inc(&mut self, x: isize) { + self.norm(); + self.w[0] += x as Chunk; + } + + /* return self-x */ + pub fn minus(&self, x: &BIG) -> BIG { + let mut d = BIG::new(); + for i in 0..NLEN { + d.w[i] = self.w[i] - x.w[i]; + } + d + } + + /* self-=x */ + pub fn sub(&mut self, x: &BIG) { + for i in 0..NLEN { + self.w[i] -= x.w[i]; + } + } + + /* reverse subtract this=x-this */ + + pub fn rsub(&mut self, x: &BIG) { + for i in 0..NLEN { + self.w[i] = x.w[i] - self.w[i] + } + } + + /* self-=x, where x is int */ + pub fn dec(&mut self, x: isize) { + self.norm(); + self.w[0] -= x as Chunk; + } + + /* self*=x, where x is small int BIG { + let mut m = BIG::new(); + for i in 0..(MODBYTES as usize) { + m.fshl(8); + m.w[0] += b[i + n] as Chunk; + } + m + } + + pub fn tobytes(&self, b: &mut [u8]) { + self.tobytearray(b, 0) + } + + pub fn frombytes(b: &[u8]) -> BIG { + BIG::frombytearray(b, 0) + } + + /* self*=x, where x is >NEXCESS */ + pub fn pmul(&mut self, c: isize) -> Chunk { + let mut carry = 0 as Chunk; + for i in 0..NLEN { + let ak = self.w[i]; + let tuple = BIG::muladd(ak, c as Chunk, carry, 0 as Chunk); + carry = tuple.0; + self.w[i] = tuple.1; + } + carry + } + + /* self*=c and catch overflow in DBIG */ + pub fn pxmul(&mut self, c: isize) -> DBIG { + let mut m = DBIG::new(); + let mut carry = 0 as Chunk; + for j in 0..NLEN { + let tuple = BIG::muladd(self.w[j], c as Chunk, carry, m.w[j]); + carry = tuple.0; + m.w[j] = tuple.1; + } + m.w[NLEN] = carry; + m + } + + /* divide by 3 */ + pub fn div3(&mut self) -> Chunk { + let mut carry = 0 as Chunk; + self.norm(); + let base = 1 << BASEBITS; + for i in (0..NLEN).rev() { + let ak = carry * base + self.w[i]; + self.w[i] = ak / 3; + carry = ak % 3; + } + carry + } + + /* return a*b where result fits in a BIG */ + pub fn smul(a: &BIG, b: &BIG) -> BIG { + let mut c = BIG::new(); + for i in 0..NLEN { + let mut carry = 0 as Chunk; + for j in 0..NLEN { + if i + j < NLEN { + let tuple = BIG::muladd(a.w[i], b.w[j], carry, c.w[i + j]); + carry = tuple.0; + c.w[i + j] = tuple.1; + } + } + } + c + } + + /* Compare a and b, return 0 if a==b, -1 if ab. Inputs must be normalised */ + pub fn comp(a: &BIG, b: &BIG) -> isize { + let mut gt = 0 as Chunk; + let mut eq = 1 as Chunk; + for i in (0..NLEN).rev() { + gt |= ((b.w[i]-a.w[i]) >> BASEBITS) & eq; + eq &= ((b.w[i]^a.w[i])-1) >> BASEBITS; + } + (gt+gt+eq-1) as isize + } + + /* set x = x mod 2^m */ + pub fn mod2m(&mut self, m: usize) { + let wd = m / BASEBITS; + let bt = m % BASEBITS; + let msk = (1 << bt) - 1; + self.w[wd] &= msk; + for i in wd + 1..NLEN { + self.w[i] = 0 + } + } + + /* Arazi and Qi inversion mod 256 */ + pub fn invmod256(a: isize) -> isize { + let mut t1: isize = 0; + let mut c = (a >> 1) & 1; + t1 += c; + t1 &= 1; + t1 = 2 - t1; + t1 <<= 1; + let mut u = t1 + 1; + + // i=2 + let mut b = a & 3; + t1 = u * b; + t1 >>= 2; + c = (a >> 2) & 3; + let mut t2 = (u * c) & 3; + t1 += t2; + t1 *= u; + t1 &= 3; + t1 = 4 - t1; + t1 <<= 2; + u += t1; + + // i=4 + b = a & 15; + t1 = u * b; + t1 >>= 4; + c = (a >> 4) & 15; + t2 = (u * c) & 15; + t1 += t2; + t1 *= u; + t1 &= 15; + t1 = 16 - t1; + t1 <<= 4; + u += t1; + + u + } + + /* return parity */ + pub fn parity(&self) -> isize { + (self.w[0] % 2) as isize + } + + /* return n-th bit */ + pub fn bit(&self, n: usize) -> isize { + return ((self.w[n / (BASEBITS as usize)] & (1 << (n % BASEBITS))) >> (n%BASEBITS)) as isize; + + + // if (self.w[n / (BASEBITS as usize)] & (1 << (n % BASEBITS))) > 0 { +// 1 +// } else { +// 0 +// } + } + + /* return n last bits */ + pub fn lastbits(&mut self, n: usize) -> isize { + let msk = ((1 << n) - 1) as Chunk; + self.norm(); + (self.w[0] & msk) as isize + } + + /* a=1/a mod 2^256. This is very fast! */ + pub fn invmod2m(&mut self) { + let mut u = BIG::new(); + let mut b = BIG::new(); + let mut c = BIG::new(); + + u.inc(BIG::invmod256(self.lastbits(8))); + + let mut i = 8; + while i < BIGBITS { + u.norm(); + b.copy(self); + b.mod2m(i); + let mut t1 = BIG::smul(&u, &b); + t1.shr(i); + c.copy(self); + c.shr(i); + c.mod2m(i); + + let mut t2 = BIG::smul(&u, &c); + t2.mod2m(i); + t1.add(&t2); + t1.norm(); + b = BIG::smul(&t1, &u); + t1.copy(&b); + t1.mod2m(i); + + t2.one(); + t2.shl(i); + t1.rsub(&t2); + t1.norm(); + t1.shl(i); + u.add(&t1); + i <<= 1; + } + u.mod2m(BIGBITS); + self.copy(&u); + self.norm(); + } + +// Set self=self mod m in constant time (if bd is known at compile time) +// bd is Max number of bits in b - Actual number of bits in m + pub fn ctmod(&mut self,m:&BIG,bd:usize) { + let mut k=bd; + let mut r=BIG::new(); + let mut c=BIG::new_copy(m); + self.norm(); + + c.shl(k); + loop { + r.copy(self); + r.sub(&c); + r.norm(); + self.cmove(&r,(1 - ((r.w[NLEN - 1] >> (arch::CHUNK - 1)) & 1)) as isize); + if k==0 {break;} + c.fshr(1); + k -= 1; + } + } + + /* reduce self mod m */ + pub fn rmod(&mut self, m: &BIG) { + let ss=self.nbits() as isize; + let ms=m.nbits() as isize; + let mut k=(ss-ms) as usize; + if ss> (arch::CHUNK - 1)) & 1)) as isize; + a.cmove(&r, d); + r.copy(self); + r.add(&e); + r.norm(); + self.cmove(&r, d); + if k==0 {break;} + k -= 1; + c.fshr(1); + e.fshr(1); + } + } + + /* divide self by m */ + pub fn div(&mut self, m: &BIG) { + let ss=self.nbits() as isize; + let ms=m.nbits() as isize; + let mut k=(ss-ms) as usize; + if ss BIG { + let mut m = BIG::new(); + let mut j = 0; + let mut r: u8 = 0; + /* generate random BIG */ + + for _ in 0..8 * (MODBYTES as usize) { + if j == 0 { + r = rng.getbyte() + } else { + r >>= 1 + } + + let b = (r as Chunk) & 1; + m.shl(1); + m.w[0] += b; + j += 1; + j &= 7; + } + m + } + + /* Create random BIG in portable way, one bit at a time */ + pub fn randomnum(q: &BIG, rng: &mut RAND) -> BIG { + let mut d = DBIG::new(); + let mut j = 0; + let mut r: u8 = 0; + let t = BIG::new_copy(q); + for _ in 0..2 * t.nbits() { + if j == 0 { + r = rng.getbyte(); + } else { + r >>= 1 + } + + let b = (r as Chunk) & 1; + d.shl(1); + d.w[0] += b; + j += 1; + j &= 7; + } + d.dmod(q) + } + +/* create randum BIG less than r and less than trunc bits */ + pub fn randtrunc(q: &BIG, trunc: usize, rng: &mut RAND) -> BIG { + let mut m=BIG::randomnum(q,rng); + if q.nbits() > trunc { + m.mod2m(trunc); + } + m + } + + /* Jacobi Symbol (this/p). Returns 0, 1 or -1 */ + pub fn jacobi(&mut self, p: &BIG) -> isize { + let mut m: usize = 0; + let mut t = BIG::new(); + let mut x = BIG::new(); + let mut n = BIG::new(); + let zilch = BIG::new(); + let one = BIG::new_int(1); + if p.parity() == 0 || BIG::comp(self, &zilch) == 0 || BIG::comp(p, &one) <= 0 { + return 0; + } + self.norm(); + + x.copy(self); + n.copy(p); + x.rmod(p); + + while BIG::comp(&n, &one) > 0 { + if BIG::comp(&x, &zilch) == 0 { + return 0; + } + let n8 = n.lastbits(3) as usize; + let mut k = 0; + while x.parity() == 0 { + k += 1; + x.shr(1); + } + if k % 2 == 1 { + m += (n8 * n8 - 1) / 8 + } + m += (n8 - 1) * ((x.lastbits(2) as usize) - 1) / 4; + t.copy(&n); + t.rmod(&x); + n.copy(&x); + x.copy(&t); + m %= 2; + } + if m == 0 { + 1 + } else { + -1 + } + } + +// Set self=1/self mod p. Binary method +// NOTE: This function is NOT side-channel safe +// If a is a secret then ALWAYS calculate 1/a = m*(1/am) mod p +// where m is a random masking value + pub fn invmodp(&mut self, p: &BIG) { + self.rmod(p); + if self.iszilch() {return;} + let mut u = BIG::new_copy(self); + let mut v = BIG::new_copy(p); + let mut x1 = BIG::new_int(1); + let mut x2 = BIG::new(); + let mut t = BIG::new(); + let one = BIG::new_int(1); + + while (BIG::comp(&u, &one) != 0) && (BIG::comp(&v, &one) != 0) { + while u.parity() == 0 { + u.fshr(1); + t.copy(&x1); + t.add(p); + x1.cmove(&t,x1.parity()); + x1.norm(); + x1.fshr(1); + } + while v.parity() == 0 { + v.fshr(1); + t.copy(&x2); + t.add(p); + x2.cmove(&t,x2.parity()); + x2.norm(); + x2.fshr(1); + } + if BIG::comp(&u, &v) >= 0 { + u.sub(&v); + u.norm(); + t.copy(&x1); + t.add(p); + x1.cmove(&t,(BIG::comp(&x1,&x2)>>1)&1); + x1.sub(&x2); + x1.norm(); + } else { + v.sub(&u); + v.norm(); + t.copy(&x2); + t.add(p); + x2.cmove(&t,(BIG::comp(&x2,&x1)>>1)&1); + x2.sub(&x1); + x2.norm(); + } + } + self.copy(&x1); + self.cmove(&x2,BIG::comp(&u,&one)&1); + } + + /* return a*b as DBIG - Simple Karatsuba */ + pub fn mul(a: &BIG, b: &BIG) -> DBIG { + let mut c = DBIG::new(); + let rm = BMASK as DChunk; + let rb = BASEBITS; + + let mut d: [DChunk; DNLEN] = [0; DNLEN]; + for i in 0..NLEN { + d[i] = (a.w[i] as DChunk) * (b.w[i] as DChunk); + } + let mut s = d[0]; + let mut t = s; + c.w[0] = (t & rm) as Chunk; + t >>= rb; + for k in 1..NLEN { + s += d[k]; + t += s; + for i in 1 + k / 2..k + 1 { + t += ((a.w[i] - a.w[k - i]) as DChunk) * ((b.w[k - i] - b.w[i]) as DChunk) + } + c.w[k] = (t & rm) as Chunk; + t >>= rb; + } + for k in NLEN..2 * NLEN - 1 { + s -= d[k - NLEN]; + t += s; + let mut i = 1 + k / 2; + while i < NLEN { + t += ((a.w[i] - a.w[k - i]) as DChunk) * ((b.w[k - i] - b.w[i]) as DChunk); + i += 1; + } + + c.w[k] = (t & rm) as Chunk; + t >>= rb; + } + c.w[2 * NLEN - 1] = t as Chunk; + c + } + + /* return a^2 as DBIG */ + pub fn sqr(a: &BIG) -> DBIG { + let mut c = DBIG::new(); + let rm = BMASK as DChunk; + let rb = BASEBITS; + + let mut t = (a.w[0] as DChunk) * (a.w[0] as DChunk); + c.w[0] = (t & rm) as Chunk; + let mut co = t >> rb; + + let mut j = 1; + while j < NLEN - 1 { + t = (a.w[j] as DChunk) * (a.w[0] as DChunk); + for i in 1..(j + 1) / 2 { + t += (a.w[j - i] as DChunk) * (a.w[i] as DChunk); + } + t += t; + t += co; + c.w[j] = (t & rm) as Chunk; + co = t >> rb; + j += 1; + t = (a.w[j] as DChunk) * (a.w[0] as DChunk); + for i in 1..(j + 1) / 2 { + t += (a.w[j - i] as DChunk) * (a.w[i] as DChunk); + } + t += t; + t += co; + t += (a.w[j / 2] as DChunk) * (a.w[j / 2] as DChunk); + c.w[j] = (t & rm) as Chunk; + co = t >> rb; + j += 1; + } + + j = NLEN + (NLEN % 2) - 1; + while j < DNLEN - 3 { + t = (a.w[NLEN - 1] as DChunk) * (a.w[j + 1 - NLEN] as DChunk); + for i in j + 2 - NLEN..(j + 1) / 2 { + t += (a.w[j - i] as DChunk) * (a.w[i] as DChunk); + } + t += t; + t += co; + c.w[j] = (t & rm) as Chunk; + co = t >> rb; + j += 1; + t = (a.w[NLEN - 1] as DChunk) * (a.w[j + 1 - NLEN] as DChunk); + for i in j + 2 - NLEN..(j + 1) / 2 { + t += (a.w[j - i] as DChunk) * (a.w[i] as DChunk); + } + t += t; + t += co; + t += (a.w[j / 2] as DChunk) * (a.w[j / 2] as DChunk); + c.w[j] = (t & rm) as Chunk; + co = t >> rb; + j += 1; + } + + t = (a.w[NLEN - 2] as DChunk) * (a.w[NLEN - 1] as DChunk); + t += t; + t += co; + c.w[DNLEN - 3] = (t & rm) as Chunk; + co = t >> rb; + + t = (a.w[NLEN - 1] as DChunk) * (a.w[NLEN - 1] as DChunk) + co; + c.w[DNLEN - 2] = (t & rm) as Chunk; + co = t >> rb; + c.w[DNLEN - 1] = co as Chunk; + + c + } + + /* Montgomery reduction */ + pub fn monty(md: &BIG, mc: Chunk, d: &mut DBIG) -> BIG { + let mut b = BIG::new(); + let rm = BMASK as DChunk; + let rb = BASEBITS; + + let mut dd: [DChunk; NLEN] = [0; NLEN]; + let mut v: [Chunk; NLEN] = [0; NLEN]; + + b.zero(); + + let mut t = d.w[0] as DChunk; + v[0] = (((t & rm) as Chunk).wrapping_mul(mc)) & BMASK; + t += (v[0] as DChunk) * (md.w[0] as DChunk); + t = (d.w[1] as DChunk) + (t >> rb); + let mut s: DChunk = 0; + for k in 1..NLEN { + t = t + s + (v[0] as DChunk) * (md.w[k] as DChunk); + let mut i = 1 + k / 2; + while i < k { + t += ((v[k - i] - v[i]) as DChunk) * ((md.w[i] - md.w[k - i]) as DChunk); + i += 1; + } + v[k] = (((t & rm) as Chunk).wrapping_mul(mc)) & BMASK; + t += (v[k] as DChunk) * (md.w[0] as DChunk); + t = (d.w[k + 1] as DChunk) + (t >> rb); + dd[k] = (v[k] as DChunk) * (md.w[k] as DChunk); + s += dd[k]; + } + + for k in NLEN..2 * NLEN - 1 { + t += s; + let mut i = 1 + k / 2; + while i < NLEN { + t += ((v[k - i] - v[i]) as DChunk) * ((md.w[i] - md.w[k - i]) as DChunk); + i += 1; + } + b.w[k - NLEN] = (t & rm) as Chunk; + t = (d.w[k + 1] as DChunk) + (t >> rb); + s -= dd[k + 1 - NLEN]; + } + b.w[NLEN - 1] = (t & rm) as Chunk; + b + } + + /* Fast combined shift, subtract and norm. Return sign of result */ + pub fn ssn(r: &mut BIG, a: &BIG, m: &mut BIG) -> isize { + let n = NLEN - 1; + m.w[0] = (m.w[0] >> 1) | ((m.w[1] << (BASEBITS - 1)) & BMASK); + r.w[0] = a.w[0] - m.w[0]; + let mut carry = r.w[0] >> BASEBITS; + r.w[0] &= BMASK; + for i in 1..n { + m.w[i] = (m.w[i] >> 1) | ((m.w[i + 1] << (BASEBITS - 1)) & BMASK); + r.w[i] = a.w[i] - m.w[i] + carry; + carry = r.w[i] >> BASEBITS; + r.w[i] &= BMASK; + } + m.w[n] >>= 1; + r.w[n] = a.w[n] - m.w[n] + carry; + ((r.w[n] >> (arch::CHUNK - 1)) & 1) as isize + } + + /* return a*b mod m */ + pub fn modmul(a1: &BIG, b1: &BIG, m: &BIG) -> BIG { + let mut a = BIG::new_copy(a1); + let mut b = BIG::new_copy(b1); + a.rmod(m); + b.rmod(m); + let mut d = BIG::mul(&a, &b); + d.ctdmod(m,m.nbits()) + } + + /* return a^2 mod m */ + pub fn modsqr(a1: &BIG, m: &BIG) -> BIG { + let mut a = BIG::new_copy(a1); + a.rmod(m); + let mut d = BIG::sqr(&a); + d.ctdmod(m,m.nbits()) + } + + /* return -a mod m */ + pub fn modneg(a1: &BIG, m: &BIG) -> BIG { + let mut a = BIG::new_copy(a1); + a.rmod(m); + a.rsub(m); + a.norm(); + a + } + + /* return a+b mod m */ + pub fn modadd(a1: &BIG, b1: &BIG, m: &BIG) -> BIG { + let mut a = BIG::new_copy(a1); + let mut b = BIG::new_copy(b1); + a.rmod(m); + b.rmod(m); + a.add(&b); a.norm(); + a.ctmod(m,1); + a + } + + /* return this^e mod m */ + pub fn powmod(&mut self, e1: &BIG, m: &BIG) -> BIG { + self.norm(); + let mut e = BIG::new_copy(e1); + e.norm(); + let mut a = BIG::new_int(1); + let mut z = BIG::new_copy(&e); + let mut s = BIG::new_copy(self); + loop { + let bt = z.parity(); + z.fshr(1); + if bt == 1 { + a = BIG::modmul(&a, &s, m) + } + if z.iszilch() { + break; + } + s = BIG::modsqr(&s, m); + } + a + } +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/bls.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/bls.rs new file mode 100644 index 000000000000..dddbae94bf2d --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/bls.rs @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::bn254::big; +use crate::bn254::big::BIG; +use crate::bn254::fp::FP; +use crate::bn254::ecp; +use crate::bn254::ecp::ECP; +use crate::bn254::dbig::DBIG; +use crate::bn254::ecp2::ECP2; +//use crate::bn254::fp4::FP4; +use crate::bn254::pair; +use crate::bn254::rom; +use crate::hmac; + +/* Boneh-Lynn-Shacham signature 128-bit API Functions */ + +/* Loosely (for now) following https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-02 */ + +// Minimal-signature-size variant + +pub const BFS: usize = big::MODBYTES as usize; +pub const BGS: usize = big::MODBYTES as usize; +pub const BLS_OK: isize = 0; +pub const BLS_FAIL: isize = -1; + +// NOTE this must be accessed in unsafe mode. +// But it is just written to once at start-up, so actually safe. + +// Best not to use precomp if stack memory limited - i.e. embedded use +// Uncomment to use precomp - Note this example code may no longer work when Rust 2024 appears in version 1.82, and may need restructuring +// static mut G2_TAB: [FP4; ecp::G2_TABLE] = [FP4::new(); ecp::G2_TABLE]; + +fn ceil(a: usize,b: usize) -> usize { + (a-1)/b+1 +} + +/* output u \in F_p */ +fn hash_to_field(hash: usize,hlen: usize ,u: &mut [FP], dst: &[u8],m: &[u8],ctr: usize) { + let q = BIG::new_ints(&rom::MODULUS); + let nbq=q.nbits(); + let el = ceil(nbq+ecp::AESKEY*8,8); + + let mut okm: [u8;256]=[0;256]; + let mut fd: [u8;128]=[0;128]; + + hmac::xmd_expand(hash,hlen,&mut okm,el*ctr,&dst,&m); + for i in 0..ctr { + for j in 0..el { + fd[j]=okm[el*i+j]; + } + u[i]=FP::new_big(&DBIG::frombytes(&fd[0 .. el]).ctdmod(&q,8*el-nbq)); + } +} + +/* hash a message to an ECP point, using SHA2, random oracle method */ +#[allow(non_snake_case)] +pub fn bls_hash_to_point(m: &[u8]) -> ECP { + let dst= "BLS_SIG_BN254G1_XMD:SHA-256_SVDW_RO_NUL_"; + + let mut u: [FP; 2] = [ + FP::new(), + FP::new(), + ]; + hash_to_field(hmac::MC_SHA2,ecp::HASH_TYPE,&mut u,dst.as_bytes(),m,2); + + let mut P=ECP::map2point(&u[0]); + let P1=ECP::map2point(&u[1]); + + P.add(&P1); + P.cfp(); + P.affine(); + P +} + +pub fn init() -> isize { +// Uncomment to use precomp +// let g = ECP2::generator(); +// if g.is_infinity() { +// return BLS_FAIL; +// } +// unsafe { +// pair::precomp(&mut G2_TAB, &g); +// } + BLS_OK +} + +/* generate key pair, private key s, public key w */ +pub fn key_pair_generate(ikm: &[u8], s: &mut [u8], w: &mut [u8]) -> isize { + let r = BIG::new_ints(&rom::CURVE_ORDER); + let nbr=r.nbits(); + let el = ceil(3*ceil(nbr,8),2); + let g = ECP2::generator(); + let mut len: [u8; 2] = [0; 2]; + hmac::inttobytes(el,&mut len); + + let salt="BLS-SIG-KEYGEN-SALT-"; + + let mut prk: [u8;64]=[0;64]; + let mut okm: [u8;128]=[0;128]; + let mut aikm: [u8;65]=[0;65]; + let likm=ikm.len(); + for i in 0..likm { + aikm[i]=ikm[i]; + } + aikm[likm]=0; + + let hlen=ecp::HASH_TYPE; + + hmac::hkdf_extract(hmac::MC_SHA2,hlen,&mut prk,Some(&salt.as_bytes()),&aikm[0 .. likm+1]); + hmac::hkdf_expand(hmac::MC_SHA2,hlen,&mut okm,el,&prk[0 .. hlen],&len); + + let mut dx = DBIG::frombytes(&okm[0 .. el]); + let sc = dx.ctdmod(&r,8*el-nbr); + sc.tobytes(s); +// SkToPk + pair::g2mul(&g, &sc).tobytes(w,true); // true for public key compression + BLS_OK +} + +/* Sign message m using private key s to produce signature sig */ + +pub fn core_sign(sig: &mut [u8], m: &[u8], s: &[u8]) -> isize { + let d = bls_hash_to_point(m); + let sc = BIG::frombytes(&s); + pair::g1mul(&d, &sc).tobytes(sig, true); + BLS_OK +} + +/* Verify signature given message m, the signature sig, and the public key w */ + +pub fn core_verify(sig: &[u8], m: &[u8], w: &[u8]) -> isize { + let hm = bls_hash_to_point(m); + let mut d = ECP::frombytes(&sig); + + if !pair::g1member(&d) { + return BLS_FAIL; + } + d.neg(); + + let pk = ECP2::frombytes(&w); + if !pair::g2member(&pk) { + return BLS_FAIL; + } + +// Uncomment to use precomp + // Use new multi-pairing mechanism + //let mut r = pair::initmp(); + // pair::another(&mut r,&g,&d); + + //unsafe { + // pair::another_pc(&mut r, &G2_TAB, &d); + //} + //pair::another(&mut r, &pk, &hm); + //let mut v = pair::miller(&mut r); + + //.. or else do not use precomp! + let g = ECP2::generator(); + let mut v = pair::ate2(&g, &d, &pk, &hm); + + v = pair::fexp(&v); + if v.isunity() { + return BLS_OK; + } + BLS_FAIL +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/dbig.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/dbig.rs new file mode 100644 index 000000000000..5f2de894baed --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/dbig.rs @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::arch; +use crate::arch::Chunk; +use crate::bn254::big; +use crate::bn254::big::BIG; + +pub struct DBIG { + pub w: [Chunk; big::DNLEN], +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for DBIG { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for DBIG { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +impl DBIG { + pub fn new() -> DBIG { + DBIG { + w: [0; big::DNLEN as usize], + } + } + + pub fn new_copy(y: &DBIG) -> DBIG { + let mut s = DBIG::new(); + for i in 0..big::DNLEN { + s.w[i] = y.w[i] + } + s + } + + pub fn new_scopy(x: &BIG) -> DBIG { + let mut b = DBIG::new(); + for i in 0..big::NLEN { + b.w[i] = x.w[i]; + } + b.w[big::NLEN - 1] = x.get(big::NLEN - 1) & big::BMASK; /* top word normalized */ + b.w[big::NLEN] = x.get(big::NLEN - 1) >> big::BASEBITS; + + for i in big::NLEN + 1..big::DNLEN { + b.w[i] = 0 + } + b + } + + /* split DBIG at position n, return higher half, keep lower half */ + pub fn split(&mut self, n: usize) -> BIG { + let mut t = BIG::new(); + let m = n % big::BASEBITS; + let mut carry = self.w[big::DNLEN - 1] << (big::BASEBITS - m); + + for i in (big::NLEN - 1..big::DNLEN - 1).rev() { + let nw = (self.w[i] >> m) | carry; + carry = (self.w[i] << (big::BASEBITS - m)) & big::BMASK; + t.set(i + 1 - big::NLEN, nw); + } + self.w[big::NLEN - 1] &= ((1 as Chunk) << m) - 1; + t + } + + /* general shift left */ + pub fn shl(&mut self, k: usize) { + let n = k % big::BASEBITS; + let m = k / big::BASEBITS; + self.w[big::DNLEN - 1] = + (self.w[big::DNLEN - 1 - m] << n) | (self.w[big::DNLEN - m - 2] >> (big::BASEBITS - n)); + for i in (m + 1..big::DNLEN - 1).rev() { + self.w[i] = + ((self.w[i - m] << n) & big::BMASK) | (self.w[i - m - 1] >> (big::BASEBITS - n)); + } + + self.w[m] = (self.w[0] << n) & big::BMASK; + for i in 0..m { + self.w[i] = 0 + } + } + + /* general shift right */ + pub fn shr(&mut self, k: usize) { + let n = k % big::BASEBITS; + let m = k / big::BASEBITS; + for i in 0..big::DNLEN - m - 1 { + self.w[i] = + (self.w[m + i] >> n) | ((self.w[m + i + 1] << (big::BASEBITS - n)) & big::BMASK); + } + self.w[big::DNLEN - m - 1] = self.w[big::DNLEN - 1] >> n; + for i in big::DNLEN - m..big::DNLEN { + self.w[i] = 0 + } + } + + /* Copy from another DBIG */ + pub fn copy(&mut self, x: &DBIG) { + for i in 0..big::DNLEN { + self.w[i] = x.w[i]; + } + } + + pub fn ucopy(&mut self, x: &BIG) { + for i in 0..big::NLEN { + self.w[i] = 0; + } + for i in big::NLEN..big::DNLEN { + self.w[i] = x.w[i - big::NLEN]; + } + } + + pub fn cmove(&mut self, g: &DBIG, d: isize) -> Chunk { + let b = -d as Chunk; + let mut w=0 as Chunk; + let r=self.w[0]^g.w[1]; + let mut ra=r.wrapping_add(r); ra >>= 1; + for i in 0..big::DNLEN { + let mut t = b & (self.w[i] ^ g.w[i]); + t^=r; + let e=self.w[i]^t; w^=e; + self.w[i]=e^ra; + } + return w; + } + + /* self+=x */ + pub fn add(&mut self, x: &DBIG) { + for i in 0..big::DNLEN { + self.w[i] += x.w[i]; + } + } + + /* self-=x */ + pub fn sub(&mut self, x: &DBIG) { + for i in 0..big::DNLEN { + self.w[i] -= x.w[i]; + } + } + + /* self=x-self */ + pub fn rsub(&mut self, x: &DBIG) { + for i in 0..big::DNLEN { + self.w[i] = x.w[i] - self.w[i]; + } + } + + /* Compare a and b, return 0 if a==b, -1 if ab. Inputs must be normalised */ + pub fn comp(a: &DBIG, b: &DBIG) -> isize { + let mut gt = 0 as Chunk; + let mut eq = 1 as Chunk; + for i in (0..big::DNLEN).rev() { + gt |= ((b.w[i]-a.w[i]) >> big::BASEBITS) & eq; + eq &= ((b.w[i]^a.w[i])-1) >> big::BASEBITS; + } + (gt+gt+eq-1) as isize + } + + /* convert from byte array to BIG */ + pub fn frombytes(b: &[u8]) -> DBIG { + let mut m = DBIG::new(); + for i in 0..(b.len()) { + m.shl(8); + m.w[0] += b[i] as Chunk; + } + m + } + + /* normalise BIG - force all digits < 2^big::BASEBITS */ + pub fn norm(&mut self) { + let mut carry = self.w[0]>>big::BASEBITS; + self.w[0] &= big::BMASK; + for i in 1..big::DNLEN - 1 { + let d = self.w[i] + carry; + self.w[i] = d & big::BMASK; + carry = d >> big::BASEBITS; + } + self.w[big::DNLEN - 1] += carry + } + +// Set self=self mod m in constant time (if bd is known at compile time) +// bd is Max number of bits in b - Actual number of bits in m + pub fn ctdmod(&mut self, m: &BIG, bd: usize) -> BIG { + let mut k=bd; + self.norm(); + let mut c = DBIG::new_scopy(m); + let mut dr = DBIG::new(); + + c.shl(k); + + loop { + dr.copy(self); + dr.sub(&c); + dr.norm(); + self.cmove(&dr,(1 - ((dr.w[big::DNLEN - 1] >> (arch::CHUNK - 1)) & 1)) as isize); + if k==0 {break;} + c.shr(1); + k -= 1; + } + BIG::new_dcopy(self) + } + + /* reduces self DBIG mod a BIG, and returns the BIG */ + pub fn dmod(&mut self, m: &BIG) -> BIG { + let ss=self.nbits() as isize; + let ms=m.nbits() as isize; + let mut k=(ss-ms) as usize; + if ss BIG { + let mut k=bd; + let mut c = DBIG::new_scopy(m); + let mut a = BIG::new(); + let mut e = BIG::new_int(1); + let mut dr = DBIG::new(); + let mut r = BIG::new(); + self.norm(); + + c.shl(k); + e.shl(k); + + loop { + dr.copy(self); + dr.sub(&c); + dr.norm(); + let d = (1 - ((dr.w[big::DNLEN - 1] >> (arch::CHUNK - 1)) & 1)) as isize; + self.cmove(&dr, d); + r.copy(&a); + r.add(&e); + r.norm(); + a.cmove(&r, d); + if k==0 {break;} + k -= 1; + c.shr(1); + e.shr(1); + } + a + } + + /* return this/c */ + pub fn div(&mut self, m: &BIG) -> BIG { + let ss=self.nbits() as isize; + let ms=m.nbits() as isize; + let mut k=(ss-ms) as usize; + if ss usize { + let mut k = big::DNLEN - 1; + let mut s = DBIG::new_copy(&self); + s.norm(); + while (k as isize) >= 0 && s.w[k] == 0 { + k = k.wrapping_sub(1) + } + if (k as isize) < 0 { + return 0; + } + let mut bts = (big::BASEBITS as usize) * k; + let mut c = s.w[k]; + while c != 0 { + c /= 2; + bts += 1; + } + bts + } + + /* Convert to Hex String */ + #[cfg(feature = "std")] + pub fn tostring(&self) -> String { + let mut s = String::new(); + let mut len = self.nbits(); + + if len % 4 == 0 { + len /= 4; + } else { + len /= 4; + len += 1; + } + + for i in (0..len).rev() { + let mut b = DBIG::new_copy(&self); + b.shr(i * 4); + s = s + &format!("{:X}", b.w[0] & 15); + } + s + } +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecdh.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecdh.rs new file mode 100644 index 000000000000..96ceadf4f311 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecdh.rs @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* ECDH/ECIES/ECDSA API Functions */ + +use crate::bn254::big; +use crate::bn254::big::BIG; +use crate::bn254::ecp; +use crate::bn254::ecp::ECP; +use crate::bn254::rom; + +use crate::aes; +use crate::hmac; +use crate::rand::RAND; + +pub const INVALID_PUBLIC_KEY: isize = -2; +pub const ERROR: isize = -3; +//pub const INVALID: isize = -4; +pub const EFS: usize = big::MODBYTES as usize; +pub const EGS: usize = big::MODBYTES as usize; + +pub fn rfc7748(r: &mut BIG) { + let mut lg=0; + let mut t=BIG::new_int(1); + let mut c=rom::CURVE_COF_I; + while c!=1 { + lg+=1; + c/=2; + } + let n=(8*EGS-lg+1) as usize; + r.mod2m(n); + t.shl(n); + r.add(&t); + c=r.lastbits(lg as usize); + r.dec(c); +} + +#[allow(non_snake_case)] +pub fn in_range(s: &[u8]) -> bool { + let r = BIG::new_ints(&rom::CURVE_ORDER); + let sc = BIG::frombytes(&s); + if sc.iszilch() { + return false; + } + if BIG::comp(&sc, &r) >= 0 { + return false; + } + true +} + +/* Calculate a public/private EC GF(p) key pair w,s where W=s.G mod EC(p), + * where s is the secret key and W is the public key + * and G is fixed generator. + * If RNG is NULL then the private key is provided externally in s + * otherwise it is generated randomly internally */ +#[allow(non_snake_case)] +pub fn key_pair_generate(rng: Option<&mut RAND>, s: &mut [u8], w: &mut [u8]) -> isize { + let res = 0; + let mut sc: BIG; + let G = ECP::generator(); + let r = BIG::new_ints(&rom::CURVE_ORDER); + + if let Some(x) = rng { + if ecp::CURVETYPE != ecp::WEIERSTRASS { + sc = BIG::random(x); // from random bytes + } else { + sc = BIG::randomnum(&r, x); // Removes biases + } + } else { + sc = BIG::frombytes(&s); + } + + if ecp::CURVETYPE != ecp::WEIERSTRASS { + rfc7748(&mut sc); // For Montgomery or Edwards, apply RFC7748 transformation + } + sc.tobytes(s); + let WP = G.clmul(&sc,&r); + WP.tobytes(w, false); // To use point compression on public keys, change to true + + res +} + +/* validate public key */ +#[allow(non_snake_case)] +pub fn public_key_validate(w: &[u8]) -> isize { + let mut WP = ECP::frombytes(w); + let mut res = 0; + + let r = BIG::new_ints(&rom::CURVE_ORDER); + + if WP.is_infinity() { + res = INVALID_PUBLIC_KEY + } + if res == 0 { + let q = BIG::new_ints(&rom::MODULUS); + let nb = q.nbits(); + let mut k = BIG::new(); + k.one(); + k.shl((nb + 4) / 2); + k.add(&q); + k.div(&r); + + while k.parity() == 0 { + k.shr(1); + WP.dbl(); + } + + if !k.isunity() { + WP = WP.mul(&mut k) + } + if WP.is_infinity() { + res = INVALID_PUBLIC_KEY + } + } + res +} + +/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */ +#[allow(non_snake_case)] +pub fn ecpsvdp_dh(s: &[u8], wd: &[u8], z: &mut [u8], typ: isize) -> isize { + let mut res = 0; + + let sc = BIG::frombytes(&s); + + let mut W = ECP::frombytes(&wd); + if W.is_infinity() { + res = ERROR + } + + if res == 0 { + let r = BIG::new_ints(&rom::CURVE_ORDER); + W = W.clmul(&sc,&r); + if W.is_infinity() { + res = ERROR; + } else { + if ecp::CURVETYPE != ecp::MONTGOMERY { + if typ>0 { + if typ==1 { + W.tobytes(z,true); + } else { + W.tobytes(z,false); + } + } else { + W.getx().tobytes(z); + } + return res; + } else { + W.getx().tobytes(z); + } + } + } + res +} + +/* IEEE ECDSA Signature, C and D are signature on F using private key S */ +#[allow(non_snake_case)] +pub fn ecpsp_dsa( + sha: usize, + rng: &mut RAND, + s: &[u8], + f: &[u8], + c: &mut [u8], + d: &mut [u8], +) -> isize { + let mut t: [u8; EGS] = [0; EGS]; + let mut b: [u8; EGS] = [0; EGS]; + + hmac::GPhashit(hmac::MC_SHA2, sha, &mut b, EGS,0,Some(f), -1, None); + + let G = ECP::generator(); + + let r = BIG::new_ints(&rom::CURVE_ORDER); + + let sc = BIG::frombytes(s); /* s or &s? */ + let fb = BIG::frombytes(&b); + + let mut cb = BIG::new(); + let mut db = BIG::new(); + let mut tb = BIG::new(); + let mut V = ECP::new(); + + while db.iszilch() { + let mut u = BIG::randomnum(&r, rng); + let w = BIG::randomnum(&r, rng); /* IMPORTANT - side channel masking to protect invmodp() */ + + V.copy(&G); + V = V.clmul(&u,&r); + let vx = V.getx(); + cb.copy(&vx); + cb.rmod(&r); + if cb.iszilch() { + continue; + } + + tb.copy(&BIG::modmul(&u, &w, &r)); + u.copy(&tb); + + u.invmodp(&r); + db.copy(&BIG::modmul(&sc, &cb, &r)); + db.copy(&BIG::modadd(&db, &fb, &r)); + tb.copy(&BIG::modmul(&db, &w, &r)); + db.copy(&tb); + + tb.copy(&BIG::modmul(&u, &db, &r)); + db.copy(&tb); + } + + cb.tobytes(&mut t); + for i in 0..EGS { + c[i] = t[i] + } + db.tobytes(&mut t); + for i in 0..EGS { + d[i] = t[i] + } + 0 +} + +/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */ +#[allow(non_snake_case)] +pub fn ecpvp_dsa(sha: usize, w: &[u8], f: &[u8], c: &[u8], d: &[u8]) -> isize { + let mut res = 0; + + let mut b: [u8; EGS] = [0; EGS]; + + hmac::GPhashit(hmac::MC_SHA2, sha, &mut b, EGS, 0,Some(f), -1, None); + + let mut G = ECP::generator(); + + let r = BIG::new_ints(&rom::CURVE_ORDER); + + let mut cb = BIG::frombytes(c); /* c or &c ? */ + let mut db = BIG::frombytes(d); /* d or &d ? */ + let mut fb = BIG::frombytes(&b); + let mut tb = BIG::new(); + + if cb.iszilch() || BIG::comp(&cb, &r) >= 0 || db.iszilch() || BIG::comp(&db, &r) >= 0 { + res = ERROR; + } + + if res == 0 { + db.invmodp(&r); + tb.copy(&BIG::modmul(&mut fb, &mut db, &r)); + fb.copy(&tb); + let h2 = BIG::modmul(&mut cb, &mut db, &r); + + let WP = ECP::frombytes(&w); + if WP.is_infinity() { + res = ERROR; + } else { + let mut P = ECP::new(); + P.copy(&WP); + + P = P.mul2(&h2, &mut G, &fb); + + if P.is_infinity() { + res = ERROR; + } else { + db = P.getx(); + db.rmod(&r); + + if BIG::comp(&db, &cb) != 0 { + res = ERROR + } + } + } + } + + res +} + +/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */ +// returns length of ciphertext +#[allow(non_snake_case)] +pub fn ecies_encrypt( + sha: usize, + p1: &[u8], + p2: &[u8], + rng: &mut RAND, + w: &[u8], + m: &[u8], + v: &mut [u8], + c: &mut [u8], + t: &mut [u8], +) -> usize { + let mut z: [u8; EFS] = [0; EFS]; + let mut k1: [u8; ecp::AESKEY] = [0; ecp::AESKEY]; + let mut k2: [u8; ecp::AESKEY] = [0; ecp::AESKEY]; + let mut u: [u8; EGS] = [0; EGS]; + let mut vz: [u8; 3 * EFS + 1] = [0; 3 * EFS + 1]; + let mut k: [u8; 2 * ecp::AESKEY] = [0; 2 * ecp::AESKEY]; + + if key_pair_generate(Some(rng), &mut u, v) != 0 { + return 0; + } + if ecpsvdp_dh(&u, &w, &mut z, 0) != 0 { + return 0; + } + + for i in 0..2 * EFS + 1 { + vz[i] = v[i] + } + for i in 0..EFS { + vz[2 * EFS + 1 + i] = z[i] + } + + hmac::kdf2(hmac::MC_SHA2, sha, &vz, Some(p1), 2 * ecp::AESKEY, &mut k); + + for i in 0..ecp::AESKEY { + k1[i] = k[i]; + k2[i] = k[ecp::AESKEY + i] + } + + let clen = aes::cbc_iv0_encrypt(&k1, m, c); + + let mut l2: [u8; 8] = [0; 8]; + let p2l = p2.len(); + + hmac::inttobytes(p2l, &mut l2); + + let mut opt=clen; + for i in 0..p2l { + c[opt]=p2[i]; opt+=1; + } + for i in 0..8 { + c[opt]=l2[i]; opt+=1; + } + + hmac::hmac1(hmac::MC_SHA2, sha, t, t.len(), &k2, &c[0..opt]); + + clen +} + +/* constant time n-byte compare */ +fn ncomp(t1: &[u8], t2: &[u8], n: usize) -> bool { + let mut res = 0; + for i in 0..n { + res |= (t1[i] ^ t2[i]) as isize; + } + if res == 0 { + return true; + } + false +} + +/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */ +// returns length of plaintext +#[allow(non_snake_case)] +pub fn ecies_decrypt( + sha: usize, + p1: &[u8], + p2: &[u8], + v: &[u8], + c: &mut [u8], + clen: usize, + t: &[u8], + u: &[u8], + m: &mut [u8], +) -> usize { + let mut z: [u8; EFS] = [0; EFS]; + let mut k1: [u8; ecp::AESKEY] = [0; ecp::AESKEY]; + let mut k2: [u8; ecp::AESKEY] = [0; ecp::AESKEY]; + let mut vz: [u8; 3 * EFS + 1] = [0; 3 * EFS + 1]; + let mut k: [u8; 2 * ecp::AESKEY] = [0; 2 * ecp::AESKEY]; + let mut tag: [u8; 32] = [0; 32]; /* 32 is max length of tag */ + + for i in 0..t.len() { + tag[i] = t[i] + } + + if ecpsvdp_dh(&u, &v, &mut z, 0) != 0 { + return 0; + } + + for i in 0..2 * EFS + 1 { + vz[i] = v[i] + } + for i in 0..EFS { + vz[2 * EFS + 1 + i] = z[i] + } + + hmac::kdf2(hmac::MC_SHA2, sha, &vz, Some(p1), 2 * ecp::AESKEY, &mut k); + + for i in 0..ecp::AESKEY { + k1[i] = k[i]; + k2[i] = k[ecp::AESKEY + i] + } + + let mlen = aes::cbc_iv0_decrypt(&k1, &c[0..clen], m); + + if mlen == 0 { + return 0; + } + + let mut l2: [u8; 8] = [0; 8]; + let p2l = p2.len(); + + hmac::inttobytes(p2l, &mut l2); + let mut opt=clen; + + for i in 0..p2l { + c[opt]=p2[i]; opt+=1; + } + for i in 0..8 { + c[opt]=l2[i]; opt+=1; + } + + let tl=tag.len(); + hmac::hmac1(hmac::MC_SHA2, sha, &mut tag, tl, &k2, &c[0..opt]); + + if !ncomp(&t, &tag, t.len()) { + return 0; + } + + mlen +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecp.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecp.rs new file mode 100644 index 000000000000..23a6b7adf915 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecp.rs @@ -0,0 +1,1824 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::bn254::big; +use crate::bn254::big::BIG; +use crate::bn254::dbig::DBIG; +use crate::bn254::fp::FP; +use crate::bn254::fp; +use crate::bn254::rom; + +#[derive(Clone)] +pub struct ECP { + x: FP, + y: FP, + z: FP, +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for ECP { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for ECP { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +pub const WEIERSTRASS: usize = 0; +pub const EDWARDS: usize = 1; +pub const MONTGOMERY: usize = 2; +pub const NOT: usize = 0; +pub const BN: usize = 1; +pub const BLS12: usize = 2; +pub const BLS24: usize = 3; +pub const BLS48: usize = 4; +pub const D_TYPE: usize = 0; +pub const M_TYPE: usize = 1; +pub const POSITIVEX: usize = 0; +pub const NEGATIVEX: usize = 1; + +pub const CURVETYPE:usize=WEIERSTRASS; +pub const CURVE_A:isize=0; +pub const CURVE_PAIRING_TYPE:usize=BN; +pub const SEXTIC_TWIST:usize=D_TYPE; +pub const SIGN_OF_X:usize=NEGATIVEX; +pub const ATE_BITS:usize=66; +pub const G2_TABLE:usize=71; +pub const HTC_ISO:usize=0; +pub const HTC_ISO_G2:usize=0; + +pub const ALLOW_ALT_COMPRESS:bool=false; +pub const HASH_TYPE:usize=32; +pub const AESKEY:usize=16; + +#[allow(non_snake_case)] +impl ECP { + pub fn pnew() -> ECP { + ECP { + x: FP::new(), + y: FP::new_int(1), + z: FP::new(), + } + } + + pub fn new() -> ECP { + let mut E = ECP::pnew(); + if CURVETYPE == EDWARDS { + E.z.one(); + } + E + } + + /* set (x,y) from two BIGs */ + pub fn new_bigs(ix: &BIG, iy: &BIG) -> ECP { + let mut E = ECP::new(); + E.x.bcopy(ix); + E.y.bcopy(iy); + E.z.one(); + E.x.norm(); + let rhs = ECP::rhs(&E.x); + if CURVETYPE == MONTGOMERY { + if rhs.qr(None) != 1 { + E.inf(); + } + } else { + let mut y2 = FP::new_copy(&E.y); + y2.sqr(); + if !y2.equals(&rhs) { + E.inf(); + } + } + E + } + + /* set (x,y) from BIG and a bit */ + pub fn new_bigint(ix: &BIG, s: isize) -> ECP { + let mut E = ECP::new(); + let mut hint = FP::new(); + E.x.bcopy(ix); + E.x.norm(); + E.z.one(); + + let rhs = ECP::rhs(&E.x); + + if rhs.qr(Some(&mut hint)) == 1 { + let mut ny = rhs.sqrt(Some(&hint)); + if ny.sign() != s { + ny.neg(); ny.norm() + } + E.y.copy(&ny); + } else { + E.inf() + } + E + } + + #[allow(non_snake_case)] + /* set from x - calculate y from curve equation */ + pub fn new_big(ix: &BIG) -> ECP { + let mut E = ECP::new(); + let mut hint = FP::new(); + E.x.bcopy(ix); + E.x.norm(); + E.z.one(); + let rhs = ECP::rhs(&E.x); + if rhs.qr(Some(&mut hint)) == 1 { + if CURVETYPE != MONTGOMERY { + E.y.copy(&rhs.sqrt(Some(&hint))) + } + } else { + E.inf(); + } + E + } + + /* set this=O */ + pub fn inf(&mut self) { + self.x.zero(); + if CURVETYPE != MONTGOMERY { + self.y.one(); + } + if CURVETYPE != EDWARDS { + self.z.zero(); + } else { + self.z.one() + } + } + + /* Calculate RHS of curve equation */ + fn rhs(x: &FP) -> FP { + let mut r = FP::new_copy(x); + r.sqr(); + + if CURVETYPE == WEIERSTRASS { + // x^3+Ax+B + let b = FP::new_big(&BIG::new_ints(&rom::CURVE_B)); + r.mul(x); + if CURVE_A == -3 { + let mut cx = FP::new_copy(x); + cx.imul(3); + cx.neg(); + cx.norm(); + r.add(&cx); + } + r.add(&b); + } + if CURVETYPE == EDWARDS { + // (Ax^2-1)/(Bx^2-1) + let mut b = FP::new_big(&BIG::new_ints(&rom::CURVE_B)); + let one = FP::new_int(1); + b.mul(&r); + b.sub(&one); + b.norm(); + if CURVE_A == -1 { + r.neg() + } + r.sub(&one); + r.norm(); + b.inverse(None); + r.mul(&b); + } + if CURVETYPE == MONTGOMERY { + // x^3+Ax^2+x + let mut x3 = FP::new(); + x3.copy(&r); + x3.mul(x); + r.imul(CURVE_A); + r.add(&x3); + r.add(&x); + } + r.reduce(); + r + } + + /* test for O point-at-infinity */ + pub fn is_infinity(&self) -> bool { + if CURVETYPE == EDWARDS { + return self.x.iszilch() && self.y.equals(&self.z); + } + if CURVETYPE == WEIERSTRASS { + return self.x.iszilch() && self.z.iszilch(); + } + if CURVETYPE == MONTGOMERY { + return self.z.iszilch(); + } + true + } + + /* Conditional swap of P and Q dependant on d */ + pub fn cswap(&mut self, Q: &mut ECP, d: isize) { + self.x.cswap(&mut Q.x, d); + if CURVETYPE != MONTGOMERY { + self.y.cswap(&mut Q.y, d) + } + self.z.cswap(&mut Q.z, d); + } + + /* Conditional move of Q to P dependant on d */ + pub fn cmove(&mut self, Q: &ECP, d: isize) { + self.x.cmove(&Q.x, d); + if CURVETYPE != MONTGOMERY { + self.y.cmove(&Q.y, d) + } + self.z.cmove(&Q.z, d); + } + + /* return 1 if b==c, no branching */ + fn teq(b: i32, c: i32) -> isize { + let mut x = b ^ c; + x -= 1; // if x=0, x now -1 + ((x >> 31) & 1) as isize + } + + /* this=P */ + pub fn copy(&mut self, P: &ECP) { + self.x.copy(&P.x); + if CURVETYPE != MONTGOMERY { + self.y.copy(&P.y) + } + self.z.copy(&P.z); + } + + /* this=-this */ + pub fn neg(&mut self) { + if CURVETYPE == WEIERSTRASS { + self.y.neg(); + self.y.norm(); + } + if CURVETYPE == EDWARDS { + self.x.neg(); + self.x.norm(); + } + } + /* multiply x coordinate */ + pub fn mulx(&mut self, c: &mut FP) { + self.x.mul(c); + } + + /* Constant time select from pre-computed table */ + fn selector(&mut self, W: &[ECP], b: i32) { + // unsure about &[& syntax. An array of pointers I hope.. + let mut MP = ECP::new(); + let m = b >> 31; + let mut babs = (b ^ m) - m; + + babs = (babs - 1) / 2; + + self.cmove(&W[0], ECP::teq(babs, 0)); // conditional move + self.cmove(&W[1], ECP::teq(babs, 1)); + self.cmove(&W[2], ECP::teq(babs, 2)); + self.cmove(&W[3], ECP::teq(babs, 3)); + self.cmove(&W[4], ECP::teq(babs, 4)); + self.cmove(&W[5], ECP::teq(babs, 5)); + self.cmove(&W[6], ECP::teq(babs, 6)); + self.cmove(&W[7], ECP::teq(babs, 7)); + + MP.copy(self); + MP.neg(); + self.cmove(&MP, (m & 1) as isize); + } + + /* Test P == Q */ + pub fn equals(&self, Q: &ECP) -> bool { + let mut a = FP::new(); + let mut b = FP::new(); + a.copy(&self.x); + a.mul(&Q.z); + b.copy(&Q.x); + b.mul(&self.z); + if !a.equals(&b) { + return false; + } + if CURVETYPE != MONTGOMERY { + a.copy(&self.y); + a.mul(&Q.z); + b.copy(&Q.y); + b.mul(&self.z); + if !a.equals(&b) { + return false; + } + } + true + } + + /* set to affine - from (x,y,z) to (x,y) */ + pub fn affine(&mut self) { + if self.is_infinity() { + return; + } + let one = FP::new_int(1); + if self.z.equals(&one) { + return; + } + self.z.inverse(None); + + self.x.mul(&self.z); + self.x.reduce(); + if CURVETYPE != MONTGOMERY { + self.y.mul(&self.z); + self.y.reduce(); + } + self.z.copy(&one); + } + + /* extract x as a BIG */ + pub fn getx(&self) -> BIG { + let mut W = ECP::new(); + W.copy(self); + W.affine(); + W.x.redc() + } + + /* extract y as a BIG */ + pub fn gety(&self) -> BIG { + let mut W = ECP::new(); + W.copy(self); + W.affine(); + W.y.redc() + } + + /* get sign of Y */ + pub fn gets(&self) -> isize { + let mut W = ECP::new(); + W.copy(self); + W.affine(); + W.y.sign() + } + + /* extract x as an FP */ + pub fn getpx(&self) -> FP { + FP::new_copy(&self.x) + } + /* extract y as an FP */ + pub fn getpy(&self) -> FP { + FP::new_copy(&self.y) + } + + /* extract z as an FP */ + pub fn getpz(&self) -> FP { + FP::new_copy(&self.z) + } + + /* convert to byte array */ + pub fn tobytes(&self, b: &mut [u8], compress: bool) { + const MB:usize = big::MODBYTES as usize; + let mut t: [u8; MB] = [0; MB]; + let mut alt=false; + let mut W = ECP::new(); + W.copy(self); + W.affine(); + W.x.redc().tobytes(&mut t); + + if CURVETYPE == MONTGOMERY { + for i in 0..MB { + b[i] = t[i] + } + return; + } + + if (fp::MODBITS-1)%8 <= 4 && ALLOW_ALT_COMPRESS { + alt=true; + } + + if alt { + for i in 0..MB { + b[i]=t[i]; + } + if compress { + b[0]|=0x80; + if W.y.islarger()==1 { + b[0]|=0x20; + } + } else { + W.y.redc().tobytes(&mut t); + for i in 0..MB { + b[i+MB]=t[i]; + } + } + } else { + for i in 0..MB { + b[i + 1] = t[i]; + } + if compress { + b[0] = 0x02; + if W.y.sign() == 1 { + b[0] = 0x03; + } + return; + } + b[0] = 0x04; + W.y.redc().tobytes(&mut t); + for i in 0..MB { + b[i + MB + 1] = t[i]; + } + } + } + + /* convert from byte array to point */ + pub fn frombytes(b: &[u8]) -> ECP { + const MB:usize = big::MODBYTES as usize; + let mut t: [u8; MB] = [0; MB]; + let mut alt=false; + let p = BIG::new_ints(&rom::MODULUS); + + if CURVETYPE == MONTGOMERY { + for i in 0..MB { + t[i] = b[i]; + } + let px = BIG::frombytes(&t); + if BIG::comp(&px, &p) >= 0 { + return ECP::new(); + } + return ECP::new_big(&px); + } + + if (fp::MODBITS-1)%8 <= 4 && ALLOW_ALT_COMPRESS { + alt=true; + } + + if alt { + for i in 0..MB { + t[i]=b[i]; + } + t[0]&=0x1f; + let px=BIG::frombytes(&t); + if (b[0]&0x80)==0 { + for i in 0 ..MB { + t[i]=b[i+MB]; + } + let py=BIG::frombytes(&t); + return ECP::new_bigs(&px, &py); + } else { + let sgn=(b[0]&0x20)>>5; + let mut P=ECP::new_bigint(&px,0); + let cmp=P.y.islarger(); + if (sgn == 1 && cmp != 1) || (sgn == 0 && cmp == 1) { + P.neg(); + } + return P; + } + } else { + for i in 0..MB { + t[i] = b[i + 1]; + } + let px = BIG::frombytes(&t); + if BIG::comp(&px, &p) >= 0 { + return ECP::new(); + } + if b[0] == 0x04 { + for i in 0..MB { + t[i] = b[i + MB + 1]; + } + let py = BIG::frombytes(&t); + if BIG::comp(&py, &p) >= 0 { + return ECP::new(); + } + return ECP::new_bigs(&px, &py); + } + if b[0] == 0x02 || b[0] == 0x03 { + return ECP::new_bigint(&px, (b[0] & 1) as isize); + } + } + + ECP::new() + } + + /* convert to hex string */ + #[cfg(feature = "std")] + pub fn tostring(&self) -> String { + let mut W = ECP::new(); + W.copy(self); + W.affine(); + if W.is_infinity() { + return String::from("infinity"); + } + if CURVETYPE == MONTGOMERY { + return format!("({})", W.x.redc().tostring()); + } else { + return format!("({},{})", W.x.redc().tostring(), W.y.redc().tostring()); + }; + } + + /* this*=2 */ + pub fn dbl(&mut self) { + if CURVETYPE == WEIERSTRASS { + if CURVE_A == 0 { + let mut t0 = FP::new_copy(&self.y); + t0.sqr(); + let mut t1 = FP::new_copy(&self.y); + t1.mul(&self.z); + let mut t2 = FP::new_copy(&self.z); + t2.sqr(); + + self.z.copy(&t0); + self.z.add(&t0); + self.z.norm(); + self.z.dbl(); + self.z.dbl(); + self.z.norm(); + t2.imul(3 * rom::CURVE_B_I); + + let mut x3 = FP::new_copy(&t2); + x3.mul(&self.z); + + let mut y3 = FP::new_copy(&t0); + y3.add(&t2); + y3.norm(); + self.z.mul(&t1); + t1.copy(&t2); + t1.add(&t2); + t2.add(&t1); + t0.sub(&t2); + t0.norm(); + y3.mul(&t0); + y3.add(&x3); + t1.copy(&self.x); + t1.mul(&self.y); + self.x.copy(&t0); + self.x.norm(); + self.x.mul(&t1); + self.x.dbl(); + self.x.norm(); + self.y.copy(&y3); + self.y.norm(); + } else { + let mut t0 = FP::new_copy(&self.x); + let mut t1 = FP::new_copy(&self.y); + let mut t2 = FP::new_copy(&self.z); + let mut t3 = FP::new_copy(&self.x); + let mut z3 = FP::new_copy(&self.z); + let mut y3 = FP::new(); + let mut x3 = FP::new(); + let mut b = FP::new(); + + if rom::CURVE_B_I == 0 { + b.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_B))); + } + + t0.sqr(); //1 x^2 + t1.sqr(); //2 y^2 + t2.sqr(); //3 + + t3.mul(&self.y); //4 + t3.dbl(); + t3.norm(); //5 + z3.mul(&self.x); //6 + z3.dbl(); + z3.norm(); //7 + y3.copy(&t2); + + if rom::CURVE_B_I == 0 { + y3.mul(&b); //8 + } else { + y3.imul(rom::CURVE_B_I); + } + + y3.sub(&z3); //9 *** + x3.copy(&y3); + x3.add(&y3); + x3.norm(); //10 + + y3.add(&x3); //11 + x3.copy(&t1); + x3.sub(&y3); + x3.norm(); //12 + y3.add(&t1); + y3.norm(); //13 + y3.mul(&x3); //14 + x3.mul(&t3); //15 + t3.copy(&t2); + t3.add(&t2); //16 + t2.add(&t3); //17 + + if rom::CURVE_B_I == 0 { + z3.mul(&b); //18 + } else { + z3.imul(rom::CURVE_B_I); + } + + z3.sub(&t2); //19 + z3.sub(&t0); + z3.norm(); //20 *** + t3.copy(&z3); + t3.add(&z3); //21 + + z3.add(&t3); + z3.norm(); //22 + t3.copy(&t0); + t3.add(&t0); //23 + t0.add(&t3); //24 + t0.sub(&t2); + t0.norm(); //25 + + t0.mul(&z3); //26 + y3.add(&t0); //27 + t0.copy(&self.y); + t0.mul(&self.z); //28 + t0.dbl(); + t0.norm(); //29 + z3.mul(&t0); //30 + x3.sub(&z3); //31 + t0.dbl(); + t0.norm(); //32 + t1.dbl(); + t1.norm(); //33 + z3.copy(&t0); + z3.mul(&t1); //34 + + self.x.copy(&x3); + self.x.norm(); + self.y.copy(&y3); + self.y.norm(); + self.z.copy(&z3); + self.z.norm(); + } + } + if CURVETYPE == EDWARDS { + let mut c = FP::new_copy(&self.x); + let mut d = FP::new_copy(&self.y); + let mut h = FP::new_copy(&self.z); + let mut j = FP::new(); + + self.x.mul(&self.y); + self.x.dbl(); + self.x.norm(); + c.sqr(); + d.sqr(); + if CURVE_A == -1 { + c.neg() + } + self.y.copy(&c); + self.y.add(&d); + self.y.norm(); + h.sqr(); + h.dbl(); + self.z.copy(&self.y); + j.copy(&self.y); + j.sub(&h); + j.norm(); + self.x.mul(&j); + c.sub(&d); + c.norm(); + self.y.mul(&c); + self.z.mul(&j); + } + if CURVETYPE == MONTGOMERY { + let mut a = FP::new_copy(&self.x); + let mut b = FP::new_copy(&self.x); + let mut aa = FP::new(); + let mut bb = FP::new(); + let mut c = FP::new(); + + a.add(&self.z); + a.norm(); + aa.copy(&a); + aa.sqr(); + b.sub(&self.z); + b.norm(); + bb.copy(&b); + bb.sqr(); + c.copy(&aa); + c.sub(&bb); + c.norm(); + + self.x.copy(&aa); + self.x.mul(&bb); + + a.copy(&c); + a.imul((CURVE_A + 2) / 4); + + bb.add(&a); + bb.norm(); + self.z.copy(&bb); + self.z.mul(&c); + } + } + + /* self+=Q */ + pub fn add(&mut self, Q: &ECP) { + if CURVETYPE == WEIERSTRASS { + if CURVE_A == 0 { + let b = 3 * rom::CURVE_B_I; + let mut t0 = FP::new_copy(&self.x); + t0.mul(&Q.x); + let mut t1 = FP::new_copy(&self.y); + t1.mul(&Q.y); + let mut t2 = FP::new_copy(&self.z); + t2.mul(&Q.z); + let mut t3 = FP::new_copy(&self.x); + t3.add(&self.y); + t3.norm(); + let mut t4 = FP::new_copy(&Q.x); + t4.add(&Q.y); + t4.norm(); + t3.mul(&t4); + t4.copy(&t0); + t4.add(&t1); + + t3.sub(&t4); + t3.norm(); + t4.copy(&self.y); + t4.add(&self.z); + t4.norm(); + let mut x3 = FP::new_copy(&Q.y); + x3.add(&Q.z); + x3.norm(); + + t4.mul(&x3); + x3.copy(&t1); + x3.add(&t2); + + t4.sub(&x3); + t4.norm(); + x3.copy(&self.x); + x3.add(&self.z); + x3.norm(); + let mut y3 = FP::new_copy(&Q.x); + y3.add(&Q.z); + y3.norm(); + x3.mul(&y3); + y3.copy(&t0); + y3.add(&t2); + y3.rsub(&x3); + y3.norm(); + x3.copy(&t0); + x3.add(&t0); + t0.add(&x3); + t0.norm(); + t2.imul(b); + + let mut z3 = FP::new_copy(&t1); + z3.add(&t2); + z3.norm(); + t1.sub(&t2); + t1.norm(); + y3.imul(b); + + x3.copy(&y3); + x3.mul(&t4); + t2.copy(&t3); + t2.mul(&t1); + x3.rsub(&t2); + y3.mul(&t0); + t1.mul(&z3); + y3.add(&t1); + t0.mul(&t3); + z3.mul(&t4); + z3.add(&t0); + + self.x.copy(&x3); + self.x.norm(); + self.y.copy(&y3); + self.y.norm(); + self.z.copy(&z3); + self.z.norm(); + } else { + let mut t0 = FP::new_copy(&self.x); + let mut t1 = FP::new_copy(&self.y); + let mut t2 = FP::new_copy(&self.z); + let mut t3 = FP::new_copy(&self.x); + let mut t4 = FP::new_copy(&Q.x); + let mut z3 = FP::new(); + let mut y3 = FP::new_copy(&Q.x); + let mut x3 = FP::new_copy(&Q.y); + let mut b = FP::new(); + + if rom::CURVE_B_I == 0 { + b.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_B))); + } + + t0.mul(&Q.x); //1 + t1.mul(&Q.y); //2 + t2.mul(&Q.z); //3 + + t3.add(&self.y); + t3.norm(); //4 + t4.add(&Q.y); + t4.norm(); //5 + t3.mul(&t4); //6 + t4.copy(&t0); + t4.add(&t1); //7 + t3.sub(&t4); + t3.norm(); //8 + t4.copy(&self.y); + t4.add(&self.z); + t4.norm(); //9 + x3.add(&Q.z); + x3.norm(); //10 + t4.mul(&x3); //11 + x3.copy(&t1); + x3.add(&t2); //12 + + t4.sub(&x3); + t4.norm(); //13 + x3.copy(&self.x); + x3.add(&self.z); + x3.norm(); //14 + y3.add(&Q.z); + y3.norm(); //15 + + x3.mul(&y3); //16 + y3.copy(&t0); + y3.add(&t2); //17 + + y3.rsub(&x3); + y3.norm(); //18 + z3.copy(&t2); + + if rom::CURVE_B_I == 0 { + z3.mul(&b); //18 + } else { + z3.imul(rom::CURVE_B_I); + } + + x3.copy(&y3); + x3.sub(&z3); + x3.norm(); //20 + z3.copy(&x3); + z3.add(&x3); //21 + + x3.add(&z3); //22 + z3.copy(&t1); + z3.sub(&x3); + z3.norm(); //23 + x3.add(&t1); + x3.norm(); //24 + + if rom::CURVE_B_I == 0 { + y3.mul(&b); //18 + } else { + y3.imul(rom::CURVE_B_I); + } + + t1.copy(&t2); + t1.add(&t2); //t1.norm();//26 + t2.add(&t1); //27 + + y3.sub(&t2); //28 + + y3.sub(&t0); + y3.norm(); //29 + t1.copy(&y3); + t1.add(&y3); //30 + y3.add(&t1); + y3.norm(); //31 + + t1.copy(&t0); + t1.add(&t0); //32 + t0.add(&t1); //33 + t0.sub(&t2); + t0.norm(); //34 + t1.copy(&t4); + t1.mul(&y3); //35 + t2.copy(&t0); + t2.mul(&y3); //36 + y3.copy(&x3); + y3.mul(&z3); //37 + y3.add(&t2); //y3.norm();//38 + x3.mul(&t3); //39 + x3.sub(&t1); //40 + z3.mul(&t4); //41 + t1.copy(&t3); + t1.mul(&t0); //42 + z3.add(&t1); + self.x.copy(&x3); + self.x.norm(); + self.y.copy(&y3); + self.y.norm(); + self.z.copy(&z3); + self.z.norm(); + } + } + if CURVETYPE == EDWARDS { + let bb = FP::new_big(&BIG::new_ints(&rom::CURVE_B)); + let mut a = FP::new_copy(&self.z); + let mut b = FP::new(); + let mut c = FP::new_copy(&self.x); + let mut d = FP::new_copy(&self.y); + let mut e = FP::new(); + let mut f = FP::new(); + let mut g = FP::new(); + + a.mul(&Q.z); + b.copy(&a); + b.sqr(); + c.mul(&Q.x); + d.mul(&Q.y); + + e.copy(&c); + e.mul(&d); + e.mul(&bb); + f.copy(&b); + f.sub(&e); + g.copy(&b); + g.add(&e); + + if CURVE_A == 1 { + e.copy(&d); + e.sub(&c); + } + c.add(&d); + + b.copy(&self.x); + b.add(&self.y); + d.copy(&Q.x); + d.add(&Q.y); + b.norm(); + d.norm(); + b.mul(&d); + b.sub(&c); + b.norm(); + f.norm(); + b.mul(&f); + self.x.copy(&a); + self.x.mul(&b); + g.norm(); + if CURVE_A == 1 { + e.norm(); + c.copy(&e); + c.mul(&g); + } + if CURVE_A == -1 { + c.norm(); + c.mul(&g); + } + self.y.copy(&a); + self.y.mul(&c); + self.z.copy(&f); + self.z.mul(&g); + } + } + + /* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */ + pub fn dadd(&mut self, Q: &ECP, W: &ECP) { + let mut a = FP::new_copy(&self.x); + let mut b = FP::new_copy(&self.x); + let mut c = FP::new_copy(&Q.x); + let mut d = FP::new_copy(&Q.x); + let mut da = FP::new(); + let mut cb = FP::new(); + + a.add(&self.z); + b.sub(&self.z); + + c.add(&Q.z); + d.sub(&Q.z); + + a.norm(); + d.norm(); + + da.copy(&d); + da.mul(&a); + + c.norm(); + b.norm(); + + cb.copy(&c); + cb.mul(&b); + + a.copy(&da); + a.add(&cb); + a.norm(); + a.sqr(); + b.copy(&da); + b.sub(&cb); + b.norm(); + b.sqr(); + + self.x.copy(&a); + self.z.copy(&W.x); + self.z.mul(&b); + } + + /* self-=Q */ + pub fn sub(&mut self, Q: &ECP) { + let mut NQ = ECP::new(); + NQ.copy(Q); + NQ.neg(); + self.add(&NQ); + } + + /* constant time multiply by small integer of length bts - use ladder */ + pub fn pinmul(&self, e: i32, bts: i32) -> ECP { + if CURVETYPE == MONTGOMERY { + self.mul(&BIG::new_int(e as isize)) + } else { + let mut P = ECP::new(); + let mut R0 = ECP::new(); + let mut R1 = ECP::new(); + R1.copy(&self); + + for i in (0..bts).rev() { + let b = ((e >> i) & 1) as isize; + P.copy(&R1); + P.add(&R0); + R0.cswap(&mut R1, b); + R1.copy(&P); + R0.dbl(); + R0.cswap(&mut R1, b); + } + P.copy(&R0); + P + } + } + +// Point multiplication, multiplies a point P by a scalar e +// This code has no inherent awareness of the order of the curve, or the order of the point. +// The order of the curve will be h.r, where h is a cofactor, and r is a large prime +// Typically P will be of order r (but not always), and typically e will be less than r (but not always) +// A problem can arise if a secret e is a few bits less than r, as the leading zeros in e will leak via a timing attack +// The secret e may however be greater than r (see RFC7748 which combines elimination of a small cofactor h with the point multiplication, using an e>r) +// Our solution is to use as a multiplier an e, whose length in bits is that of the logical OR of e and r, hence allowing e>r while forcing inclusion of leading zeros if e ECP { + return self.clmul(e,e); + } + +// .. but this one does not (typically set maxe=r) +// Set P=e*P + pub fn clmul(&self, e: &BIG, maxe: &BIG) -> ECP { + if e.iszilch() || self.is_infinity() { + return ECP::new(); + } + let mut P = ECP::new(); + let mut cm = BIG::new_copy(e); cm.or(maxe); + let max=cm.nbits(); + + if CURVETYPE == MONTGOMERY { + /* use Ladder */ + let mut D = ECP::new(); + let mut R0 = ECP::new(); + R0.copy(&self); + let mut R1 = ECP::new(); + R1.copy(&self); + R1.dbl(); + D.copy(&self); D.affine(); + let nb = max; + + for i in (0..nb - 1).rev() { + let b = e.bit(i); + P.copy(&R1); + P.dadd(&R0, &D); + R0.cswap(&mut R1, b); + R1.copy(&P); + R0.dbl(); + R0.cswap(&mut R1, b); + } + P.copy(&R0) + } else { + // fixed size windows + let mut mt = BIG::new(); + let mut t = BIG::new(); + let mut Q = ECP::new(); + let mut C = ECP::new(); + + let mut W: [ECP; 8] = [ + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ]; + + const CT: usize = 1 + (big::NLEN * (big::BASEBITS as usize) + 3) / 4; + let mut w: [i8; CT] = [0; CT]; + + Q.copy(&self); + Q.dbl(); + + W[0].copy(&self); + + for i in 1..8 { + C.copy(&W[i - 1]); + W[i].copy(&C); + W[i].add(&Q); + } + + // make exponent odd - add 2P if even, P if odd + t.copy(&e); + let s = t.parity(); + t.inc(1); + t.norm(); + let ns = t.parity(); + mt.copy(&t); + mt.inc(1); + mt.norm(); + t.cmove(&mt, s); + Q.cmove(&self, ns); + C.copy(&Q); + + let nb = 1 + (max + 3) / 4; + + // convert exponent to signed 4-bit window + for i in 0..nb { + w[i] = (t.lastbits(5) - 16) as i8; + t.dec(w[i] as isize); + t.norm(); + t.fshr(4); + } + w[nb] = t.lastbits(5) as i8; + + //P.copy(&W[((w[nb] as usize) - 1) / 2]); + + P.selector(&W, w[nb] as i32); + for i in (0..nb).rev() { + Q.selector(&W, w[i] as i32); + P.dbl(); + P.dbl(); + P.dbl(); + P.dbl(); + P.add(&Q); + } + P.sub(&C); /* apply correction */ + } + P + } + +// Generic multi-multiplication, fixed 4-bit window, P=Sigma e_i*X_i + pub fn muln(n: usize, X: &[ECP], e: &[BIG]) -> ECP { + let mut B: [ECP; 16] = [ + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ]; + let mut mt = BIG::new(); + let mut t = BIG::new(); + let mut P = ECP::new(); + let mut S = ECP::new(); + let mut R = ECP::new(); + + mt.copy(&e[0]); mt.norm(); + for i in 1..n { // find biggest + t.copy(&e[i]); t.norm(); + let k=BIG::comp(&t,&mt); + mt.cmove(&t,(k+1)/2); + } + let nb=(mt.nbits()+3)/4; + for i in (0..nb).rev() { // Pippenger's algorithm + for j in 0..16 { + B[j].inf(); + } + for j in 0..n { + mt.copy(&e[j]); mt.norm(); + mt.shr((i*4) as usize); + let k=mt.lastbits(4) as usize; + B[k].add(&X[j]); + } + R.inf(); S.inf(); + for j in (1..16).rev() { + R.add(&B[j]); + S.add(&R); + } + for _ in 0..4 { + P.dbl(); + } + P.add(&S); + } + P + } + + /* Return e.this+f.Q */ + + pub fn mul2(&self, e: &BIG, Q: &ECP, f: &BIG) -> ECP { + let mut te = BIG::new(); + let mut tf = BIG::new(); + let mut mt = BIG::new(); + let mut S = ECP::new(); + let mut T = ECP::new(); + let mut C = ECP::new(); + + let mut W: [ECP; 8] = [ + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ECP::new(), + ]; + + const CT: usize = 1 + (big::NLEN * (big::BASEBITS as usize) + 1) / 2; + let mut w: [i8; CT] = [0; CT]; + + te.copy(e); + tf.copy(f); + + // precompute table + + W[1].copy(&self); + W[1].sub(Q); + W[2].copy(&self); + W[2].add(Q); + S.copy(&Q); + S.dbl(); + C.copy(&W[1]); + W[0].copy(&C); + W[0].sub(&S); // copy to C is stupid Rust thing.. + C.copy(&W[2]); + W[3].copy(&C); + W[3].add(&S); + T.copy(&self); + T.dbl(); + C.copy(&W[1]); + W[5].copy(&C); + W[5].add(&T); + C.copy(&W[2]); + W[6].copy(&C); + W[6].add(&T); + C.copy(&W[5]); + W[4].copy(&C); + W[4].sub(&S); + C.copy(&W[6]); + W[7].copy(&C); + W[7].add(&S); + + // if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction + + let mut s = te.parity(); + te.inc(1); + te.norm(); + let mut ns = te.parity(); + mt.copy(&te); + mt.inc(1); + mt.norm(); + te.cmove(&mt, s); + T.cmove(&self, ns); + C.copy(&T); + + s = tf.parity(); + tf.inc(1); + tf.norm(); + ns = tf.parity(); + mt.copy(&tf); + mt.inc(1); + mt.norm(); + tf.cmove(&mt, s); + S.cmove(&Q, ns); + C.add(&S); + + mt.copy(&te); + mt.add(&tf); + mt.norm(); + let nb = 1 + (mt.nbits() + 1) / 2; + + // convert exponent to signed 2-bit window + for i in 0..nb { + let a = te.lastbits(3) - 4; + te.dec(a); + te.norm(); + te.fshr(2); + let b = tf.lastbits(3) - 4; + tf.dec(b); + tf.norm(); + tf.fshr(2); + w[i] = (4 * a + b) as i8; + } + w[nb] = (4 * te.lastbits(3) + tf.lastbits(3)) as i8; + //S.copy(&W[((w[nb] as usize) - 1) / 2]); + S.selector(&W, w[nb] as i32); + + for i in (0..nb).rev() { + T.selector(&W, w[i] as i32); + S.dbl(); + S.dbl(); + S.add(&T); + } + S.sub(&C); /* apply correction */ + S + } + + pub fn cfp(&mut self) { + let cf = rom::CURVE_COF_I; + if cf == 1 { + return; + } + if cf == 4 { + self.dbl(); + self.dbl(); + return; + } + if cf == 8 { + self.dbl(); + self.dbl(); + self.dbl(); + return; + } + let c = BIG::new_ints(&rom::CURVE_COF); + let P = self.mul(&c); + self.copy(&P); + } + +/* Hunt and Peck a BIG to a curve point */ + #[allow(non_snake_case)] + pub fn hap2point(h: &BIG) -> ECP { + let mut P: ECP; + let mut x =BIG::new_copy(&h); + loop { + if CURVETYPE != MONTGOMERY { + P = ECP::new_bigint(&x, 0); + } else { + P = ECP::new_big(&x); + } + x.inc(1); + x.norm(); + if !P.is_infinity() { + break; + } + } + P + } + +/* Constant time Map to Point */ + #[allow(non_snake_case)] + pub fn map2point(h: &FP) -> ECP { + let mut P = ECP::new(); + + if CURVETYPE == MONTGOMERY { + // Elligator 2 + let mut X1=FP::new(); + let mut X2=FP::new(); + let mut t =FP::new_copy(h); + let mut w =FP::new(); + let one=FP::new_int(1); + let A=FP::new_int(CURVE_A); + let mut N =FP::new(); + let mut D =FP::new(); + let mut hint =FP::new(); + + t.sqr(); // t^2 + + if fp::PM1D2 == 2 { + t.dbl(); // 2t^2 + } + if fp::PM1D2 == 1 { + t.neg(); // -t^2 + } + if fp::PM1D2 > 2 { + t.imul(fp::QNRI as isize); // precomputed QNR + } + + t.norm(); + D.copy(&t); D.add(&one); D.norm(); // Denominator D=1+z.t^2 + + X1.copy(&A); + X1.neg(); X1.norm(); // X1=-A/D + X2.copy(&X1); + X2.mul(&t); // X2=-At/D + + w.copy(&X1); w.sqr(); N.copy(&w); N.mul(&X1); + w.mul(&A); w.mul(&D); N.add(&w); + t.copy(&D); t.sqr(); + t.mul(&X1); + N.add(&t); N.norm(); // Numerator=x^3+ADx^2+D^2x + + t.copy(&N); t.mul(&D); // N*D + let qres=t.qr(Some(&mut hint)); // only exp + w.copy(&t); w.inverse(Some(&hint)); + D.copy(&w); D.mul(&N); // 1/D + X1.mul(&D); // get X1 + X2.mul(&D); // get X2 + X1.cmove(&X2,1-qres); + + let a=X1.redc(); + P.copy(&ECP::new_big(&a)); + + } + if CURVETYPE == EDWARDS { +// Elligator 2 - map to Montgomery, place point, map back + let mut X1=FP::new(); + let mut X2=FP::new(); + let mut t=FP::new_copy(h); + let mut w=FP::new(); + let one=FP::new_int(1); + let mut A=FP::new(); + let mut w1=FP::new(); + let mut w2=FP::new(); + let mut B = FP::new_big(&BIG::new_ints(&rom::CURVE_B)); + let mut Y=FP::new(); + let mut K=FP::new(); + let mut D=FP::new(); + let mut hint=FP::new(); + //let mut Y3=FP::new(); + let rfc: isize; + + if fp::MODTYPE != fp::GENERALISED_MERSENNE { + A.copy(&B); + if CURVE_A==1 { + A.add(&one); // A=B+1 + B.sub(&one); // B=B-1 + } else { + A.sub(&one); // A=B-1 + B.add(&one); // B=B+1 + } + A.norm(); B.norm(); + + A.div2(); // (A+B)/2 = J/K + B.div2(); // (B-A)/2 + B.div2(); // (B-A)/4 = -1/K + + K.copy(&B); + K.neg(); K.norm(); + + K.invsqrt(&mut w2,&mut w1); // return K, sqrt(1/K) - could be precalculated! + K.copy(&w2); + rfc=fp::RIADZ; + if rfc==1 { + A.mul(&K); + K.mul(&w1); + } else { + B.sqr(); + } + } else { + rfc=1; + A.copy(&FP::new_int(156326)); + } +// Map to this Montgomery curve X^2=X^3+AX^2+BX + t.sqr(); // t^2 + let mut qnr=0; + if fp::PM1D2 == 2 { + t.dbl(); + qnr=2; + } + if fp::PM1D2 == 1 { + t.neg(); + qnr = -1; + } + if fp::PM1D2 > 2 { + t.imul(fp::QNRI as isize); // precomputed QNR + qnr=fp::QNRI as isize; + } + t.norm(); + + D.copy(&t); D.add(&one); D.norm(); // Denominator=(1+z.u^2) + X1.copy(&A); + X1.neg(); X1.norm(); // X1=-(J/K).inv(1+z.u^2) + X2.copy(&X1); X2.mul(&t); // X2=X1*z*u^2 + +// Figure out RHS of Montgomery curve in rational form gx1/d^3 + + w.copy(&X1); w.sqr(); w1.copy(&w); w1.mul(&X1); + w.mul(&A); w.mul(&D); w1.add(&w); + w2.copy(&D); w2.sqr(); + + if rfc==0 { + w.copy(&X1); w.mul(&B); + w2.mul(&w); + w1.add(&w2); // w1=X1^3+ADX1^2+BD^2X1 + } else { + w2.mul(&X1); + w1.add(&w2); // w1=X1^3+ADX1^2+D^2X1 + } + w1.norm(); + + B.copy(&w1); B.mul(&D); // gx1=num/den^3 - is_qr num*den (same as num/den, same as num/den^3) + let qres=B.qr(Some(&mut hint)); // Exponentiation + w.copy(&B); w.inverse(Some(&hint)); + D.copy(&w); D.mul(&w1); // 1/D + X1.mul(&D); // get X1 + X2.mul(&D); // get X2 + D.sqr(); + + w1.copy(&B); w1.imul(qnr); + w.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_HTPC))); + w.mul(&hint); + w2.copy(&D); w2.mul(&h); + + X1.cmove(&X2,1-qres); + B.cmove(&w1,1-qres); + hint.cmove(&w,1-qres); + D.cmove(&w2,1-qres); + + Y.copy(&B.sqrt(Some(&hint))); + Y.mul(&D); +/* + Y.copy(&B.sqrt(Some(&hint))); // sqrt(num*den) + Y.mul(&D); // sqrt(num/den^3) + + B.imul(qnr); // now for gx2 = Z.u^2.gx1 + w.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_HTPC))); // qnr^C3 + hint.mul(&w); // modify hint for gx2 + + Y3.copy(&B.sqrt(Some(&hint))); // second candidate + D.mul(&h); + Y3.mul(&D); + + X1.cmove(&X2,1-qres); // pick correct one + Y.cmove(&Y3,1-qres); +*/ +// correct sign of Y + w.copy(&Y); w.neg(); w.norm(); + Y.cmove(&w,qres^Y.sign()); + + if rfc==0 { + X1.mul(&K); + Y.mul(&K); + } + + if fp::MODTYPE == fp::GENERALISED_MERSENNE { + // GOLDILOCKS isogeny + t.copy(&X1); t.sqr(); + w.copy(&t); w.add(&one); w.norm(); + t.sub(&one); t.norm(); + w1.copy(&t); w1.mul(&Y); + w1.dbl(); X2.copy(&w1); X2.add(&w1); X2.norm(); + t.sqr(); + Y.sqr(); Y.dbl(); Y.dbl(); Y.norm(); + B.copy(&t); B.add(&Y); B.norm(); + + w2.copy(&Y); w2.sub(&t); w2.norm(); + w2.mul(&X1); + t.mul(&X1); + Y.div2(); + w1.copy(&Y); w1.mul(&w); + w1.rsub(&t); w1.norm(); + + t.copy(&X2); t.mul(&w1); // output in projective to avoid inversion + P.x.copy(&t); + t.copy(&w2); t.mul(&B); + P.y.copy(&t); + t.copy(&w1); t.mul(&B); + P.z.copy(&t); + + return P; + } else { + w1.copy(&X1); w1.add(&one); w1.norm(); // s+1 + w2.copy(&X1); w2.sub(&one); w2.norm(); // s-1 + t.copy(&w1); t.mul(&Y); + X1.mul(&w1); + + if rfc==1 { + X1.mul(&K); + } + Y.mul(&w2); // output in projective to avoid inversion + P.x.copy(&X1); + P.y.copy(&Y); + P.z.copy(&t); + + return P + } + } + if CURVETYPE==WEIERSTRASS { + // SSWU or SVDW method + let mut A=FP::new(); + let mut B=FP::new(); + let mut X1=FP::new(); + let mut X2=FP::new(); + let mut X3=FP::new(); + let one=FP::new_int(1); + let mut Y=FP::new(); + let mut D=FP::new(); + let mut t=FP::new_copy(h); + let mut w=FP::new(); + let mut D2=FP::new(); + let mut hint=FP::new(); + let mut GX1=FP::new(); + //let mut Y3=FP::new(); + + let sgn=t.sign(); + + if CURVE_A != 0 || HTC_ISO != 0 + { // Map to point on isogenous curve + if HTC_ISO != 0 { +/* CAHCZS + A.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_AD))); + B.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_BD))); +CAHCZF */ + } else { + A.copy(&FP::new_int(CURVE_A)); + B.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_B))); + } + // SSWU Method + t.sqr(); + t.imul(fp::RIADZ); // Z from hash-to-point draft standard + w.copy(&t); w.add(&one); w.norm(); + + w.mul(&t); D.copy(&A); + D.mul(&w); + + w.add(&one); w.norm(); + w.mul(&B); + w.neg(); w.norm(); + + X2.copy(&w); + X3.copy(&t); X3.mul(&X2); + +// x^3+Ad^2x+Bd^3 + GX1.copy(&X2); GX1.sqr(); D2.copy(&D); + D2.sqr(); w.copy(&A); w.mul(&D2); GX1.add(&w); GX1.norm(); GX1.mul(&X2); D2.mul(&D); w.copy(&B); w.mul(&D2); GX1.add(&w); GX1.norm(); + + w.copy(&GX1); w.mul(&D); + let qr=w.qr(Some(&mut hint)); + D.copy(&w); D.inverse(Some(&hint)); + D.mul(&GX1); + X2.mul(&D); + X3.mul(&D); + t.mul(h); + D2.copy(&D); D2.sqr(); + + D.copy(&D2); D.mul(&t); + t.copy(&w); t.imul(fp::RIADZ); + X1.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_HTPC))); + X1.mul(&hint); + + X2.cmove(&X3,1-qr); + D2.cmove(&D,1-qr); + w.cmove(&t,1-qr); + hint.cmove(&X1,1-qr); + + Y.copy(&w.sqrt(Some(&hint))); + Y.mul(&D2); +/* + Y.copy(&w.sqrt(Some(&hint))); + Y.mul(&D2); + + D2.mul(&t); + w.imul(fp::RIADZ); + + X1.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_HTPC))); + hint.mul(&X1); + + Y3.copy(&w.sqrt(Some(&hint))); + Y3.mul(&D2); + + X2.cmove(&X3,1-qr); + Y.cmove(&Y3,1-qr); +*/ + let ne=Y.sign()^sgn; + w.copy(&Y); w.neg(); w.norm(); + Y.cmove(&w,ne); + + if HTC_ISO != 0 { + +/* CAHCZS + let mut k=0; + let isox=HTC_ISO; + let isoy=3*(isox-1)/2; + // xnum + let mut xnum=FP::new_big(&BIG::new_ints(&rom::PC[k])); k+=1; + for _ in 0..isox { + xnum.mul(&X2); + w.copy(&FP::new_big(&BIG::new_ints(&rom::PC[k]))); k+=1; + xnum.add(&w); xnum.norm(); + } + // xden + let mut xden=FP::new_copy(&X2); + w.copy(&FP::new_big(&BIG::new_ints(&rom::PC[k]))); k+=1; + xden.add(&w); xden.norm(); + for _ in 0..isox-2 { + xden.mul(&X2); + w.copy(&FP::new_big(&BIG::new_ints(&rom::PC[k]))); k+=1; + xden.add(&w); xden.norm(); + } + // ynum + let mut ynum=FP::new_big(&BIG::new_ints(&rom::PC[k])); k+=1; + for _ in 0..isoy { + ynum.mul(&X2); + w.copy(&FP::new_big(&BIG::new_ints(&rom::PC[k]))); k+=1; + ynum.add(&w); ynum.norm(); + } + // yden + let mut yden=FP::new_copy(&X2); + w.copy(&FP::new_big(&BIG::new_ints(&rom::PC[k]))); k+=1; + yden.add(&w); yden.norm(); + for _ in 0..isoy-1 { + yden.mul(&X2); + w.copy(&FP::new_big(&BIG::new_ints(&rom::PC[k]))); k+=1; + yden.add(&w); yden.norm(); + } + ynum.mul(&Y); + w.copy(&xnum); w.mul(&yden); + P.x.copy(&w); + w.copy(&ynum); w.mul(&xden); + P.y.copy(&w); + w.copy(&xden); w.mul(&yden); + P.z.copy(&w); + return P; +CAHCZF */ + } else { + let x=X2.redc(); + let y=Y.redc(); + P.copy(&ECP::new_bigs(&x,&y)); + return P; + } + } else { +// Shallue and van de Woestijne +// SQRTM3 not available, so preprocess this out +/* */ + let Z=fp::RIADZ; + X1.copy(&FP::new_int(Z)); + X3.copy(&X1); + A.copy(&ECP::rhs(&X1)); + B.copy(&FP::new_big(&BIG::new_ints(&rom::SQRTM3))); + B.imul(Z); + + t.sqr(); + Y.copy(&A); Y.mul(&t); + t.copy(&one); t.add(&Y); t.norm(); + Y.rsub(&one); Y.norm(); + D.copy(&t); D.mul(&Y); + D.mul(&B); + + w.copy(&A); + FP::tpo(&mut D,&mut w); + + w.mul(&B); + if w.sign()==1 { + w.neg(); + w.norm(); + } + w.mul(&B); + w.mul(&h); w.mul(&Y); w.mul(&D); + + X1.neg(); X1.norm(); X1.div2(); + X2.copy(&X1); + X1.sub(&w); X1.norm(); + X2.add(&w); X2.norm(); + A.dbl(); A.dbl(); A.norm(); + t.sqr(); t.mul(&D); t.sqr(); + A.mul(&t); + X3.add(&A); X3.norm(); + + let mut rhs=ECP::rhs(&X2); + X3.cmove(&X2,rhs.qr(None)); + rhs.copy(&ECP::rhs(&X1)); + X3.cmove(&X1,rhs.qr(None)); + rhs.copy(&ECP::rhs(&X3)); + Y.copy(&rhs.sqrt(None)); + + let ne=Y.sign()^sgn; + w.copy(&Y); w.neg(); w.norm(); + Y.cmove(&w,ne); + + let x=X3.redc(); + let y=Y.redc(); + P.copy(&ECP::new_bigs(&x,&y)); + return P; +/* */ + } + } + P + } + +/* Map byte string to curve point */ + #[allow(non_snake_case)] + pub fn mapit(h: &[u8]) -> ECP { + let q = BIG::new_ints(&rom::MODULUS); + let mut dx = DBIG::frombytes(h); + let x=dx.dmod(&q); + let mut P=ECP::hap2point(&x); + P.cfp(); + P + } + + pub fn generator() -> ECP { + let G: ECP; + let gx = BIG::new_ints(&rom::CURVE_GX); + if CURVETYPE != MONTGOMERY { + let gy = BIG::new_ints(&rom::CURVE_GY); + G = ECP::new_bigs(&gx, &gy); + } else { + G = ECP::new_big(&gx); + } + G + } +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecp2.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecp2.rs new file mode 100644 index 000000000000..09c66c6bcd9a --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/ecp2.rs @@ -0,0 +1,999 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::bn254::big; +use crate::bn254::big::BIG; +use crate::bn254::ecp; +use crate::bn254::fp2::FP2; +use crate::bn254::rom; +use crate::bn254::fp; +use crate::bn254::fp::FP; +use crate::bn254::dbig::DBIG; + +#[derive(Clone)] +pub struct ECP2 { + x: FP2, + y: FP2, + z: FP2, +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for ECP2 { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for ECP2 { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +#[allow(non_snake_case)] +impl ECP2 { + pub fn new() -> ECP2 { + ECP2 { + x: FP2::new(), + y: FP2::new_int(1), + z: FP2::new(), + } + } + #[allow(non_snake_case)] + /* construct this from (x,y) - but set to O if not on curve */ + pub fn new_fp2s(ix: &FP2, iy: &FP2) -> ECP2 { + let mut E = ECP2::new(); + E.x.copy(&ix); + E.y.copy(&iy); + E.z.one(); + E.x.norm(); + + let rhs = ECP2::rhs(&E.x); + let mut y2 = FP2::new_copy(&E.y); + y2.sqr(); + if !y2.equals(&rhs) { + E.inf(); + } + E + } + + /* construct this from x - but set to O if not on curve */ + pub fn new_fp2(ix: &FP2, s:isize) -> ECP2 { + let mut E = ECP2::new(); + let mut h = FP::new(); + E.x.copy(&ix); + E.y.one(); + E.z.one(); + E.x.norm(); + let mut rhs = ECP2::rhs(&E.x); + if rhs.qr(Some(&mut h)) == 1 { + rhs.sqrt(Some(&h)); + if rhs.sign() != s { + rhs.neg(); + } + rhs.reduce(); + E.y.copy(&rhs); + } else { + E.inf(); + } + E + } + + /* Test this=O? */ + pub fn is_infinity(&self) -> bool { + self.x.iszilch() && self.z.iszilch() + } + + /* copy self=P */ + pub fn copy(&mut self, P: &ECP2) { + self.x.copy(&P.x); + self.y.copy(&P.y); + self.z.copy(&P.z); + } + + /* set self=O */ + pub fn inf(&mut self) { + self.x.zero(); + self.y.one(); + self.z.zero(); + } + + /* set self=-self */ + pub fn neg(&mut self) { + self.y.norm(); + self.y.neg(); + self.y.norm(); + } + + /* Conditional move of Q to self dependant on d */ + pub fn cmove(&mut self, Q: &ECP2, d: isize) { + self.x.cmove(&Q.x, d); + self.y.cmove(&Q.y, d); + self.z.cmove(&Q.z, d); + } + + /* return 1 if b==c, no branching */ + fn teq(b: i32, c: i32) -> isize { + let mut x = b ^ c; + x -= 1; // if x=0, x now -1 + ((x >> 31) & 1) as isize + } + + /* Constant time select from pre-computed table */ + pub fn selector(&mut self, W: &[ECP2], b: i32) { + let mut MP = ECP2::new(); + let m = b >> 31; + let mut babs = (b ^ m) - m; + + babs = (babs - 1) / 2; + + self.cmove(&W[0], ECP2::teq(babs, 0)); // conditional move + self.cmove(&W[1], ECP2::teq(babs, 1)); + self.cmove(&W[2], ECP2::teq(babs, 2)); + self.cmove(&W[3], ECP2::teq(babs, 3)); + self.cmove(&W[4], ECP2::teq(babs, 4)); + self.cmove(&W[5], ECP2::teq(babs, 5)); + self.cmove(&W[6], ECP2::teq(babs, 6)); + self.cmove(&W[7], ECP2::teq(babs, 7)); + + MP.copy(self); + MP.neg(); + self.cmove(&MP, (m & 1) as isize); + } + + /* Test if P == Q */ + pub fn equals(&self, Q: &ECP2) -> bool { + let mut a = FP2::new_copy(&self.x); + let mut b = FP2::new_copy(&Q.x); + + a.mul(&Q.z); + b.mul(&self.z); + if !a.equals(&b) { + return false; + } + a.copy(&self.y); + a.mul(&Q.z); + b.copy(&Q.y); + b.mul(&self.z); + if !a.equals(&b) { + return false; + } + + true + } + + /* set to Affine - (x,y,z) to (x,y) */ + pub fn affine(&mut self) { + if self.is_infinity() { + return; + } + let one = FP2::new_int(1); + if self.z.equals(&one) { + return; + } + self.z.inverse(None); + + self.x.mul(&self.z); + self.x.reduce(); + self.y.mul(&self.z); + self.y.reduce(); + self.z.copy(&one); + } + + /* extract affine x as FP2 */ + pub fn getx(&self) -> FP2 { + let mut W = ECP2::new(); + W.copy(self); + W.affine(); + FP2::new_copy(&W.x) + } + + /* extract affine y as FP2 */ + pub fn gety(&self) -> FP2 { + let mut W = ECP2::new(); + W.copy(self); + W.affine(); + FP2::new_copy(&W.y) + } + + /* extract projective x */ + pub fn getpx(&self) -> FP2 { + FP2::new_copy(&self.x) + } + /* extract projective y */ + pub fn getpy(&self) -> FP2 { + FP2::new_copy(&self.y) + } + /* extract projective z */ + pub fn getpz(&self) -> FP2 { + FP2::new_copy(&self.z) + } + + /* convert to byte array */ + pub fn tobytes(&self, b: &mut [u8], compress: bool) { + const MB:usize = 2*(big::MODBYTES as usize); + let mut t: [u8; MB] = [0; MB]; + let mut alt=false; + let mut W = ECP2::new(); + W.copy(self); + W.affine(); + W.x.tobytes(&mut t); + + if (fp::MODBITS-1)%8 <= 4 && ecp::ALLOW_ALT_COMPRESS { + alt=true; + } + if alt { + for i in 0..MB { + b[i]=t[i] + } + if !compress { + W.y.tobytes(&mut t); + for i in 0..MB { + b[i+MB]=t[i]; + } + } else { + b[0]|=0x80; + if W.y.islarger()==1 { + b[0]|=0x20; + } + } + + } else { + for i in 0..MB { + b[i+1]=t[i]; + } + if !compress { + b[0]=0x04; + W.y.tobytes(&mut t); + for i in 0..MB { + b[i+MB+1]=t[i]; + } + } else { + b[0]=0x02; + if W.y.sign() == 1 { + b[0]=0x03; + } + } + } + } + + /* convert from byte array to point */ + pub fn frombytes(b: &[u8]) -> ECP2 { + const MB:usize = 2*(big::MODBYTES as usize); + let mut t: [u8; MB] = [0; MB]; + let typ=b[0] as isize; + let mut alt=false; + + if (fp::MODBITS-1)%8 <= 4 && ecp::ALLOW_ALT_COMPRESS { + alt=true; + } + + if alt { + for i in 0..MB { + t[i]=b[i]; + } + t[0]&=0x1f; + let rx=FP2::frombytes(&t); + if (b[0]&0x80)==0 { + for i in 0..MB { + t[i]=b[i+MB]; + } + let ry=FP2::frombytes(&t); + ECP2::new_fp2s(&rx,&ry) + } else { + let sgn=(b[0]&0x20)>>5; + let mut P=ECP2::new_fp2(&rx,0); + let cmp=P.y.islarger(); + if (sgn == 1 && cmp != 1) || (sgn == 0 && cmp == 1) { + P.neg(); + } + P + } + } else { + for i in 0..MB { + t[i]=b[i+1]; + } + let rx=FP2::frombytes(&t); + if typ == 0x04 { + for i in 0..MB { + t[i]=b[i+MB+1]; + } + let ry=FP2::frombytes(&t); + ECP2::new_fp2s(&rx,&ry) + } else { + ECP2::new_fp2(&rx,typ&1) + } + } + } + + /* convert this to hex string */ + #[cfg(feature = "std")] + pub fn tostring(&self) -> String { + let mut W = ECP2::new(); + W.copy(self); + W.affine(); + if W.is_infinity() { + String::from("infinity") + } else { + format!("({},{})", W.x.tostring(), W.y.tostring()) + } + } + + /* Calculate RHS of twisted curve equation x^3+B/i */ + pub fn rhs(x: &FP2) -> FP2 { + let mut r = FP2::new_copy(x); + r.sqr(); + let mut b = FP2::new_big(&BIG::new_ints(&rom::CURVE_B)); + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + b.div_ip(); + } + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + b.norm(); + b.mul_ip(); + b.norm(); + } + + r.mul(x); + r.add(&b); + + r.reduce(); + r + } + + /* self+=self */ + pub fn dbl(&mut self) -> isize { + let mut iy = FP2::new_copy(&self.y); + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + iy.mul_ip(); + iy.norm(); + } + + let mut t0 = FP2::new_copy(&self.y); //***** Change + t0.sqr(); + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + t0.mul_ip(); + } + let mut t1 = FP2::new_copy(&iy); + t1.mul(&self.z); + let mut t2 = FP2::new_copy(&self.z); + t2.sqr(); + + self.z.copy(&t0); + self.z.add(&t0); + self.z.norm(); + self.z.dbl(); + self.z.dbl(); + self.z.norm(); + + t2.imul(3 * rom::CURVE_B_I); + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + t2.mul_ip(); + t2.norm(); + } + let mut x3 = FP2::new_copy(&t2); + x3.mul(&self.z); + + let mut y3 = FP2::new_copy(&t0); + + y3.add(&t2); + y3.norm(); + self.z.mul(&t1); + t1.copy(&t2); + t1.add(&t2); + t2.add(&t1); + t2.norm(); + t0.sub(&t2); + t0.norm(); //y^2-9bz^2 + y3.mul(&t0); + y3.add(&x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2 + t1.copy(&self.x); + t1.mul(&iy); // + self.x.copy(&t0); + self.x.norm(); + self.x.mul(&t1); + self.x.dbl(); //(y^2-9bz^2)xy2 + + self.x.norm(); + self.y.copy(&y3); + self.y.norm(); + + 1 + } + + /* self+=Q - return 0 for add, 1 for double, -1 for O */ + pub fn add(&mut self, Q: &ECP2) -> isize { + let b = 3 * rom::CURVE_B_I; + let mut t0 = FP2::new_copy(&self.x); + t0.mul(&Q.x); // x.Q.x + let mut t1 = FP2::new_copy(&self.y); + t1.mul(&Q.y); // y.Q.y + + let mut t2 = FP2::new_copy(&self.z); + t2.mul(&Q.z); + let mut t3 = FP2::new_copy(&self.x); + t3.add(&self.y); + t3.norm(); //t3=X1+Y1 + let mut t4 = FP2::new_copy(&Q.x); + t4.add(&Q.y); + t4.norm(); //t4=X2+Y2 + t3.mul(&t4); //t3=(X1+Y1)(X2+Y2) + t4.copy(&t0); + t4.add(&t1); //t4=X1.X2+Y1.Y2 + + t3.sub(&t4); + t3.norm(); + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + t3.mul_ip(); + t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1 + } + t4.copy(&self.y); + t4.add(&self.z); + t4.norm(); //t4=Y1+Z1 + let mut x3 = FP2::new_copy(&Q.y); + x3.add(&Q.z); + x3.norm(); //x3=Y2+Z2 + + t4.mul(&x3); //t4=(Y1+Z1)(Y2+Z2) + x3.copy(&t1); // + x3.add(&t2); //X3=Y1.Y2+Z1.Z2 + + t4.sub(&x3); + t4.norm(); + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + t4.mul_ip(); + t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1 + } + x3.copy(&self.x); + x3.add(&self.z); + x3.norm(); // x3=X1+Z1 + let mut y3 = FP2::new_copy(&Q.x); + y3.add(&Q.z); + y3.norm(); // y3=X2+Z2 + x3.mul(&y3); // x3=(X1+Z1)(X2+Z2) + y3.copy(&t0); + y3.add(&t2); // y3=X1.X2+Z1+Z2 + y3.rsub(&x3); + y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1 + + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + t0.mul_ip(); + t0.norm(); // x.Q.x + t1.mul_ip(); + t1.norm(); // y.Q.y + } + x3.copy(&t0); + x3.add(&t0); + t0.add(&x3); + t0.norm(); + t2.imul(b); + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + t2.mul_ip(); + t2.norm(); + } + let mut z3 = FP2::new_copy(&t1); + z3.add(&t2); + z3.norm(); + t1.sub(&t2); + t1.norm(); + y3.imul(b); + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + y3.mul_ip(); + y3.norm(); + } + x3.copy(&y3); + x3.mul(&t4); + t2.copy(&t3); + t2.mul(&t1); + x3.rsub(&t2); + y3.mul(&t0); + t1.mul(&z3); + y3.add(&t1); + t0.mul(&t3); + z3.mul(&t4); + z3.add(&t0); + + self.x.copy(&x3); + self.x.norm(); + self.y.copy(&y3); + self.y.norm(); + self.z.copy(&z3); + self.z.norm(); + + 0 + } + + /* set this-=Q */ + pub fn sub(&mut self, Q: &ECP2) -> isize { + let mut NQ = ECP2::new(); + NQ.copy(Q); + NQ.neg(); + self.add(&NQ) + } + + /* set this*=q, where q is Modulus, using Frobenius */ + pub fn frob(&mut self, x: &FP2) { + let mut x2 = FP2::new_copy(x); + x2.sqr(); + self.x.conj(); + self.y.conj(); + self.z.conj(); + self.z.reduce(); + self.x.mul(&x2); + self.y.mul(&x2); + self.y.mul(x); + } + + /* self*=e */ + pub fn mul(&self, e: &BIG) -> ECP2 { + /* fixed size windows */ + let mut mt = BIG::new(); + let mut t = BIG::new(); + let mut P = ECP2::new(); + let mut Q = ECP2::new(); + let mut C = ECP2::new(); + + if self.is_infinity() { + return P; + } + + let mut W: [ECP2; 8] = [ + ECP2::new(), + ECP2::new(), + ECP2::new(), + ECP2::new(), + ECP2::new(), + ECP2::new(), + ECP2::new(), + ECP2::new(), + ]; + + const CT: usize = 1 + (big::NLEN * (big::BASEBITS as usize) + 3) / 4; + let mut w: [i8; CT] = [0; CT]; + + /* precompute table */ + Q.copy(&self); + Q.dbl(); + + W[0].copy(&self); + + for i in 1..8 { + C.copy(&W[i - 1]); + W[i].copy(&C); + W[i].add(&Q); + } + + /* make exponent odd - add 2P if even, P if odd */ + t.copy(&e); + let s = t.parity(); + t.inc(1); + t.norm(); + let ns = t.parity(); + mt.copy(&t); + mt.inc(1); + mt.norm(); + t.cmove(&mt, s); + Q.cmove(&self, ns); + C.copy(&Q); + + let nb = 1 + (t.nbits() + 3) / 4; + + /* convert exponent to signed 4-bit window */ + for i in 0..nb { + w[i] = (t.lastbits(5) - 16) as i8; + t.dec(w[i] as isize); + t.norm(); + t.fshr(4); + } + w[nb] = (t.lastbits(5)) as i8; + + //P.copy(&W[((w[nb] as usize) - 1) / 2]); + + P.selector(&W, w[nb] as i32); + for i in (0..nb).rev() { + Q.selector(&W, w[i] as i32); + P.dbl(); + P.dbl(); + P.dbl(); + P.dbl(); + P.add(&Q); + } + P.sub(&C); + P + } + + #[allow(non_snake_case)] + pub fn cfp(&mut self) { + let mut X = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + X.inverse(None); + X.norm(); + } + let x = BIG::new_ints(&rom::CURVE_BNX); + // Faster Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez + // Q -> xQ + F(3xQ) + F(F(xQ)) + F(F(F(Q))). + if ecp::CURVE_PAIRING_TYPE == ecp::BN { + let mut T = self.mul(&x); + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + T.neg(); + } + let mut K = ECP2::new(); + K.copy(&T); + K.dbl(); + K.add(&T); + + K.frob(&X); + self.frob(&X); + self.frob(&X); + self.frob(&X); + self.add(&T); + self.add(&K); + T.frob(&X); + T.frob(&X); + self.add(&T); + } + // Efficient hash maps to G2 on BLS curves - Budroni, Pintore + // Q -> x2Q -xQ -Q +F(xQ -Q) +F(F(2Q)) + if ecp::CURVE_PAIRING_TYPE > ecp::BN { + let mut xQ = self.mul(&x); + let mut x2Q = xQ.mul(&x); + + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + xQ.neg(); + } + x2Q.sub(&xQ); + x2Q.sub(&self); + + xQ.sub(&self); + xQ.frob(&X); + + self.dbl(); + self.frob(&X); + self.frob(&X); + + self.add(&x2Q); + self.add(&xQ); + } + } + + + /* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */ + // Bos & Costello https://eprint.iacr.org/2013/458.pdf + // Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf + // Side channel attack secure + + pub fn mul4(Q: &[ECP2], u: &[BIG]) -> ECP2 { + let mut W = ECP2::new(); + let mut P = ECP2::new(); + + let mut T: [ECP2; 8] = [ + ECP2::new(), + ECP2::new(), + ECP2::new(), + ECP2::new(), + ECP2::new(), + ECP2::new(), + ECP2::new(), + ECP2::new(), + ]; + + let mut mt = BIG::new(); + + let mut t: [BIG; 4] = [ + BIG::new_copy(&u[0]), + BIG::new_copy(&u[1]), + BIG::new_copy(&u[2]), + BIG::new_copy(&u[3]), + ]; + + const CT: usize = 1 + big::NLEN * (big::BASEBITS as usize); + let mut w: [i8; CT] = [0; CT]; + let mut s: [i8; CT] = [0; CT]; + + for i in 0..4 { + t[i].norm(); + } + + T[0].copy(&Q[0]); + W.copy(&T[0]); + T[1].copy(&W); + T[1].add(&Q[1]); // Q[0]+Q[1] + T[2].copy(&W); + T[2].add(&Q[2]); + W.copy(&T[1]); // Q[0]+Q[2] + T[3].copy(&W); + T[3].add(&Q[2]); + W.copy(&T[0]); // Q[0]+Q[1]+Q[2] + T[4].copy(&W); + T[4].add(&Q[3]); + W.copy(&T[1]); // Q[0]+Q[3] + T[5].copy(&W); + T[5].add(&Q[3]); + W.copy(&T[2]); // Q[0]+Q[1]+Q[3] + T[6].copy(&W); + T[6].add(&Q[3]); + W.copy(&T[3]); // Q[0]+Q[2]+Q[3] + T[7].copy(&W); + T[7].add(&Q[3]); // Q[0]+Q[1]+Q[2]+Q[3] + + // Make it odd + let pb = 1 - t[0].parity(); + t[0].inc(pb); + t[0].norm(); + + // Number of bits + mt.zero(); + for i in 0..4 { + mt.or(&t[i]); + } + + let nb = 1 + mt.nbits(); + + // Sign pivot + + s[nb - 1] = 1; + for i in 0..nb - 1 { + t[0].fshr(1); + s[i] = (2 * t[0].parity() - 1) as i8; + } + + // Recoded exponent + for i in 0..nb { + w[i] = 0; + let mut k = 1; + for j in 1..4 { + let bt = s[i] * (t[j].parity() as i8); + t[j].fshr(1); + t[j].dec((bt >> 1) as isize); + t[j].norm(); + w[i] += bt * (k as i8); + k *= 2; + } + } + + // Main loop + P.selector(&T, (2 * w[nb - 1] + 1) as i32); + for i in (0..nb - 1).rev() { + P.dbl(); + W.selector(&T, (2 * w[i] + s[i]) as i32); + P.add(&W); + } + + // apply correction + W.copy(&P); + W.sub(&Q[0]); + P.cmove(&W, pb); + + P + } + +/* Hunt and Peck a BIG to a curve point */ + #[allow(non_snake_case)] + pub fn hap2point(h: &BIG) -> ECP2 { + let mut Q: ECP2; + let one = BIG::new_int(1); + let mut x =BIG::new_copy(&h); + loop { + let X = FP2::new_bigs(&one, &x); + Q = ECP2::new_fp2(&X,0); + if !Q.is_infinity() { + break; + } + x.inc(1); + x.norm(); + } + Q + } + +/* Constant time Map to Point */ + #[allow(unreachable_code)] + #[allow(non_snake_case)] + pub fn map2point(H: &FP2) -> ECP2 { + let mut T=FP2::new_copy(H); /**/ + let sgn=T.sign(); /**/ + if ecp::HTC_ISO_G2 == 0 { + // Shallue and van de Woestijne +/* */ + let mut NY=FP2::new_int(1); + let mut Z=FP::new_int(fp::RIADZG2A); + let mut X1=FP2::new_fp(&Z); + let mut X3=FP2::new_copy(&X1); + let mut A=ECP2::rhs(&X1); + let mut W=FP2::new_copy(&A); + + if fp::RIADZG2A==-1 && fp::RIADZG2B==0 && ecp::SEXTIC_TWIST == ecp::M_TYPE && rom::CURVE_B_I==4 { + W.copy(&FP2::new_ints(2,1)); + } else { + W.sqrt(None); + } + let s = FP::new_big(&BIG::new_ints(&rom::SQRTM3)); + Z.mul(&s); + + T.sqr(); + let mut Y=FP2::new_copy(&A); Y.mul(&T); + T.copy(&NY); T.add(&Y); T.norm(); + Y.rsub(&NY); Y.norm(); + NY.copy(&T); NY.mul(&Y); + + NY.pmul(&Z); + NY.inverse(None); + + W.pmul(&Z); + if W.sign()==1 { + W.neg(); + W.norm(); + } + W.pmul(&Z); + W.mul(&H); W.mul(&Y); W.mul(&NY); + + X1.neg(); X1.norm(); X1.div2(); + let mut X2=FP2::new_copy(&X1); + X1.sub(&W); X1.norm(); + X2.add(&W); X2.norm(); + A.dbl(); A.dbl(); A.norm(); + T.sqr(); T.mul(&NY); T.sqr(); + A.mul(&T); + X3.add(&A); X3.norm(); + + Y.copy(&ECP2::rhs(&X2)); + X3.cmove(&X2,Y.qr(None)); + Y.copy(&ECP2::rhs(&X1)); + X3.cmove(&X1,Y.qr(None)); + Y.copy(&ECP2::rhs(&X3)); + Y.sqrt(None); + + let ne=Y.sign()^sgn; + W.copy(&Y); W.neg(); W.norm(); + Y.cmove(&W,ne); + + return ECP2::new_fp2s(&X3,&Y); +/* */ + } else { +/* CAHCZS + let NY=FP2::new_int(1); + let Ad=FP2::new_bigs(&BIG::new_ints(&rom::CURVE_ADR),&BIG::new_ints(&rom::CURVE_ADI)); + let Bd=FP2::new_bigs(&BIG::new_ints(&rom::CURVE_BDR),&BIG::new_ints(&rom::CURVE_BDI)); + let ZZ=FP2::new_ints(fp::RIADZG2A,fp::RIADZG2B); + let mut hint=FP::new(); + + T.sqr(); + T.mul(&ZZ); + let mut W=FP2::new_copy(&T); + W.add(&NY); W.norm(); + + W.mul(&T); + let mut D=FP2::new_copy(&Ad); + D.mul(&W); + + W.add(&NY); W.norm(); + W.mul(&Bd); + W.neg(); W.norm(); + + let mut X2=FP2::new_copy(&W); + let mut X3=FP2::new_copy(&T); + X3.mul(&X2); + + let mut GX1=FP2::new_copy(&X2); GX1.sqr(); + let mut D2=FP2::new_copy(&D); D2.sqr(); + + W.copy(&Ad); W.mul(&D2); GX1.add(&W); GX1.norm(); GX1.mul(&X2); D2.mul(&D); W.copy(&Bd); W.mul(&D2); GX1.add(&W); GX1.norm(); // x^3+Ax+b + + W.copy(&GX1); W.mul(&D); + let qr=W.qr(Some(&mut hint)); + D.copy(&W); D.inverse(Some(&hint)); + D.mul(&GX1); + X2.mul(&D); + X3.mul(&D); + T.mul(&H); + D2.copy(&D); D2.sqr(); + + D.copy(&D2); D.mul(&T); + T.copy(&W); T.mul(&ZZ); + + let mut s=FP::new_big(&BIG::new_ints(&rom::CURVE_HTPC2)); + s.mul(&hint); + + X2.cmove(&X3,1-qr); + W.cmove(&T,1-qr); + D2.cmove(&D,1-qr); + hint.cmove(&s,1-qr); + + let mut Y=FP2::new_copy(&W); Y.sqrt(Some(&hint)); + Y.mul(&D2); + + let ne=Y.sign()^sgn; + W.copy(&Y); W.neg(); W.norm(); + Y.cmove(&W,ne); + + let mut k=0; + let isox=ecp::HTC_ISO_G2; + let isoy=3*(isox-1)/2; + + // xnum + let mut xnum=FP2::new_bigs(&BIG::new_ints(&rom::PCR[k]),&BIG::new_ints(&rom::PCI[k])); k+=1; + for _ in 0..isox { + xnum.mul(&X2); + xnum.add(&FP2::new_bigs(&BIG::new_ints(&rom::PCR[k]),&BIG::new_ints(&rom::PCI[k]))); k+=1; + xnum.norm(); + } + //xden + let mut xden=FP2::new_copy(&X2); + xden.add(&FP2::new_bigs(&BIG::new_ints(&rom::PCR[k]),&BIG::new_ints(&rom::PCI[k]))); k+=1; + xden.norm(); + for _ in 0..isox-2 { + xden.mul(&X2); + xden.add(&FP2::new_bigs(&BIG::new_ints(&rom::PCR[k]),&BIG::new_ints(&rom::PCI[k]))); k+=1; + xden.norm(); + } + //ynum + let mut ynum=FP2::new_bigs(&BIG::new_ints(&rom::PCR[k]),&BIG::new_ints(&rom::PCI[k])); k+=1; + for _ in 0..isoy { + ynum.mul(&X2); + ynum.add(&FP2::new_bigs(&BIG::new_ints(&rom::PCR[k]),&BIG::new_ints(&rom::PCI[k]))); k+=1; + ynum.norm(); + } + //yden + let mut yden=FP2::new_copy(&X2); + yden.add(&FP2::new_bigs(&BIG::new_ints(&rom::PCR[k]),&BIG::new_ints(&rom::PCI[k]))); k+=1; + yden.norm(); + for _ in 0..isoy-1 { + yden.mul(&X2); + yden.add(&FP2::new_bigs(&BIG::new_ints(&rom::PCR[k]),&BIG::new_ints(&rom::PCI[k]))); k+=1; + yden.norm(); + } + ynum.mul(&Y); + + let mut Q=ECP2::new(); + T.copy(&xnum); T.mul(&yden); + Q.x.copy(&T); + T.copy(&ynum); T.mul(&xden); + Q.y.copy(&T); + T.copy(&xden); T.mul(&yden); + Q.z.copy(&T); + return Q; +CAHCZF */ + + } + ECP2::new() + } + +/* Map byte string to curve point */ + #[allow(non_snake_case)] + pub fn mapit(h: &[u8]) -> ECP2 { + let q = BIG::new_ints(&rom::MODULUS); + let mut dx = DBIG::frombytes(h); + let x=dx.dmod(&q); + let mut P=ECP2::hap2point(&x); + P.cfp(); + P + } + + pub fn generator() -> ECP2 { + ECP2::new_fp2s( + &FP2::new_bigs( + &BIG::new_ints(&rom::CURVE_PXA), + &BIG::new_ints(&rom::CURVE_PXB), + ), + &FP2::new_bigs( + &BIG::new_ints(&rom::CURVE_PYA), + &BIG::new_ints(&rom::CURVE_PYB), + ), + ) + } +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/eddsa.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/eddsa.rs new file mode 100644 index 000000000000..202c88672ebc --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/eddsa.rs @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* ECDH/ECIES/ECDSA API Functions */ + +use crate::bn254::big; +use crate::bn254::big::BIG; +use crate::bn254::fp; +use crate::bn254::fp::FP; +use crate::bn254::dbig::DBIG; +use crate::bn254::ecp; +use crate::bn254::ecp::ECP; +use crate::bn254::rom; +use crate::rand::RAND; +use crate::hash512::HASH512; +use crate::sha3; +use crate::sha3::SHA3; + +pub const INVALID_PUBLIC_KEY: isize = -2; +pub const ERROR: isize = -3; +//pub const INVALID: isize = -4; +pub const EFS: usize = big::MODBYTES as usize; +pub const EGS: usize = big::MODBYTES as usize; + +fn rfc7748(r: &mut BIG) { + let mut lg=0; + let mut t=BIG::new_int(1); + let mut c=rom::CURVE_COF_I; + while c!=1 { + lg+=1; + c/=2; + } + let n=(8*EGS-lg+1) as usize; + r.mod2m(n); + t.shl(n); + r.add(&t); + c=r.lastbits(lg as usize); + r.dec(c); +} + +// reverse first n bytes of buff - for little endian +fn reverse(n: usize,buff: &mut [u8]) { + for i in 0..n/2 { + let ch = buff[i]; + buff[i] = buff[n - i - 1]; + buff[n - i - 1] = ch; + } +} + +// dom - domain function +fn dom(ds: &str,ph: bool,cl: usize,domain: &mut [u8]) -> usize { + let dsb = ds.as_bytes(); + let n=dsb.len(); + for i in 0..n { + domain[i]=dsb[i]; + } + if ph { + domain[n]=1; + } else { + domain[n]=0; + } + domain[n+1]=cl as u8; + return n+2; +} + +fn h(s: &[u8],digest: &mut [u8]) -> usize { + if ecp::AESKEY<=16 { // for ed25519? + let mut sh=HASH512::new(); + for i in 0..s.len() { + sh.process(s[i]); + } + let hs = sh.hash(); + for i in 0..64 { + digest[i]=hs[i]; + } + return 64; + } else { // for ed448? + let mut sh=SHA3::new(sha3::SHAKE256); + for i in 0..s.len() { + sh.process(s[i]); + } + sh.shake(digest,2*s.len()); + return 2*s.len(); + } +} + +fn h2(ph: bool,ctx: Option<&[u8]>,r: &[u8],q: &[u8],m: &[u8]) -> DBIG { + let b=q.len(); + let mut cl=0; + if let Some(sctx) = ctx { + cl=sctx.len(); + } + let mut domain: [u8; 64] = [0; 64]; + if ecp::AESKEY<=16 { // Ed25519?? + let mut sh=HASH512::new(); + if ph || cl>0 { // if not prehash and no context, omit dom2() + let dl=dom("Sigbn254 no bn254 collisions",ph,cl,&mut domain); + for i in 0..dl { + sh.process(domain[i]); + } + if let Some(sctx) = ctx { + for i in 0..cl { + sh.process(sctx[i]); + } + } + } + for i in 0..b { + sh.process(r[i]); + } + for i in 0..b { + sh.process(q[i]); + } + for i in 0..m.len() { + sh.process(m[i]); + } + let mut h=sh.hash(); + reverse(64,&mut h); + return DBIG::frombytes(&h); + } else { // for ed448? + let dl=dom("Sigbn254",ph,cl,&mut domain); + let mut h: [u8; 128] = [0; 128]; + let mut sh=SHA3::new(sha3::SHAKE256); + for i in 0..dl { + sh.process(domain[i]); + } + if let Some(sctx) = ctx { + for i in 0..cl { + sh.process(sctx[i]); + } + } + for i in 0..b { + sh.process(r[i]); + } + for i in 0..b { + sh.process(q[i]); + } + for i in 0..m.len() { + sh.process(m[i]); + } + + sh.shake(&mut h,2*b); + reverse(2*b,&mut h[0..2*b]); + return DBIG::frombytes(&h[0..2*b]); + } +} + +fn getr(ph: bool,b: usize,digest: &[u8],ctx: Option<&[u8]>,m: &[u8]) -> DBIG { + let mut cl=0; + if let Some(sctx) = ctx { + cl=sctx.len(); + } + let mut domain: [u8; 64] = [0; 64]; + + if ecp::AESKEY<=16 { // Ed25519?? + let mut sh=HASH512::new(); + if ph || cl>0 { // if not prehash and no context, omit dom2() + let dl=dom("Sigbn254 no bn254 collisions",ph,cl,&mut domain); + for i in 0..dl { + sh.process(domain[i]); + } + if let Some(sctx) = ctx { + for i in 0..cl { + sh.process(sctx[i]); + } + } + } + for i in b..2*b { + sh.process(digest[i]); + } + for i in 0..m.len() { + sh.process(m[i]); + } + let mut h=sh.hash(); + reverse(64,&mut h); + return DBIG::frombytes(&h); + } else { // for ed448? + let dl=dom("Sigbn254",ph,cl,&mut domain); + let mut h: [u8; 128] = [0; 128]; + let mut sh=SHA3::new(sha3::SHAKE256); + for i in 0..dl { + sh.process(domain[i]); + } + if let Some(sctx) = ctx { + for i in 0..cl { + sh.process(sctx[i]); + } + } + for i in b..2*b { + sh.process(digest[i]); + } + for i in 0..m.len() { + sh.process(m[i]); + } + sh.shake(&mut h,2*b); + reverse(2*b,&mut h[0..2*b]); + return DBIG::frombytes(&h[0..2*b]); + } +} + +// encode integer (little endian) +fn encode_int(x: &BIG,w: &mut [u8]) -> usize { + let mut index=0; + if 8*EFS==fp::MODBITS { + index=1; // extra byte needed for compression + } + let b=EFS+index; + + w[0]=0; + x.tobytearray(w,index); + reverse(b,w); + return b; +} + +// encode point + #[allow(non_snake_case)] +fn encode(P: &ECP,w: &mut [u8]) { + let mut index=0; + if 8*EFS==fp::MODBITS { + index=1; // extra byte needed for compression + } + let b=EFS+index; + + let x=P.getx(); + let y=P.gety(); + encode_int(&y,w); + w[b-1]|=(x.parity()<<7) as u8; +} + +// get sign +fn getsign(x: &[u8]) -> isize{ + let mut index=0; + if 8*EFS==fp::MODBITS { + index=1; // extra byte needed for compression + } + let b=EFS+index; + + if (x[b-1]&0x80)!=0 { + return 1; + } else { + return 0; + } +} + +// decode integer (little endian) +fn decode_int(strip_sign: bool,ei: &[u8]) -> BIG { + let mut index=0; + if 8*EFS==fp::MODBITS { + index=1; // extra byte needed for compression + } + let b=EFS+index; + + let mut r: [u8; EFS+1] = [0; EFS+1]; + + for i in 0..b { + r[i]=ei[i]; + } + reverse(b,&mut r); + + if strip_sign { + r[0]&=0x7f; + } + return BIG::frombytearray(&r,index); +} + +// decode compressed point +fn decode(w: &[u8]) -> ECP { + let sign=getsign(w); // lsb of x + let y=decode_int(true,w); + let one = FP::new_int(1); + let mut hint=FP::new(); + let mut x=FP::new_big(&y); x.sqr(); + let mut d=FP::new_copy(&x); + x.sub(&one); + x.norm(); + let mut t = FP::new_big(&BIG::new_ints(&rom::CURVE_B)); + d.mul(&t); + if ecp::CURVE_A==1 { + d.sub(&one); + } + if ecp::CURVE_A==-1 { + d.add(&one); + } + d.norm(); +// inverse square root trick for sqrt(x/d) + t.copy(&x); + t.sqr(); + x.mul(&t); + x.mul(&d); + if x.qr(Some(&mut hint))!=1 { + return ECP::new(); + } + d.copy(&x.sqrt(Some(&hint))); + x.inverse(Some(&hint)); + x.mul(&d); + x.mul(&t); + x.reduce(); + if x.redc().parity()!=sign { + x.neg(); + } + x.norm(); + return ECP::new_bigs(&x.redc(),&y); +} + +#[allow(non_snake_case)] +fn key_pair_regenerate(d: &[u8], q: &mut [u8]) { + let mut index=0; + if 8*EFS==fp::MODBITS { + index=1; // extra byte needed for compression + } + let b=EFS+index; + let mut G=ECP::generator(); + + let mut digest: [u8; 128] = [0; 128]; + h(d,&mut digest); + +// reverse bytes for little endian + reverse(b,&mut digest); + let mut s=BIG::frombytearray(&digest,index); + rfc7748(&mut s); + G.copy(&G.mul(&s)); + encode(&G,q); +} + +/* Calculate a public/private EC GF(p) key pair w,s where W=s.G mod EC(p), + * where s is the secret key and W is the public key + * and G is fixed generator. + * If RNG is NULL then the private key is provided externally in s + * otherwise it is generated randomly internally */ +#[allow(non_snake_case)] +pub fn key_pair_generate(rng: Option<&mut RAND>, d: &mut [u8], q: &mut [u8]) -> isize { + let res = 0; + let mut index=0; + if 8*EFS==fp::MODBITS { + index=1; // extra byte needed for compression + } + let b=EFS+index; + let mut G=ECP::generator(); + + if let Some(srng) = rng { + for i in 0..b { + d[i]=srng.getbyte(); + } + } + let mut digest: [u8; 128] = [0; 128]; + h(d,&mut digest); + +// reverse bytes for little endian + reverse(b,&mut digest); + let mut s=BIG::frombytearray(&digest,index); + rfc7748(&mut s); + G.copy(&G.mul(&s)); + encode(&G,q); + return res; +} + +// Generate a signature using key pair (d,q) on message m +// Set ph=true if message has already been pre-hashed +// if ph=false, then context should be NULL for ed25519. However RFC8032 mode ed25519ctx is supported by supplying a non-NULL or non-empty context +#[allow(non_snake_case)] +pub fn signature(ph: bool,d: &[u8], ctx: Option<&[u8]>,m: &[u8], sig: &mut [u8]) ->isize { + let mut digest: [u8; 128] = [0; 128]; + let mut q: [u8; EFS+1] = [0; EFS+1]; // public key + h(d,&mut digest); // hash of private key + let mut res = 0; + let mut index=0; + if 8*EFS==fp::MODBITS { + index=1; // extra byte needed for compression + } + let b=EFS+index; + let r = BIG::new_ints(&rom::CURVE_ORDER); + + key_pair_regenerate(d,&mut q); + let qs=&q[0..b]; + + if d.len()!=qs.len() || d.len()!=b { + res=INVALID_PUBLIC_KEY; + } + if res==0 { + let mut dr=getr(ph,b,&digest,ctx,m); + let sr=dr.dmod(&r); + let R=ECP::generator().mul(&sr); + encode(&R,&mut sig[0..b]); + reverse(b,&mut digest); + let mut s=BIG::frombytearray(&digest,index); + rfc7748(&mut s); + dr=h2(ph,ctx,sig,&qs,m); + let sd=dr.dmod(&r); + encode_int(&BIG::modadd(&sr,&BIG::modmul(&s,&sd,&r),&r),&mut sig[b..2*b]); + } + return res; +} + +// verify a signature using public key q +// same context (if any) as used for signature +#[allow(non_snake_case)] +pub fn verify(ph: bool, q: &[u8], ctx: Option<&[u8]>,m: &[u8], sig: &[u8]) ->bool { + + let mut index=0; + if 8*EFS==fp::MODBITS { + index=1; // extra byte needed for compression + } + let b=EFS+index; + + let mut lg=0; + let mut c=rom::CURVE_COF_I; + while c!=1 { + lg+=1; + c/=2; + } + let r = BIG::new_ints(&rom::CURVE_ORDER); + let mut R=decode(&sig[0..b]); + + if R.is_infinity() { + return false; + } + + let t=decode_int(false,&sig[b..2*b]); + + if BIG::comp(&t,&r)>=0 { + return false; + } + let mut du=h2(ph,ctx,&sig,q,m); + let su=du.dmod(&r); + + let mut G=ECP::generator(); + let mut QD=decode(&q); + if QD.is_infinity() { + return false; + } + QD.neg(); + for _ in 0..lg { // use cofactor 2^c + G.dbl(); QD.dbl(); R.dbl(); + } + + if !G.mul2(&t,&QD,&su).equals(&R) { + return false; + } + return true; +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp.rs new file mode 100644 index 000000000000..17caff2ed023 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp.rs @@ -0,0 +1,825 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::arch; +use crate::arch::Chunk; +use crate::bn254::big; +use crate::bn254::big::BIG; +use crate::bn254::dbig::DBIG; +use crate::bn254::rom; + +use crate::rand::RAND; + +#[derive(Copy, Clone)] +pub struct FP { + pub x: BIG, + pub xes: i32, +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for FP { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for FP { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +pub const NOT_SPECIAL: usize = 0; +pub const PSEUDO_MERSENNE: usize = 1; +pub const MONTGOMERY_FRIENDLY: usize = 2; +pub const GENERALISED_MERSENNE: usize = 3; + +pub const NEGATOWER: usize = 0; +pub const POSITOWER: usize = 1; + +pub const MODBITS:usize = 254; /* Number of bits in Modulus */ +pub const PM1D2: usize = 1; /* Modulus mod 8 */ +pub const RIADZ: isize = -1; /* Z for hash-to-point */ +pub const RIADZG2A: isize = -1; /* G2 Z for hash-to-point */ +pub const RIADZG2B: isize = 0; /* G2 Z for hash-to-point */ +pub const MODTYPE:usize=NOT_SPECIAL; +pub const QNRI:usize=0; /* Fp2 QNR 2^i+sqrt(-1) */ +pub const TOWER:usize=NEGATOWER; /* Tower type */ + +pub const FEXCESS:i32 = ((1 as i32)<<26)-1; +pub const OMASK: Chunk = (-1) << (MODBITS % big::BASEBITS); +pub const TBITS: usize = MODBITS % big::BASEBITS; // Number of active bits in top word +pub const TMASK: Chunk = (1 << TBITS) - 1; + +pub const BIG_ENDIAN_SIGN: bool = false; + +impl FP { + /* Constructors */ + pub const fn new() -> FP { + FP { + x: BIG::new(), + xes: 1, + } + } + + pub fn new_int(a: isize) -> FP { + let mut f = FP::new(); + if a<0 { + let mut m = BIG::new_ints(&rom::MODULUS); + m.inc(a); m.norm(); + f.x.copy(&m); + } else { + f.x.inc(a); + } + f.nres(); + f + } + + pub fn new_copy(y: &FP) -> FP { + let mut f = FP::new(); + f.x.copy(&(y.x)); + f.xes = y.xes; + f + } + + pub fn new_big(y: &BIG) -> FP { + let mut f = FP::new(); + f.x.copy(y); + f.nres(); + f + } + + pub fn new_rand(rng: &mut RAND) -> FP { + let m = BIG::new_ints(&rom::MODULUS); + let w = BIG::randomnum(&m,rng); + FP::new_big(&w) + } + + pub fn nres(&mut self) { + if MODTYPE != PSEUDO_MERSENNE && MODTYPE != GENERALISED_MERSENNE { + let r = BIG::new_ints(&rom::R2MODP); + let mut d = BIG::mul(&(self.x), &r); + self.x.copy(&FP::modulo(&mut d)); + self.xes = 2; + } else { + let m = BIG::new_ints(&rom::MODULUS); + self.x.rmod(&m); + self.xes = 1; + } + } + + /* convert back to regular form */ + pub fn redc(&self) -> BIG { + if MODTYPE != PSEUDO_MERSENNE && MODTYPE != GENERALISED_MERSENNE { + let mut d = DBIG::new_scopy(&(self.x)); + FP::modulo(&mut d) + } else { + BIG::new_copy(&(self.x)) + } + } + + /* reduce a DBIG to a BIG using the appropriate form of the modulus */ + /* dd */ + pub fn modulo(d: &mut DBIG) -> BIG { + if MODTYPE == PSEUDO_MERSENNE { + let mut b = BIG::new(); + let mut t = d.split(MODBITS); + b.dcopy(&d); + let v = t.pmul(rom::MCONST as isize); + + t.add(&b); + t.norm(); + + let tw = t.w[big::NLEN - 1]; + t.w[big::NLEN - 1] &= TMASK; + t.w[0] += rom::MCONST * ((tw >> TBITS) + (v << (big::BASEBITS - TBITS))); + t.norm(); + return t; + } + + if MODTYPE == MONTGOMERY_FRIENDLY { + let mut b = BIG::new(); + for i in 0..big::NLEN { + let x = d.w[i]; + + let tuple = BIG::muladd(x, rom::MCONST - 1, x, d.w[big::NLEN + i - 1]); + d.w[big::NLEN + i] += tuple.0; + d.w[big::NLEN + i - 1] = tuple.1; + } + + b.zero(); + + for i in 0..big::NLEN { + b.w[i] = d.w[big::NLEN + i]; + } + b.norm(); + return b; + } + + if MODTYPE == GENERALISED_MERSENNE { + // GoldiLocks Only + let mut b = BIG::new(); + let t = d.split(MODBITS); + let rm2 = (MODBITS / 2) as usize; + b.dcopy(&d); + b.add(&t); + let mut dd = DBIG::new_scopy(&t); + dd.shl(rm2); + + let mut tt = dd.split(MODBITS); + let lo = BIG::new_dcopy(&dd); + b.add(&tt); + b.add(&lo); + b.norm(); + tt.shl(rm2); + b.add(&tt); + + let carry = b.w[big::NLEN - 1] >> TBITS; + b.w[big::NLEN - 1] &= TMASK; + b.w[0] += carry; + + let ix=(224 / big::BASEBITS) as usize; + b.w[ix] += carry << (224 % big::BASEBITS); + b.norm(); + return b; + } + + if MODTYPE == NOT_SPECIAL { + let m = BIG::new_ints(&rom::MODULUS); + return BIG::monty(&m, rom::MCONST, d); + } + BIG::new() + } + + /* convert to string */ + #[cfg(feature = "std")] + pub fn tostring(&self) -> String { + self.redc().tostring() + } + + /* reduce this mod Modulus */ + pub fn reduce(&mut self) { + let mut m = BIG::new_ints(&rom::MODULUS); + let mut r = BIG::new_copy(&m); + let mut sb: usize; + self.x.norm(); + if self.xes > 16 { + let q = FP::quo(&self.x, &m); + let carry = r.pmul(q); + r.w[big::NLEN - 1] += carry << big::BASEBITS; // correction - put any carry out back in again + self.x.sub(&r); + self.x.norm(); + sb = 2; + } else { + sb = FP::logb2((self.xes - 1) as u32); + } + m.fshl(sb); + + while sb > 0 { + let sr = BIG::ssn(&mut r, &self.x, &mut m); + self.x.cmove(&r, 1 - sr); + sb -= 1; + } + + self.xes = 1; + } + + /* test this=0? */ + pub fn iszilch(&self) -> bool { + let mut a = FP::new_copy(self); + a.reduce(); + a.x.iszilch() + } + + pub fn islarger(&self) -> isize { + if self.iszilch() { + return 0; + } + let mut sx = BIG::new_ints(&rom::MODULUS); + let fx=self.redc(); + sx.sub(&fx); sx.norm(); + BIG::comp(&fx,&sx) + } + + pub fn tobytes(&self,b: &mut [u8]) { + self.redc().tobytes(b) + } + + pub fn frombytes(b: &[u8]) -> FP { + let t=BIG::frombytes(b); + FP::new_big(&t) + } + + /* test this=0? */ + pub fn isunity(&self) -> bool { + let mut a = FP::new_copy(self); + a.reduce(); + a.redc().isunity() + } + + pub fn sign(&self) -> isize { + if BIG_ENDIAN_SIGN { + let mut m = BIG::new_ints(&rom::MODULUS); + m.dec(1); + m.fshr(1); + let mut n = FP::new_copy(self); + n.reduce(); + let w=n.redc(); + let cp=BIG::comp(&w,&m); + ((cp+1)&2)>>1 + } else { + let mut a = FP::new_copy(self); + a.reduce(); + a.redc().parity() + } + } + + /* copy from FP b */ + pub fn copy(&mut self, b: &FP) { + self.x.copy(&(b.x)); + self.xes = b.xes; + } + + /* copy from BIG b */ + pub fn bcopy(&mut self, b: &BIG) { + self.x.copy(&b); + self.nres(); + } + + /* set this=0 */ + pub fn zero(&mut self) { + self.x.zero(); + self.xes = 1; + } + + /* set this=1 */ + pub fn one(&mut self) { + self.x.one(); + self.nres() + } + + /* normalise this */ + pub fn norm(&mut self) { + self.x.norm(); + } + + /* swap FPs depending on d */ + pub fn cswap(&mut self, b: &mut FP, d: isize) { + self.x.cswap(&mut (b.x), d); + let mut c = d as i32; + c = !(c - 1); + let t = c & (self.xes ^ b.xes); + self.xes ^= t; + b.xes ^= t; + } + + /* copy FPs depending on d */ + pub fn cmove(&mut self, b: &FP, d: isize) { + self.x.cmove(&(b.x), d); + let c = d as i32; + self.xes ^= (self.xes ^ b.xes) & (-c); + } + + /* this*=b mod Modulus */ + pub fn mul(&mut self, b: &FP) { + if (self.xes as i64) * (b.xes as i64) > FEXCESS as i64 { + self.reduce() + } + + let mut d = BIG::mul(&(self.x), &(b.x)); + self.x.copy(&FP::modulo(&mut d)); + self.xes = 2; + } + + fn logb2(w: u32) -> usize { + let mut v = w; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + + v = v - ((v >> 1) & 0x55555555); + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + ((((v + (v >> 4)) & 0xF0F0F0F).wrapping_mul(0x1010101)) >> 24) as usize + } + + // find appoximation to quotient of a/m + // Out by at most 2. + // Note that MAXXES is bounded to be 2-bits less than half a word + fn quo(n: &BIG, m: &BIG) -> isize { + let hb = arch::CHUNK / 2; + + if TBITS < hb { + let sh = hb - TBITS; + let num = (n.w[big::NLEN - 1] << sh) | (n.w[big::NLEN - 2] >> (big::BASEBITS - sh)); + let den = (m.w[big::NLEN - 1] << sh) | (m.w[big::NLEN - 2] >> (big::BASEBITS - sh)); + (num / (den + 1)) as isize + } else { + let num = n.w[big::NLEN - 1]; + let den = m.w[big::NLEN - 1]; + (num / (den + 1)) as isize + } + } + + /* this = -this mod Modulus */ + pub fn neg(&mut self) { + let mut p = BIG::new_ints(&rom::MODULUS); + let sb = FP::logb2((self.xes - 1) as u32); + + p.fshl(sb); + self.x.rsub(&p); + self.xes = 1 << ((sb as i32) + 1); + if self.xes > FEXCESS { + self.reduce() + } + } + + /* this*=c mod Modulus, where c is a small int */ + pub fn imul(&mut self, c: isize) { + let mut cc = c; + let mut s = false; + if cc < 0 { + cc = -cc; + s = true; + } + + if MODTYPE == PSEUDO_MERSENNE || MODTYPE == GENERALISED_MERSENNE { + let mut d = self.x.pxmul(cc); + self.x.copy(&FP::modulo(&mut d)); + self.xes = 2 + } else if self.xes * (cc as i32) <= FEXCESS { + self.x.pmul(cc); + self.xes *= cc as i32; + } else { + let n = FP::new_int(cc); + self.mul(&n); + } + + if s { + self.neg(); + self.norm(); + } + } + + /* self*=self mod Modulus */ + pub fn sqr(&mut self) { + if (self.xes as i64) * (self.xes as i64) > FEXCESS as i64 { + self.reduce() + } + + let mut d = BIG::sqr(&(self.x)); + self.x.copy(&FP::modulo(&mut d)); + self.xes = 2 + } + + /* self+=b */ + pub fn add(&mut self, b: &FP) { + self.x.add(&(b.x)); + self.xes += b.xes; + if self.xes > FEXCESS { + self.reduce() + } + } + + /* self+=self */ + pub fn dbl(&mut self) { + self.x.dbl(); + self.xes += self.xes; + if self.xes > FEXCESS { + self.reduce() + } + } + + /* self-=b */ + pub fn sub(&mut self, b: &FP) { + let mut n = FP::new_copy(b); + n.neg(); + self.add(&n); + } + + /* self=b-self */ + pub fn rsub(&mut self, b: &FP) { + self.neg(); + self.add(&b); + } + + /* self/=2 mod Modulus */ + pub fn div2(&mut self) { + let p = BIG::new_ints(&rom::MODULUS); + let pr = self.x.parity(); + let mut w = BIG::new_copy(&self.x); + self.x.fshr(1); + w.add(&p); w.norm(); + w.fshr(1); + self.x.cmove(&w,pr); + } + /* return jacobi symbol (this/Modulus) */ + pub fn jacobi(&mut self) -> isize { + let p = BIG::new_ints(&rom::MODULUS); + let mut w = self.redc(); + w.jacobi(&p) + } + /* return TRUE if self==a */ + pub fn equals(&self, a: &FP) -> bool { + let mut f = FP::new_copy(self); + let mut s = FP::new_copy(a); + f.reduce(); + s.reduce(); + BIG::comp(&(f.x), &(s.x)) == 0 + } + + /* return self^e mod Modulus */ + // Could leak size of e + // but not used here with secret exponent e + pub fn pow(&self, e: &BIG) -> FP { + let mut tb: [FP; 16] = [ + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + ]; + const CT: usize = 1 + (big::NLEN * (big::BASEBITS as usize) + 3) / 4; + let mut w: [i8; CT] = [0; CT]; + + let mut s = FP::new_copy(&self); + s.norm(); + let mut t = BIG::new_copy(e); + t.norm(); + let nb = 1 + (t.nbits() + 3) / 4; + + for i in 0..nb { + let lsbs = t.lastbits(4); + t.dec(lsbs); + t.norm(); + w[i] = lsbs as i8; + t.fshr(4); + } + tb[0].one(); + tb[1].copy(&s); + + let mut c = FP::new(); + for i in 2..16 { + c.copy(&tb[i - 1]); + tb[i].copy(&c); + tb[i].mul(&s); + } + let mut r = FP::new_copy(&tb[w[nb - 1] as usize]); + for i in (0..nb - 1).rev() { + r.sqr(); + r.sqr(); + r.sqr(); + r.sqr(); + r.mul(&tb[w[i] as usize]) + } + r.reduce(); + r + } + + // See eprint paper https://eprint.iacr.org/2018/1038 + // return this^(p-3)/4 or this^(p-5)/8 + pub fn fpow(&self) -> FP { + let ac: [isize; 11] = [1, 2, 3, 6, 12, 15, 30, 60, 120, 240, 255]; + let mut xp: [FP; 11] = [ + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + FP::new(), + ]; + // phase 1 + let mut t = FP::new(); + xp[0].copy(&self); // 1 + xp[1].copy(&self); + xp[1].sqr(); // 2 + t.copy(&xp[1]); + xp[2].copy(&t); + xp[2].mul(&self); // 3 + t.copy(&xp[2]); + xp[3].copy(&t); + xp[3].sqr(); // 6 + t.copy(&xp[3]); + xp[4].copy(&t); + xp[4].sqr(); // 12 + t.copy(&xp[4]); + t.mul(&xp[2]); + xp[5].copy(&t); // 15 + t.copy(&xp[5]); + xp[6].copy(&t); + xp[6].sqr(); // 30 + t.copy(&xp[6]); + xp[7].copy(&t); + xp[7].sqr(); // 60 + t.copy(&xp[7]); + xp[8].copy(&t); + xp[8].sqr(); // 120 + t.copy(&xp[8]); + xp[9].copy(&t); + xp[9].sqr(); // 240 + t.copy(&xp[9]); + t.mul(&xp[5]); + xp[10].copy(&t); // 255 + + let mut n = MODBITS as isize; + let mut c: isize; + + if MODTYPE == GENERALISED_MERSENNE { + // Goldilocks ONLY + n /= 2; + } + + let e = PM1D2 as isize; + + n-=e+1; + c=((rom::MCONST as isize)+(1< k { + i -= 1; + } + key.copy(&xp[i]); + k -= ac[i]; + } + while k != 0 { + i -= 1; + if ac[i] > k { + continue; + } + key.mul(&xp[i]); + k -= ac[i]; + } + // phase 2 + t.copy(&xp[2]); + xp[1].copy(&t); + t.copy(&xp[5]); + xp[2].copy(&t); + t.copy(&xp[10]); + xp[3].copy(&t); + + let mut j = 3; + let mut m = 8; + let nw = n - bw; + let mut r = FP::new(); + + while 2 * m < nw { + t.copy(&xp[j]); + j += 1; + for _ in 0..m { + t.sqr(); + } + r.copy(&xp[j - 1]); + r.mul(&t); + xp[j].copy(&r); + m *= 2; + } + let mut lo = nw - m; + r.copy(&xp[j]); + + while lo != 0 { + m /= 2; + j -= 1; + if lo < m { + continue; + } + lo -= m; + t.copy(&r); + for _ in 0..m { + t.sqr(); + } + r.copy(&t); + r.mul(&xp[j]); + } + // phase 3 + if bw != 0 { + for _ in 0..bw { + r.sqr(); + } + r.mul(&key); + } + if MODTYPE == GENERALISED_MERSENNE { + // Goldilocks ONLY + key.copy(&r); + r.sqr(); + r.mul(&self); + for _ in 0..n + 1 { + r.sqr(); + } + r.mul(&key); + } + while nd>0 { + r.sqr(); + nd-=1; + } + r + } + + /* Pseudo_inverse square root */ + pub fn progen(&mut self) { + if MODTYPE == PSEUDO_MERSENNE || MODTYPE == GENERALISED_MERSENNE { + self.copy(&self.fpow()); + return; + } + let e=PM1D2 as usize; + let mut m = BIG::new_ints(&rom::MODULUS); + m.dec(1); + m.shr(e); + m.dec(1); + m.fshr(1); + + self.copy(&self.pow(&m)); + } + + /* self=1/self mod Modulus */ + pub fn inverse(&mut self,take_hint: Option<&FP>) { + let e=PM1D2 as isize; + self.norm(); + let mut s=FP::new_copy(self); + for _ in 0..e-1 { + s.sqr(); + s.mul(self); + } + if let Some(hint) = take_hint { + self.copy(&hint); + } else { + self.progen(); + } + for _ in 0..=e { + self.sqr(); + } + self.mul(&s); + self.reduce(); + } + + /* Test for Quadratic Residue */ + pub fn qr(&self,give_hint: Option<&mut FP>) -> isize { + let e=PM1D2 as isize; + let mut r=FP::new_copy(self); + r.progen(); + if let Some(hint) = give_hint { + hint.copy(&r); + } + + r.sqr(); + r.mul(self); + for _ in 0..e-1 { + r.sqr(); + } + + r.isunity() as isize + } + + pub fn invsqrt(&self,i: &mut FP,s: &mut FP) -> isize { + let mut h=FP::new(); + let qr=self.qr(Some(&mut h)); + s.copy(&self.sqrt(Some(&h))); + i.copy(self); + i.inverse(Some(&h)); + qr + } + +// Two for the price of One - See Hamburg https://eprint.iacr.org/2012/309.pdf +// Calculate inverse of i and square root of s, return QR + pub fn tpo(mut i: &mut FP,mut s: &mut FP) -> isize { + let mut w = FP::new_copy(s); + let mut t = FP::new_copy(i); + w.mul(&i); + t.mul(&w); + let qr=t.invsqrt(&mut i,&mut s); + i.mul(&w); + s.mul(&i); + qr + } + + /* return sqrt(this) mod Modulus */ + pub fn sqrt(&self,take_hint: Option<&FP>) -> FP { + let e=PM1D2 as isize; + let mut g=FP::new_copy(self); + + if let Some(hint) = take_hint { + g.copy(&hint); + } else { + g.progen(); + } + let m = BIG::new_ints(&rom::ROI); + let mut v=FP::new_big(&m); + let mut t=FP::new_copy(&g); + t.sqr(); + t.mul(self); + + let mut r=FP::new_copy(self); + r.mul(&g); + let mut b=FP::new_copy(&t); + + for k in (2..=e).rev() //(int k=e;k>1;k--) + { + for _ in 1..k-1 { + b.sqr(); + } + let u=!b.isunity() as isize; + g.copy(&r); g.mul(&v); + r.cmove(&g,u); + v.sqr(); + g.copy(&t); g.mul(&v); + t.cmove(&g,u); + b.copy(&t); + } + let sgn=r.sign(); + let mut nr=FP::new_copy(&r); + nr.neg(); nr.norm(); + r.cmove(&nr,sgn); + r + } + +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp12.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp12.rs new file mode 100644 index 000000000000..04df83d8b8a7 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp12.rs @@ -0,0 +1,1111 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::bn254::big; +use crate::bn254::big::BIG; +use crate::bn254::ecp; +use crate::bn254::fp::FP; +use crate::bn254::fp2::FP2; +use crate::bn254::fp4::FP4; +use crate::bn254::rom; + +pub const ZERO: usize = 0; +pub const ONE: usize = 1; +pub const SPARSEST: usize = 2; +pub const SPARSER: usize = 3; +pub const SPARSE: usize = 4; +pub const DENSE: usize = 5; + +#[derive(Copy, Clone)] +pub struct FP12 { + a: FP4, + b: FP4, + c: FP4, + stype: usize, +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for FP12 { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for FP12 { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +impl FP12 { + pub fn new() -> FP12 { + FP12 { + a: FP4::new(), + b: FP4::new(), + c: FP4::new(), + stype: ZERO, + } + } + + pub fn settype(&mut self, t: usize) { + self.stype = t; + } + + pub fn gettype(&self) -> usize { + self.stype + } + + pub fn new_int(a: isize) -> FP12 { + let mut f = FP12::new(); + f.a.copy(&FP4::new_int(a)); + f.b.zero(); + f.c.zero(); + if a == 1 { + f.stype = ONE; + } else { + f.stype = SPARSEST; + } + f + } + + pub fn new_copy(x: &FP12) -> FP12 { + let mut f = FP12::new(); + f.a.copy(&x.a); + f.b.copy(&x.b); + f.c.copy(&x.c); + f.stype = x.stype; + f + } + + pub fn new_fp4s(d: &FP4, e: &FP4, f: &FP4) -> FP12 { + let mut g = FP12::new(); + g.a.copy(d); + g.b.copy(e); + g.c.copy(f); + g.stype = DENSE; + g + } + + pub fn new_fp4(d: &FP4) -> FP12 { + let mut g = FP12::new(); + g.a.copy(d); + g.b.zero(); + g.c.zero(); + g.stype = SPARSEST; + g + } + + /* reduce components mod Modulus */ + pub fn reduce(&mut self) { + self.a.reduce(); + self.b.reduce(); + self.c.reduce(); + } + + /* normalise components of w */ + pub fn norm(&mut self) { + self.a.norm(); + self.b.norm(); + self.c.norm(); + } + + /* test self=0 ? */ + pub fn iszilch(&self) -> bool { + //self.reduce(); + self.a.iszilch() && self.b.iszilch() && self.c.iszilch() + } + + /* Conditional move of g to self dependant on d */ + pub fn cmove(&mut self, g: &FP12, d: isize) { + self.a.cmove(&g.a, d); + self.b.cmove(&g.b, d); + self.c.cmove(&g.c, d); + let mut u = d as usize; + u = !(u.wrapping_sub(1)); + self.stype ^= (self.stype ^ g.stype) & u; + } + + /* return 1 if b==c, no branching */ + fn teq(b: i32, c: i32) -> isize { + let mut x = b ^ c; + x -= 1; // if x=0, x now -1 + ((x >> 31) & 1) as isize + } + + /* Constant time select from pre-computed table */ + pub fn selector(&mut self, g: &[FP12], b: i32) { + let m = b >> 31; + let mut babs = (b ^ m) - m; + + babs = (babs - 1) / 2; + + self.cmove(&g[0], FP12::teq(babs, 0)); // conditional move + self.cmove(&g[1], FP12::teq(babs, 1)); + self.cmove(&g[2], FP12::teq(babs, 2)); + self.cmove(&g[3], FP12::teq(babs, 3)); + self.cmove(&g[4], FP12::teq(babs, 4)); + self.cmove(&g[5], FP12::teq(babs, 5)); + self.cmove(&g[6], FP12::teq(babs, 6)); + self.cmove(&g[7], FP12::teq(babs, 7)); + + let mut invf = FP12::new_copy(self); + invf.conj(); + self.cmove(&invf, (m & 1) as isize); + } + + /* test self=1 ? */ + pub fn isunity(&self) -> bool { + let one = FP4::new_int(1); + self.a.equals(&one) && self.b.iszilch() && self.c.iszilch() + } + + /* test self=x */ + pub fn equals(&self, x: &FP12) -> bool { + self.a.equals(&x.a) && self.b.equals(&x.b) && self.c.equals(&x.c) + } + + pub fn geta(&mut self) -> FP4 { + self.a + // let f = FP4::new_copy(&self.a); + // return f; + } + + pub fn getb(&mut self) -> FP4 { + self.b + // let f = FP4::new_copy(&self.b); + // return f; + } + + pub fn getc(&mut self) -> FP4 { + self.c + // let f = FP4::new_copy(&self.c); + // return f; + } + + /* copy self=x */ + pub fn copy(&mut self, x: &FP12) { + self.a.copy(&x.a); + self.b.copy(&x.b); + self.c.copy(&x.c); + self.stype = x.stype; + } + + /* set self=1 */ + pub fn one(&mut self) { + self.a.one(); + self.b.zero(); + self.c.zero(); + self.stype = ONE; + } + + /* set self=0 */ + pub fn zero(&mut self) { + self.a.zero(); + self.b.zero(); + self.c.zero(); + self.stype = ZERO; + } + + /* this=conj(this) */ + pub fn conj(&mut self) { + self.a.conj(); + self.b.nconj(); + self.c.conj(); + } + + /* Granger-Scott Unitary Squaring */ + pub fn usqr(&mut self) { + let mut a = FP4::new_copy(&self.a); + let mut b = FP4::new_copy(&self.c); + let mut c = FP4::new_copy(&self.b); + let mut d = FP4::new(); + + self.a.sqr(); + d.copy(&self.a); + d.add(&self.a); + self.a.add(&d); + + self.a.norm(); + a.nconj(); + + a.dbl(); + self.a.add(&a); + b.sqr(); + b.times_i(); + + d.copy(&b); + d.add(&b); + b.add(&d); + b.norm(); + + c.sqr(); + d.copy(&c); + d.add(&c); + c.add(&d); + c.norm(); + + self.b.conj(); + self.b.dbl(); + self.c.nconj(); + + self.c.dbl(); + self.b.add(&b); + self.c.add(&c); + self.stype = DENSE; + self.reduce(); + } + + /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */ + pub fn sqr(&mut self) { + if self.stype == ONE { + return; + } + + let mut a = FP4::new_copy(&self.a); + let mut b = FP4::new_copy(&self.b); + let mut c = FP4::new_copy(&self.c); + let mut d = FP4::new_copy(&self.a); + + a.sqr(); + b.mul(&self.c); + b.dbl(); + b.norm(); + c.sqr(); + d.mul(&self.b); + d.dbl(); + + self.c.add(&self.a); + self.c.add(&self.b); + self.c.norm(); + self.c.sqr(); + + self.a.copy(&a); + a.add(&b); + a.norm(); + a.add(&c); + a.add(&d); + a.norm(); + + a.neg(); + b.times_i(); + c.times_i(); + + self.a.add(&b); + + self.b.copy(&c); + self.b.add(&d); + self.c.add(&a); + if self.stype == SPARSER || self.stype == SPARSEST { + self.stype = SPARSE; + } else { + self.stype = DENSE; + } + self.norm(); + } + + /* FP12 full multiplication self=self*y */ + pub fn mul(&mut self, y: &FP12) { + let mut z0 = FP4::new_copy(&self.a); + let mut z1 = FP4::new(); + let mut z2 = FP4::new_copy(&self.b); + let mut z3 = FP4::new(); + let mut t0 = FP4::new_copy(&self.a); + let mut t1 = FP4::new_copy(&y.a); + + z0.mul(&y.a); + z2.mul(&y.b); + + t0.add(&self.b); + t1.add(&y.b); + + t0.norm(); + t1.norm(); + + z1.copy(&t0); + z1.mul(&t1); + t0.copy(&self.b); + t0.add(&self.c); + t1.copy(&y.b); + t1.add(&y.c); + + t0.norm(); + t1.norm(); + + z3.copy(&t0); + z3.mul(&t1); + + t0.copy(&z0); + t0.neg(); + t1.copy(&z2); + t1.neg(); + + z1.add(&t0); + self.b.copy(&z1); + self.b.add(&t1); + + z3.add(&t1); + z2.add(&t0); + + t0.copy(&self.a); + t0.add(&self.c); + t0.norm(); + t1.copy(&y.a); + t1.add(&y.c); + t1.norm(); + t0.mul(&t1); + z2.add(&t0); + + t0.copy(&self.c); + t0.mul(&y.c); + t1.copy(&t0); + t1.neg(); + + self.c.copy(&z2); + self.c.add(&t1); + z3.add(&t1); + t0.times_i(); + self.b.add(&t0); + z3.norm(); + + z3.times_i(); + self.a.copy(&z0); + self.a.add(&z3); + self.stype = DENSE; + self.norm(); + } + + /* FP12 full multiplication w=w*y */ + /* Supports sparse multiplicands */ + /* Usually w is denser than y */ + pub fn ssmul(&mut self, y: &FP12) { + if self.stype == ONE { + self.copy(&y); + return; + } + if y.stype == ONE { + return; + } + if y.stype >= SPARSE { + let mut z0 = FP4::new_copy(&self.a); + let mut z1 = FP4::new(); + let mut z2 = FP4::new(); + let mut z3 = FP4::new(); + z0.mul(&y.a); + + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + if y.stype == SPARSE || self.stype == SPARSE { + let mut ga = FP2::new(); + let mut gb = FP2::new(); + + gb.copy(&self.b.getb()); + gb.mul(&y.b.getb()); + ga.zero(); + if y.stype != SPARSE { + ga.copy(&self.b.getb()); + ga.mul(&y.b.geta()); + } + if self.stype != SPARSE { + ga.copy(&self.b.geta()); + ga.mul(&y.b.getb()); + } + z2.set_fp2s(&ga, &gb); + z2.times_i(); + } else { + z2.copy(&self.b); + z2.mul(&y.b); + } + } else { + z2.copy(&self.b); + z2.mul(&y.b); + } + let mut t0 = FP4::new_copy(&self.a); + let mut t1 = FP4::new_copy(&y.a); + t0.add(&self.b); + t0.norm(); + t1.add(&y.b); + t1.norm(); + + z1.copy(&t0); + z1.mul(&t1); + t0.copy(&self.b); + t0.add(&self.c); + t0.norm(); + t1.copy(&y.b); + t1.add(&y.c); + t1.norm(); + + z3.copy(&t0); + z3.mul(&t1); + + t0.copy(&z0); + t0.neg(); + t1.copy(&z2); + t1.neg(); + + z1.add(&t0); + self.b.copy(&z1); + self.b.add(&t1); + + z3.add(&t1); + z2.add(&t0); + + t0.copy(&self.a); + t0.add(&self.c); + t0.norm(); + t1.copy(&y.a); + t1.add(&y.c); + t1.norm(); + + t0.mul(&t1); + z2.add(&t0); + + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + if y.stype == SPARSE || self.stype == SPARSE { + let mut ga = FP2::new(); + let mut gb = FP2::new(); + + ga.copy(&self.c.geta()); + ga.mul(&y.c.geta()); + gb.zero(); + if y.stype != SPARSE { + gb.copy(&self.c.geta()); + gb.mul(&y.c.getb()); + } + if self.stype != SPARSE { + gb.copy(&self.c.getb()); + gb.mul(&y.c.geta()); + } + t0.set_fp2s(&ga, &gb); + } else { + t0.copy(&self.c); + t0.mul(&y.c); + } + } else { + t0.copy(&self.c); + t0.mul(&y.c); + } + t1.copy(&t0); + t1.neg(); + + self.c.copy(&z2); + self.c.add(&t1); + z3.add(&t1); + t0.times_i(); + self.b.add(&t0); + z3.norm(); + z3.times_i(); + self.a.copy(&z0); + self.a.add(&z3); + } else { + if self.stype == SPARSER || self.stype == SPARSEST { + self.smul(&y); + return; + } + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + // dense by sparser - 13m + let mut z0 = FP4::new_copy(&self.a); + let mut z2 = FP4::new_copy(&self.b); + let mut z3 = FP4::new_copy(&self.b); + let mut t0 = FP4::new(); + let mut t1 = FP4::new_copy(&y.a); + + z0.mul(&y.a); + if y.stype == SPARSEST { + z2.qmul(&y.b.geta().getA()); + } else { + z2.pmul(&y.b.geta()); + } + self.b.add(&self.a); + t1.padd(&y.b.geta()); + + t1.norm(); + self.b.norm(); + self.b.mul(&t1); + z3.add(&self.c); + z3.norm(); + + if y.stype == SPARSEST { + z3.qmul(&y.b.geta().getA()); + } else { + z3.pmul(&y.b.geta()); + } + t0.copy(&z0); + t0.neg(); + t1.copy(&z2); + t1.neg(); + + self.b.add(&t0); + + self.b.add(&t1); + z3.add(&t1); + z2.add(&t0); + + t0.copy(&self.a); + t0.add(&self.c); + t0.norm(); + z3.norm(); + t0.mul(&y.a); + self.c.copy(&z2); + self.c.add(&t0); + + z3.times_i(); + self.a.copy(&z0); + self.a.add(&z3); + } + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + let mut z0 = FP4::new_copy(&self.a); + let mut z1 = FP4::new(); + let mut z2 = FP4::new(); + let mut z3 = FP4::new(); + let mut t0 = FP4::new_copy(&self.a); + let mut t1 = FP4::new(); + + z0.mul(&y.a); + t0.add(&self.b); + t0.norm(); + + z1.copy(&t0); + z1.mul(&y.a); + t0.copy(&self.b); + t0.add(&self.c); + t0.norm(); + + z3.copy(&t0); + if y.stype == SPARSEST { + z3.qmul(&y.c.getb().getA()); + } else { + z3.pmul(&y.c.getb()); + } + z3.times_i(); + + t0.copy(&z0); + t0.neg(); + z1.add(&t0); + self.b.copy(&z1); + z2.copy(&t0); + + t0.copy(&self.a); + t0.add(&self.c); + t0.norm(); + t1.copy(&y.a); + t1.add(&y.c); + t1.norm(); + + t0.mul(&t1); + z2.add(&t0); + t0.copy(&self.c); + + if y.stype == SPARSEST { + t0.qmul(&y.c.getb().getA()); + } else { + t0.pmul(&y.c.getb()); + } + t0.times_i(); + t1.copy(&t0); + t1.neg(); + + self.c.copy(&z2); + self.c.add(&t1); + z3.add(&t1); + t0.times_i(); + self.b.add(&t0); + z3.norm(); + z3.times_i(); + self.a.copy(&z0); + self.a.add(&z3); + } + } + self.stype = DENSE; + self.norm(); + } + + /* Special case of multiplication arises from special form of ATE pairing line function */ + pub fn smul(&mut self, y: &FP12) { + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + let mut w1 = FP2::new_copy(&self.a.geta()); + let mut w2 = FP2::new_copy(&self.a.getb()); + //let mut w3=FP2::new_copy(&self.b.geta()); + + let mut w3: FP2; + + w1.mul(&y.a.geta()); + w2.mul(&y.a.getb()); + + if y.stype == SPARSEST || self.stype == SPARSEST { + if y.stype == SPARSEST && self.stype == SPARSEST { + let mut t = FP::new_copy(&self.b.geta().getA()); + t.mul(&y.b.geta().getA()); + w3 = FP2::new_fp(&t); + } else if y.stype != SPARSEST { + w3 = FP2::new_copy(&y.b.geta()); + w3.pmul(&self.b.geta().getA()); + } else { + w3 = FP2::new_copy(&self.b.geta()); + w3.pmul(&y.b.geta().getA()); + } + } else { + w3 = FP2::new_copy(&self.b.geta()); + w3.mul(&y.b.geta()); + } + let mut ta = FP2::new_copy(&self.a.geta()); + let mut tb = FP2::new_copy(&y.a.geta()); + ta.add(&self.a.getb()); + ta.norm(); + tb.add(&y.a.getb()); + tb.norm(); + let mut tc = FP2::new_copy(&ta); + tc.mul(&tb); + let mut t = FP2::new_copy(&w1); + t.add(&w2); + t.neg(); + tc.add(&t); + + ta.copy(&self.a.geta()); + ta.add(&self.b.geta()); + ta.norm(); + tb.copy(&y.a.geta()); + tb.add(&y.b.geta()); + tb.norm(); + let mut td = FP2::new_copy(&ta); + td.mul(&tb); + t.copy(&w1); + t.add(&w3); + t.neg(); + td.add(&t); + + ta.copy(&self.a.getb()); + ta.add(&self.b.geta()); + ta.norm(); + tb.copy(&y.a.getb()); + tb.add(&y.b.geta()); + tb.norm(); + let mut te = FP2::new_copy(&ta); + te.mul(&tb); + t.copy(&w2); + t.add(&w3); + t.neg(); + te.add(&t); + + w2.mul_ip(); + w1.add(&w2); + + self.a.set_fp2s(&w1, &tc); + self.b.set_fp2s(&td, &te); + self.c.set_fp2(&w3); + + self.a.norm(); + self.b.norm(); + } else { + let mut w1 = FP2::new_copy(&self.a.geta()); + let mut w2 = FP2::new_copy(&self.a.getb()); + // let mut w3=FP2::new_copy(&self.c.getb()); + let mut w3: FP2; + + w1.mul(&y.a.geta()); + w2.mul(&y.a.getb()); + + if y.stype == SPARSEST || self.stype == SPARSEST { + if y.stype == SPARSEST && self.stype == SPARSEST { + let mut t = FP::new_copy(&self.c.getb().getA()); + t.mul(&y.c.getb().getA()); + w3 = FP2::new_fp(&t); + } else if y.stype != SPARSEST { + w3 = FP2::new_copy(&y.c.getb()); + w3.pmul(&self.c.getb().getA()); + } else { + w3 = FP2::new_copy(&self.c.getb()); + w3.pmul(&y.c.getb().getA()); + } + } else { + w3 = FP2::new_copy(&self.c.getb()); + w3.mul(&y.c.getb()); + } + let mut ta = FP2::new_copy(&self.a.geta()); + let mut tb = FP2::new_copy(&y.a.geta()); + ta.add(&self.a.getb()); + ta.norm(); + tb.add(&y.a.getb()); + tb.norm(); + let mut tc = FP2::new_copy(&ta); + tc.mul(&tb); + let mut t = FP2::new_copy(&w1); + t.add(&w2); + t.neg(); + tc.add(&t); + + ta.copy(&self.a.geta()); + ta.add(&self.c.getb()); + ta.norm(); + tb.copy(&y.a.geta()); + tb.add(&y.c.getb()); + tb.norm(); + let mut td = FP2::new_copy(&ta); + td.mul(&tb); + t.copy(&w1); + t.add(&w3); + t.neg(); + td.add(&t); + + ta.copy(&self.a.getb()); + ta.add(&self.c.getb()); + ta.norm(); + tb.copy(&y.a.getb()); + tb.add(&y.c.getb()); + tb.norm(); + let mut te = FP2::new_copy(&ta); + te.mul(&tb); + t.copy(&w2); + t.add(&w3); + t.neg(); + te.add(&t); + + w2.mul_ip(); + w1.add(&w2); + self.a.set_fp2s(&w1, &tc); + + w3.mul_ip(); + w3.norm(); + self.b.set_fp2h(&w3); + + te.norm(); + te.mul_ip(); + self.c.set_fp2s(&te, &td); + + self.a.norm(); + self.c.norm(); + } + self.stype = SPARSE; + } + + /* self=1/self */ + pub fn inverse(&mut self) { + let mut f0 = FP4::new_copy(&self.a); + let mut f1 = FP4::new_copy(&self.b); + let mut f2 = FP4::new_copy(&self.a); + let mut f3 = FP4::new(); + + self.norm(); + f0.sqr(); + f1.mul(&self.c); + f1.times_i(); + f0.sub(&f1); + f0.norm(); + + f1.copy(&self.c); + f1.sqr(); + f1.times_i(); + f2.mul(&self.b); + f1.sub(&f2); + f1.norm(); + + f2.copy(&self.b); + f2.sqr(); + f3.copy(&self.a); + f3.mul(&self.c); + f2.sub(&f3); + f2.norm(); + + f3.copy(&self.b); + f3.mul(&f2); + f3.times_i(); + self.a.mul(&f0); + f3.add(&self.a); + self.c.mul(&f1); + self.c.times_i(); + + f3.add(&self.c); + f3.norm(); + f3.inverse(None); + self.a.copy(&f0); + self.a.mul(&f3); + self.b.copy(&f1); + self.b.mul(&f3); + self.c.copy(&f2); + self.c.mul(&f3); + self.stype = DENSE; + } + + /* self=self^p using Frobenius */ + pub fn frob(&mut self, f: &FP2) { + let mut f2 = FP2::new_copy(f); + let mut f3 = FP2::new_copy(f); + + f2.sqr(); + f3.mul(&f2); + + self.a.frob(&f3); + self.b.frob(&f3); + self.c.frob(&f3); + + self.b.pmul(f); + self.c.pmul(&f2); + self.stype = DENSE; + } + + /* trace function */ + pub fn trace(&mut self) -> FP4 { + let mut t = FP4::new(); + t.copy(&self.a); + t.imul(3); + t.reduce(); + t + } + + /* convert from byte array to FP12 */ + pub fn frombytes(w: &[u8]) -> FP12 { + const MB:usize = 4*(big::MODBYTES as usize); + let mut t: [u8; MB] = [0; MB]; + for i in 0..MB { + t[i]=w[i]; + } + let c=FP4::frombytes(&t); + for i in 0..MB { + t[i]=w[i+MB]; + } + let b=FP4::frombytes(&t); + for i in 0..MB { + t[i]=w[i+2*MB]; + } + let a=FP4::frombytes(&t); + FP12::new_fp4s(&a,&b,&c) + } + + /* convert this to byte array */ + pub fn tobytes(&mut self, w: &mut [u8]) { + const MB:usize = 4*(big::MODBYTES as usize); + let mut t: [u8; MB] = [0; MB]; + + self.c.tobytes(&mut t); + for i in 0..MB { + w[i]=t[i]; + } + self.b.tobytes(&mut t); + for i in 0..MB { + w[i+MB]=t[i]; + } + self.a.tobytes(&mut t); + for i in 0..MB { + w[i+2*MB]=t[i]; + } + } + + /* output to hex string */ + #[cfg(feature = "std")] + pub fn tostring(&self) -> String { + format!( + "[{},{},{}]", + self.a.tostring(), + self.b.tostring(), + self.c.tostring() + ) + } + +/* Note this is simple square and multiply, so not side-channel safe */ +/* But fast for final exponentiation where exponent is not a secret */ +/* return this^e */ + pub fn pow(&self, e: &BIG) -> FP12 { + let mut r = FP12::new_copy(self); + r.norm(); + let mut e1 = BIG::new_copy(e); + e1.norm(); + let mut e3 = BIG::new_copy(&e1); + e3.pmul(3); + e3.norm(); + let mut w = FP12::new_copy(&r); + if e3.iszilch() { + w.one(); + return w; + } + + let nb = e3.nbits(); + for i in (1..nb - 1).rev() { + w.usqr(); + let bt = e3.bit(i) - e1.bit(i); + if bt == 1 { + w.mul(&r); + } + if bt == -1 { + r.conj(); + w.mul(&r); + r.conj(); + } + } + + w.reduce(); + w + } + + /* constant time powering by small integer of max length bts */ + pub fn pinpow(&mut self, e: i32, bts: i32) { + let mut r: [FP12; 2] = [FP12::new_int(1), FP12::new_copy(self)]; + let mut t = FP12::new(); + + for i in (0..bts).rev() { + let b: usize = ((e >> i) & 1) as usize; + t.copy(&r[b]); + r[1 - b].mul(&t); + r[b].usqr(); + } + self.copy(&r[0]); + } + + pub fn compow(&mut self, e: &BIG, r: &BIG) -> FP4 { + let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + let q = BIG::new_ints(&rom::MODULUS); + + let mut g1 = FP12::new_copy(self); + let mut g2 = FP12::new_copy(self); + + let mut m = BIG::new_copy(&q); + m.rmod(&r); + + let mut a = BIG::new_copy(&e); + a.rmod(&m); + + let mut b = BIG::new_copy(&e); + b.div(&m); + + let mut c = g1.trace(); + + if b.iszilch() { + c = c.xtr_pow(&a); + return c; + } + + g2.frob(&f); + let cp = g2.trace(); + g1.conj(); + g2.mul(&g1); + let cpm1 = g2.trace(); + g2.mul(&g1); + let cpm2 = g2.trace(); + + c.xtr_pow2(&cp, &cpm1, &cpm2, &a, &b) + } + + /* p=q0^u0.q1^u1.q2^u2.q3^u3 */ + // Bos & Costello https://eprint.iacr.org/2013/458.pdf + // Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf + // Side channel attack secure + pub fn pow4(q: &[FP12], u: &[BIG]) -> FP12 { + let mut g: [FP12; 8] = [ + FP12::new(), + FP12::new(), + FP12::new(), + FP12::new(), + FP12::new(), + FP12::new(), + FP12::new(), + FP12::new(), + ]; + + let mut r = FP12::new(); + let mut p = FP12::new(); + const CT: usize = 1 + big::NLEN * (big::BASEBITS as usize); + let mut w: [i8; CT] = [0; CT]; + let mut s: [i8; CT] = [0; CT]; + + let mut mt = BIG::new(); + let mut t: [BIG; 4] = [ + BIG::new_copy(&u[0]), + BIG::new_copy(&u[1]), + BIG::new_copy(&u[2]), + BIG::new_copy(&u[3]), + ]; + + for i in 0..4 { + t[i].norm(); + } + + // precomputation + g[0].copy(&q[0]); + r.copy(&g[0]); + g[1].copy(&r); + g[1].mul(&q[1]); // q[0].q[1] + g[2].copy(&r); + g[2].mul(&q[2]); + r.copy(&g[1]); // q[0].q[2] + g[3].copy(&r); + g[3].mul(&q[2]); + r.copy(&g[0]); // q[0].q[1].q[2] + g[4].copy(&r); + g[4].mul(&q[3]); + r.copy(&g[1]); // q[0].q[3] + g[5].copy(&r); + g[5].mul(&q[3]); + r.copy(&g[2]); // q[0].q[1].q[3] + g[6].copy(&r); + g[6].mul(&q[3]); + r.copy(&g[3]); // q[0].q[2].q[3] + g[7].copy(&r); + g[7].mul(&q[3]); // q[0].q[1].q[2].q[3] + + // Make it odd + let pb = 1 - t[0].parity(); + t[0].inc(pb); + t[0].norm(); + + // Number of bits + mt.zero(); + for i in 0..4 { + mt.or(&t[i]); + } + + let nb = 1 + mt.nbits(); + + // Sign pivot + s[nb - 1] = 1; + for i in 0..nb - 1 { + t[0].fshr(1); + s[i] = (2 * t[0].parity() - 1) as i8; + //println!("s={}",s[i]); + } + + // Recoded exponent + for i in 0..nb { + w[i] = 0; + let mut k = 1; + for j in 1..4 { + let bt = s[i] * (t[j].parity() as i8); + t[j].fshr(1); + t[j].dec((bt >> 1) as isize); + t[j].norm(); + w[i] += bt * (k as i8); + k *= 2; + } + } + + // Main loop + p.selector(&g, (2 * w[nb - 1] + 1) as i32); + for i in (0..nb - 1).rev() { + p.usqr(); + r.selector(&g, (2 * w[i] + s[i]) as i32); + p.mul(&r); + } + + // apply correction + r.copy(&q[0]); + r.conj(); + r.mul(&p); + p.cmove(&r, pb); + p.reduce(); + p + } +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp2.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp2.rs new file mode 100644 index 000000000000..72eb05861457 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp2.rs @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::bn254::big; +use crate::bn254::big::BIG; +use crate::bn254::dbig::DBIG; +use crate::bn254::fp; +use crate::bn254::fp::FP; +use crate::bn254::rom; + +use crate::rand::RAND; + +#[derive(Copy, Clone)] +pub struct FP2 { + a: FP, + b: FP, +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for FP2 { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for FP2 { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +impl FP2 { + pub const fn new() -> FP2 { + FP2 { + a: FP::new(), + b: FP::new(), + } + } + + pub fn new_int(a: isize) -> FP2 { + let mut f = FP2::new(); + f.a.copy(&FP::new_int(a)); + f.b.zero(); + f + } + + pub fn new_ints(a: isize, b: isize) -> FP2 { + let mut f = FP2::new(); + f.a.copy(&FP::new_int(a)); + f.b.copy(&FP::new_int(b)); + f + } + + pub fn new_copy(x: &FP2) -> FP2 { + let mut f = FP2::new(); + f.a.copy(&x.a); + f.b.copy(&x.b); + f + } + + pub fn new_fps(c: &FP, d: &FP) -> FP2 { + let mut f = FP2::new(); + f.a.copy(c); + f.b.copy(d); + f + } + + pub fn new_bigs(c: &BIG, d: &BIG) -> FP2 { + let mut f = FP2::new(); + f.a.copy(&FP::new_big(c)); + f.b.copy(&FP::new_big(d)); + f + } + + pub fn new_fp(c: &FP) -> FP2 { + let mut f = FP2::new(); + f.a.copy(c); + f.b.zero(); + f + } + + pub fn new_big(c: &BIG) -> FP2 { + let mut f = FP2::new(); + f.a.copy(&FP::new_big(c)); + f.b.zero(); + f + } + + pub fn new_rand(rng: &mut RAND) -> FP2 { + FP2::new_fps(&FP::new_rand(rng),&FP::new_rand(rng)) + } + + /* reduce components mod Modulus */ + pub fn reduce(&mut self) { + self.a.reduce(); + self.b.reduce(); + } + + /* normalise components of w */ + pub fn norm(&mut self) { + self.a.norm(); + self.b.norm(); + } + + /* test self=0 ? */ + pub fn iszilch(&self) -> bool { + self.a.iszilch() && self.b.iszilch() + } + + pub fn islarger(&self) -> isize { + if self.iszilch() { + return 0; + } + let cmp=self.b.islarger(); + if cmp!=0 { + return cmp; + } + self.a.islarger() + } + + pub fn tobytes(&self,bf: &mut [u8]) { + const MB:usize = big::MODBYTES as usize; + let mut t: [u8; MB] = [0; MB]; + self.b.tobytes(&mut t); + for i in 0..MB { + bf[i]=t[i]; + } + self.a.tobytes(&mut t); + for i in 0..MB { + bf[i+MB]=t[i]; + } + } + + pub fn frombytes(bf: &[u8]) -> FP2 { + const MB:usize = big::MODBYTES as usize; + let mut t: [u8; MB] = [0; MB]; + for i in 0..MB { + t[i]=bf[i]; + } + let tb=FP::frombytes(&t); + for i in 0..MB { + t[i]=bf[i+MB]; + } + let ta=FP::frombytes(&t); + FP2::new_fps(&ta,&tb) + } + + pub fn cmove(&mut self, g: &FP2, d: isize) { + self.a.cmove(&g.a, d); + self.b.cmove(&g.b, d); + } + + /* test self=1 ? */ + pub fn isunity(&self) -> bool { + let one = FP::new_int(1); + self.a.equals(&one) && self.b.iszilch() + } + + /* test self=x */ + pub fn equals(&self, x: &FP2) -> bool { + self.a.equals(&x.a) && self.b.equals(&x.b) + } + + /* extract a */ + #[allow(non_snake_case)] + pub fn getA(&mut self) -> FP { + self.a + } + + /* extract b */ + #[allow(non_snake_case)] + pub fn getB(&mut self) -> FP { + self.b + } + + /* extract a */ + pub fn geta(&mut self) -> BIG { + self.a.redc() + } + + /* extract b */ + pub fn getb(&mut self) -> BIG { + self.b.redc() + } + + /* copy self=x */ + pub fn copy(&mut self, x: &FP2) { + self.a.copy(&x.a); + self.b.copy(&x.b); + } + + pub fn set_fp(&mut self, x: &FP) { + self.a.copy(x); + self.b.zero(); + } + + /* set self=0 */ + pub fn zero(&mut self) { + self.a.zero(); + self.b.zero(); + } + + /* set self=1 */ + pub fn one(&mut self) { + self.a.one(); + self.b.zero(); + } + + pub fn sign(&self) -> isize { + let mut p1=self.a.sign(); + let mut p2=self.b.sign(); + if fp::BIG_ENDIAN_SIGN { + let u=self.b.iszilch() as isize; + p2^=(p1^p2)&u; + p2 + } else { + let u=self.a.iszilch() as isize; + p1^=(p1^p2)&u; + p1 + } + } + + /* negate self mod Modulus */ + pub fn neg(&mut self) { + let mut m = FP::new_copy(&self.a); + let mut t = FP::new(); + + m.add(&self.b); + m.neg(); + t.copy(&m); + t.add(&self.b); + self.b.copy(&m); + self.b.add(&self.a); + self.a.copy(&t); + } + + /* set to a-ib */ + pub fn conj(&mut self) { + self.b.neg(); + self.b.norm(); + } + + /* self+=a */ + pub fn add(&mut self, x: &FP2) { + self.a.add(&x.a); + self.b.add(&x.b); + } + + pub fn dbl(&mut self) { + self.a.dbl(); + self.b.dbl(); + } + + /* self-=a */ + pub fn sub(&mut self, x: &FP2) { + let mut m = FP2::new_copy(x); + m.neg(); + self.add(&m); + } + + /* self=a-self */ + pub fn rsub(&mut self, x: &FP2) { + self.neg(); + self.add(x); + } + + /* self*=s, where s is an FP */ + pub fn pmul(&mut self, s: &FP) { + self.a.mul(s); + self.b.mul(s); + } + + /* self*=i, where i is an int */ + pub fn imul(&mut self, c: isize) { + self.a.imul(c); + self.b.imul(c); + } + + /* self*=self */ + pub fn sqr(&mut self) { + let mut w1 = FP::new_copy(&self.a); + let mut w3 = FP::new_copy(&self.a); + let mut mb = FP::new_copy(&self.b); + + w1.add(&self.b); + + w3.add(&self.a); + w3.norm(); + self.b.mul(&w3); + + mb.neg(); + self.a.add(&mb); + + w1.norm(); + self.a.norm(); + + self.a.mul(&w1); + } + + /* this*=y */ + pub fn mul(&mut self, y: &FP2) { + if ((self.a.xes + self.b.xes) as i64) * ((y.a.xes + y.b.xes) as i64) > fp::FEXCESS as i64 { + if self.a.xes > 1 { + self.a.reduce() + } + if self.b.xes > 1 { + self.b.reduce() + } + } + + let p = BIG::new_ints(&rom::MODULUS); + let mut pr = DBIG::new(); + + pr.ucopy(&p); + + let mut c = BIG::new_copy(&(self.a.x)); + let mut d = BIG::new_copy(&(y.a.x)); + + let mut a = BIG::mul(&self.a.x, &y.a.x); + let mut b = BIG::mul(&self.b.x, &y.b.x); + + c.add(&self.b.x); + c.norm(); + d.add(&y.b.x); + d.norm(); + + let mut e = BIG::mul(&c, &d); + let mut f = DBIG::new_copy(&a); + f.add(&b); + b.rsub(&pr); + + a.add(&b); + a.norm(); + e.sub(&f); + e.norm(); + + self.a.x.copy(&FP::modulo(&mut a)); + self.a.xes = 3; + self.b.x.copy(&FP::modulo(&mut e)); + self.b.xes = 2; + } +/* + pub fn pow(&mut self, e: &BIG) { + let mut w = FP2::new_copy(self); + let mut z = BIG::new_copy(&e); + let mut r = FP2::new_int(1); + loop { + let bt = z.parity(); + z.fshr(1); + if bt == 1 { + r.mul(&mut w) + }; + if z.iszilch() { + break; + } + w.sqr(); + } + r.reduce(); + self.copy(&r); + }*/ + + pub fn qr(&mut self,h:Option<&mut FP>) -> isize { + let mut c=FP2::new_copy(self); + c.conj(); + c.mul(self); + c.getA().qr(h) + } + + /* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */ + pub fn sqrt(&mut self,h:Option<&FP>) { + if self.iszilch() { + return; + } + let mut w1 = FP::new_copy(&self.b); + let mut w2 = FP::new_copy(&self.a); + let mut w3 = FP::new_copy(&self.a); + let mut w4 = FP::new(); + let mut hint = FP::new(); + + w1.sqr(); + w2.sqr(); + w1.add(&w2); w1.norm(); + + w2.copy(&w1.sqrt(h)); + w1.copy(&w2); + + w2.copy(&self.a); + w2.add(&w1); + w2.norm(); + w2.div2(); + + w1.copy(&self.b); w1.div2(); + let qr=w2.qr(Some(&mut hint)); + +// tweak hint + w3.copy(&hint); w3.neg(); w3.norm(); + w4.copy(&w2); w4.neg(); w4.norm(); + + w2.cmove(&w4,1-qr); + hint.cmove(&w3,1-qr); + + self.a.copy(&w2.sqrt(Some(&hint))); + w3.copy(&w2); w3.inverse(Some(&hint)); + w3.mul(&self.a); + self.b.copy(&w3); self.b.mul(&w1); + w4.copy(&self.a); + + self.a.cmove(&self.b,1-qr); + self.b.cmove(&w4,1-qr); + +/* + self.a.copy(&w2.sqrt(Some(&hint))); + w3.copy(&w2); w3.inverse(Some(&hint)); + w3.mul(&self.a); + self.b.copy(&w3); self.b.mul(&w1); + + hint.neg(); hint.norm(); + w2.neg(); w2.norm(); + + w4.copy(&w2.sqrt(Some(&hint))); + w3.copy(&w2); w3.inverse(Some(&hint)); + w3.mul(&w4); + w3.mul(&w1); + + self.a.cmove(&w3,1-qr); + self.b.cmove(&w4,1-qr); +*/ + let sgn=self.sign(); + let mut nr=FP2::new_copy(&self); + nr.neg(); nr.norm(); + self.cmove(&nr,sgn); + } + + /* output to hex string */ + #[cfg(feature = "std")] + pub fn tostring(&self) -> String { + format!("[{},{}]", self.a.tostring(), self.b.tostring()) + } + + /* self=1/self */ + pub fn inverse(&mut self,h:Option<&FP>) { + self.norm(); + let mut w1 = FP::new_copy(&self.a); + let mut w2 = FP::new_copy(&self.b); + + w1.sqr(); + w2.sqr(); + w1.add(&w2); + w1.inverse(h); + self.a.mul(&w1); + w1.neg(); + w1.norm(); + self.b.mul(&w1); + } + + /* self/=2 */ + pub fn div2(&mut self) { + self.a.div2(); + self.b.div2(); + } + + /* self*=sqrt(-1) */ + pub fn times_i(&mut self) { + let z = FP::new_copy(&self.a); + self.a.copy(&self.b); + self.a.neg(); + self.b.copy(&z); + } + + /* w*=(1+sqrt(-1)) */ + /* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */ + pub fn mul_ip(&mut self) { + let mut t = FP2::new_copy(self); + let mut i = fp::QNRI; + self.times_i(); + while i > 0 { + t.dbl(); + t.norm(); + i -= 1; + } + self.add(&t); + if fp::TOWER == fp::POSITOWER { + self.norm(); + self.neg(); + } + } + + /* w/=(1+sqrt(-1)) */ + pub fn div_ip(&mut self) { + let mut z = FP2::new_ints(1 << fp::QNRI, 1); + z.inverse(None); + self.norm(); + self.mul(&z); + if fp::TOWER == fp::POSITOWER { + self.neg(); + self.norm(); + } + } +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp4.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp4.rs new file mode 100644 index 000000000000..1f36040c7c20 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/fp4.rs @@ -0,0 +1,784 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::bn254::big; +use crate::bn254::big::BIG; +use crate::bn254::fp; +use crate::bn254::fp::FP; +use crate::bn254::fp2::FP2; +use crate::rand::RAND; +#[allow(unused_imports)] +use crate::bn254::rom; + +#[derive(Copy, Clone)] +pub struct FP4 { + a: FP2, + b: FP2, +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for FP4 { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for FP4 { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.tostring()) + } +} + +impl FP4 { + pub const fn new() -> FP4 { + FP4 { + a: FP2::new(), + b: FP2::new(), + } + } + + pub fn new_int(a: isize) -> FP4 { + let mut f = FP4::new(); + f.a.copy(&FP2::new_int(a)); + f.b.zero(); + f + } + + pub fn new_ints(a: isize,b: isize) -> FP4 { + let mut f = FP4::new(); + f.a.copy(&FP2::new_int(a)); + f.b.copy(&FP2::new_int(b)); + f + } + + pub fn new_copy(x: &FP4) -> FP4 { + let mut f = FP4::new(); + f.a.copy(&x.a); + f.b.copy(&x.b); + f + } + + pub fn new_fp2s(c: &FP2, d: &FP2) -> FP4 { + let mut f = FP4::new(); + f.a.copy(c); + f.b.copy(d); + f + } + + pub fn new_fp2(c: &FP2) -> FP4 { + let mut f = FP4::new(); + f.a.copy(c); + f.b.zero(); + f + } + + pub fn new_fp(c: &FP) -> FP4 { + let mut f = FP4::new(); + f.a.set_fp(c); + f.b.zero(); + f + } + + pub fn new_rand(rng: &mut RAND) -> FP4 { + FP4::new_fp2s(&FP2::new_rand(rng),&FP2::new_rand(rng)) + } + + pub fn set_fp2s(&mut self, c: &FP2, d: &FP2) { + self.a.copy(&c); + self.b.copy(&d); + } + + pub fn set_fp(&mut self, c: &FP) { + self.a.set_fp(&c); + self.b.zero(); + } + + pub fn set_fp2(&mut self, c: &FP2) { + self.a.copy(&c); + self.b.zero(); + } + + pub fn set_fp2h(&mut self, c: &FP2) { + self.b.copy(&c); + self.a.zero(); + } + + /* reduce components mod Modulus */ + pub fn reduce(&mut self) { + self.a.reduce(); + self.b.reduce(); + } + + /* normalise components of w */ + pub fn norm(&mut self) { + self.a.norm(); + self.b.norm(); + } + + pub fn cmove(&mut self, g: &FP4, d: isize) { + self.a.cmove(&g.a, d); + self.b.cmove(&g.b, d); + } + + /* test self=0 ? */ + pub fn iszilch(&self) -> bool { + self.a.iszilch() && self.b.iszilch() + } + + pub fn islarger(&self) -> isize { + if self.iszilch() { + 0 + } else { + let cmp=self.b.islarger(); + if cmp!=0 { + cmp + } else { + self.a.islarger() + } + } + } + + pub fn tobytes(&self,bf: &mut [u8]) { + const MB:usize = 2*(big::MODBYTES as usize); + let mut t: [u8; MB] = [0; MB]; + self.b.tobytes(&mut t); + for i in 0..MB { + bf[i]=t[i]; + } + self.a.tobytes(&mut t); + for i in 0..MB { + bf[i+MB]=t[i]; + } + } + + pub fn frombytes(bf: &[u8]) -> FP4 { + const MB:usize = 2*(big::MODBYTES as usize); + let mut t: [u8; MB] = [0; MB]; + for i in 0..MB { + t[i]=bf[i]; + } + let tb=FP2::frombytes(&t); + for i in 0..MB { + t[i]=bf[i+MB]; + } + let ta=FP2::frombytes(&t); + FP4::new_fp2s(&ta,&tb) + } + + + + /* test self=1 ? */ + pub fn isunity(&self) -> bool { + let one = FP2::new_int(1); + self.a.equals(&one) && self.b.iszilch() + } + + /* test is w real? That is in a+ib test b is zero */ + pub fn isreal(&mut self) -> bool { + self.b.iszilch() + } + /* extract real part a */ + pub fn real(&self) -> FP2 { + FP2::new_copy(&self.a) + } + + pub fn geta(&self) -> FP2 { + FP2::new_copy(&self.a) + } + /* extract imaginary part b */ + pub fn getb(&self) -> FP2 { + FP2::new_copy(&self.b) + } + + /* test self=x */ + pub fn equals(&self, x: &FP4) -> bool { + self.a.equals(&x.a) && self.b.equals(&x.b) + } + /* copy self=x */ + pub fn copy(&mut self, x: &FP4) { + self.a.copy(&x.a); + self.b.copy(&x.b); + } + + /* set self=0 */ + pub fn zero(&mut self) { + self.a.zero(); + self.b.zero(); + } + + /* set self=1 */ + pub fn one(&mut self) { + self.a.one(); + self.b.zero(); + } + + pub fn sign(&self) -> isize { + let mut p1=self.a.sign(); + let mut p2=self.b.sign(); + if fp::BIG_ENDIAN_SIGN { + let u=self.b.iszilch() as isize; + p2^=(p1^p2)&u; + p2 + } else { + let u=self.a.iszilch() as isize; + p1^=(p1^p2)&u; + p1 + } + } + + /* negate self mod Modulus */ + pub fn neg(&mut self) { + self.norm(); + let mut m = FP2::new_copy(&self.a); + let mut t = FP2::new(); + + m.add(&self.b); + m.neg(); + t.copy(&m); + t.add(&self.b); + self.b.copy(&m); + self.b.add(&self.a); + self.a.copy(&t); + self.norm(); + } + + /* set to a-ib */ + pub fn conj(&mut self) { + self.b.neg(); + self.norm(); + } + + /* self=-conjugate(self) */ + pub fn nconj(&mut self) { + self.a.neg(); + self.norm(); + } + + /* self+=a */ + pub fn add(&mut self, x: &FP4) { + self.a.add(&x.a); + self.b.add(&x.b); + } + + pub fn padd(&mut self, x: &FP2) { + self.a.add(x); + } + + pub fn dbl(&mut self) { + self.a.dbl(); + self.b.dbl(); + } + + /* self-=a */ + pub fn sub(&mut self, x: &FP4) { + let mut m = FP4::new_copy(x); + m.neg(); + self.add(&m); + } + + /* self-=a */ + pub fn rsub(&mut self, x: &FP4) { + self.neg(); + self.add(x); + } + + /* self*=s, where s is an FP2 */ + pub fn pmul(&mut self, s: &FP2) { + self.a.mul(s); + self.b.mul(s); + } + + /* self*=s, where s is an FP */ + pub fn qmul(&mut self, s: &FP) { + self.a.pmul(s); + self.b.pmul(s); + } + + /* self*=i, where i is an int */ + pub fn imul(&mut self, c: isize) { + self.a.imul(c); + self.b.imul(c); + } + + /* self*=self */ + + pub fn sqr(&mut self) { + let mut t1 = FP2::new_copy(&self.a); + let mut t2 = FP2::new_copy(&self.b); + let mut t3 = FP2::new_copy(&self.a); + + t3.mul(&self.b); + t1.add(&self.b); + t2.mul_ip(); + + t2.add(&self.a); + + t1.norm(); + t2.norm(); + + self.a.copy(&t1); + + self.a.mul(&t2); + + t2.copy(&t3); + t2.mul_ip(); + t2.add(&t3); + t2.norm(); + t2.neg(); + self.a.add(&t2); + + t3.dbl(); + self.b.copy(&t3); + + self.norm(); + } + + /* self*=y */ + pub fn mul(&mut self, y: &FP4) { + //self.norm(); + + let mut t1 = FP2::new_copy(&self.a); + let mut t2 = FP2::new_copy(&self.b); + let mut t3 = FP2::new(); + let mut t4 = FP2::new_copy(&self.b); + + t1.mul(&y.a); + t2.mul(&y.b); + t3.copy(&y.b); + t3.add(&y.a); + t4.add(&self.a); + + t3.norm(); + t4.norm(); + + t4.mul(&t3); + + t3.copy(&t1); + t3.neg(); + t4.add(&t3); + t4.norm(); + + t3.copy(&t2); + t3.neg(); + self.b.copy(&t4); + self.b.add(&t3); + + t2.mul_ip(); + self.a.copy(&t2); + self.a.add(&t1); + + self.norm(); + } + + /* output to hex string */ + #[cfg(feature = "std")] + pub fn tostring(&self) -> String { + format!("[{},{}]", self.a.tostring(), self.b.tostring()) + } + + /* self=1/self */ + pub fn inverse(&mut self,h:Option<&FP>) { + //self.norm(); + + let mut t1 = FP2::new_copy(&self.a); + let mut t2 = FP2::new_copy(&self.b); + + t1.sqr(); + t2.sqr(); + t2.mul_ip(); + t2.norm(); + t1.sub(&t2); + t1.inverse(h); + self.a.mul(&t1); + t1.neg(); + t1.norm(); + self.b.mul(&t1); + } + + /* self*=i where i = sqrt(-1+sqrt(-1)) */ + pub fn times_i(&mut self) { + let mut t = FP2::new_copy(&self.b); + self.b.copy(&self.a); + t.mul_ip(); + self.a.copy(&t); + self.norm(); + if fp::TOWER == fp::POSITOWER { + self.neg(); + self.norm(); + } + } + + /* self=self^p using Frobenius */ + pub fn frob(&mut self, f: &FP2) { + self.a.conj(); + self.b.conj(); + self.b.mul(f); + } + + /* return this^e */ +/* + pub fn pow(&self, e: &BIG) -> FP4 { + let mut w = FP4::new_copy(self); + w.norm(); + let mut z = BIG::new_copy(&e); + let mut r = FP4::new_int(1); + z.norm(); + loop { + let bt = z.parity(); + z.fshr(1); + if bt == 1 { + r.mul(&mut w) + }; + if z.iszilch() { + break; + } + w.sqr(); + } + r.reduce(); + r + } +*/ + /* XTR xtr_a function */ + pub fn xtr_a(&mut self, w: &FP4, y: &FP4, z: &FP4) { + let mut r = FP4::new_copy(w); + let mut t = FP4::new_copy(w); + r.sub(y); + r.norm(); + r.pmul(&self.a); + t.add(y); + t.norm(); + t.pmul(&self.b); + t.times_i(); + + self.copy(&r); + self.add(&t); + self.add(z); + + self.norm(); + } + + /* XTR xtr_d function */ + pub fn xtr_d(&mut self) { + let mut w = FP4::new_copy(self); + self.sqr(); + w.conj(); + w.dbl(); + w.norm(); + self.sub(&w); + self.reduce(); + } + + /* r=x^n using XTR method on traces of FP12s */ + pub fn xtr_pow(&self, n: &BIG) -> FP4 { + let mut sf = FP4::new_copy(self); + sf.norm(); + let mut a = FP4::new_int(3); + let mut b = FP4::new_copy(&sf); + let mut c = FP4::new_copy(&b); + c.xtr_d(); + let mut t = FP4::new(); + let mut r = FP4::new(); + + let par = n.parity(); + let mut v = BIG::new_copy(n); + v.norm(); + v.fshr(1); + if par == 0 { + v.dec(1); + v.norm(); + } + + let nb = v.nbits(); + for i in (0..nb).rev() { + if v.bit(i) != 1 { + t.copy(&b); + sf.conj(); + c.conj(); + b.xtr_a(&a, &sf, &c); + sf.conj(); + c.copy(&t); + c.xtr_d(); + a.xtr_d(); + } else { + t.copy(&a); + t.conj(); + a.copy(&b); + a.xtr_d(); + b.xtr_a(&c, &sf, &t); + c.xtr_d(); + } + } + if par == 0 { + r.copy(&c) + } else { + r.copy(&b) + } + r.reduce(); + r + } + + /* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */ + pub fn xtr_pow2(&mut self, ck: &FP4, ckml: &FP4, ckm2l: &FP4, a: &BIG, b: &BIG) -> FP4 { + let mut e = BIG::new_copy(a); + let mut d = BIG::new_copy(b); + let mut w = BIG::new(); + e.norm(); + d.norm(); + + let mut cu = FP4::new_copy(ck); // can probably be passed in w/o copying + let mut cv = FP4::new_copy(self); + let mut cumv = FP4::new_copy(ckml); + let mut cum2v = FP4::new_copy(ckm2l); + let mut r = FP4::new(); + let mut t = FP4::new(); + + let mut f2: usize = 0; + while d.parity() == 0 && e.parity() == 0 { + d.fshr(1); + e.fshr(1); + f2 += 1; + } + + while BIG::comp(&d, &e) != 0 { + if BIG::comp(&d, &e) > 0 { + w.copy(&e); + w.imul(4); + w.norm(); + if BIG::comp(&d, &w) <= 0 { + w.copy(&d); + d.copy(&e); + e.rsub(&w); + e.norm(); + + t.copy(&cv); + t.xtr_a(&cu, &cumv, &cum2v); + cum2v.copy(&cumv); + cum2v.conj(); + cumv.copy(&cv); + cv.copy(&cu); + cu.copy(&t); + } else if d.parity() == 0 { + d.fshr(1); + r.copy(&cum2v); + r.conj(); + t.copy(&cumv); + t.xtr_a(&cu, &cv, &r); + cum2v.copy(&cumv); + cum2v.xtr_d(); + cumv.copy(&t); + cu.xtr_d(); + } else if e.parity() == 1 { + d.sub(&e); + d.norm(); + d.fshr(1); + t.copy(&cv); + t.xtr_a(&cu, &cumv, &cum2v); + cu.xtr_d(); + cum2v.copy(&cv); + cum2v.xtr_d(); + cum2v.conj(); + cv.copy(&t); + } else { + w.copy(&d); + d.copy(&e); + d.fshr(1); + e.copy(&w); + t.copy(&cumv); + t.xtr_d(); + cumv.copy(&cum2v); + cumv.conj(); + cum2v.copy(&t); + cum2v.conj(); + t.copy(&cv); + t.xtr_d(); + cv.copy(&cu); + cu.copy(&t); + } + } + if BIG::comp(&d, &e) < 0 { + w.copy(&d); + w.imul(4); + w.norm(); + if BIG::comp(&e, &w) <= 0 { + e.sub(&d); + e.norm(); + t.copy(&cv); + t.xtr_a(&cu, &cumv, &cum2v); + cum2v.copy(&cumv); + cumv.copy(&cu); + cu.copy(&t); + } else if e.parity() == 0 { + w.copy(&d); + d.copy(&e); + d.fshr(1); + e.copy(&w); + t.copy(&cumv); + t.xtr_d(); + cumv.copy(&cum2v); + cumv.conj(); + cum2v.copy(&t); + cum2v.conj(); + t.copy(&cv); + t.xtr_d(); + cv.copy(&cu); + cu.copy(&t); + } else if d.parity() == 1 { + w.copy(&e); + e.copy(&d); + w.sub(&d); + w.norm(); + d.copy(&w); + d.fshr(1); + t.copy(&cv); + t.xtr_a(&cu, &cumv, &cum2v); + cumv.conj(); + cum2v.copy(&cu); + cum2v.xtr_d(); + cum2v.conj(); + cu.copy(&cv); + cu.xtr_d(); + cv.copy(&t); + } else { + d.fshr(1); + r.copy(&cum2v); + r.conj(); + t.copy(&cumv); + t.xtr_a(&cu, &cv, &r); + cum2v.copy(&cumv); + cum2v.xtr_d(); + cumv.copy(&t); + cu.xtr_d(); + } + } + } + r.copy(&cv); + r.xtr_a(&cu, &cumv, &cum2v); + for _ in 0..f2 { + r.xtr_d() + } + r = r.xtr_pow(&d); + r + } + + /* this/=2 */ + pub fn div2(&mut self) { + self.a.div2(); + self.b.div2(); + } + + pub fn div_i(&mut self) { + let mut u = FP2::new_copy(&self.a); + let v = FP2::new_copy(&self.b); + u.div_ip(); + self.a.copy(&v); + self.b.copy(&u); + if fp::TOWER == fp::POSITOWER { + self.neg(); + self.norm(); + } + } +/* + pub fn pow(&mut self, e: &BIG) { + let mut w = FP4::new_copy(self); + let mut z = BIG::new_copy(&e); + let mut r = FP4::new_int(1); + loop { + let bt = z.parity(); + z.fshr(1); + if bt == 1 { + r.mul(&mut w) + }; + if z.iszilch() { + break; + } + w.sqr(); + } + r.reduce(); + self.copy(&r); + } +*/ + +/* PFGE24S + + pub fn qr(&mut self,h:Option<&mut FP>) -> isize { + let mut c=FP4::new_copy(self); + c.conj(); + c.mul(self); + c.geta().qr(h) + } + + // sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) + // returns true if this is QR + pub fn sqrt(&mut self,h:Option<&FP>) { + if self.iszilch() { + return; + } + + let mut a = FP2::new_copy(&self.a); + let mut b = FP2::new_copy(&self.a); + let mut s = FP2::new_copy(&self.b); + let mut t = FP2::new_copy(&self.a); + let mut hint = FP::new(); + + s.sqr(); + a.sqr(); + s.mul_ip(); + s.norm(); + a.sub(&s); + + s.copy(&a); s.norm(); + + s.sqrt(h); + + a.copy(&t); + a.add(&s); + a.norm(); + a.div2(); + + + b.copy(&self.b); b.div2(); + let qr=a.qr(Some(&mut hint)); + + +// tweak hint - multiply old hint by Norm(1/Beta)^e where Beta is irreducible polynomial + s.copy(&a); + let mut twk = FP::new_big(&BIG::new_ints(&rom::TWK)); + twk.mul(&hint); + s.div_ip(); s.norm(); + + a.cmove(&s,1-qr); + hint.cmove(&twk,1-qr); + + self.a.copy(&a); self.a.sqrt(Some(&hint)); + s.copy(&a); s.inverse(Some(&hint)); + s.mul(&self.a); + self.b.copy(&s); self.b.mul(&b); + t.copy(&self.a); + + self.a.cmove(&self.b,1-qr); + self.b.cmove(&t,1-qr); + + let sgn=self.sign(); + let mut nr=FP4::new_copy(&self); + nr.neg(); nr.norm(); + self.cmove(&nr,sgn); + } +PFGE24F */ +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/hpke.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/hpke.rs new file mode 100644 index 000000000000..6aa0b7ce0f19 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/hpke.rs @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::bn254::ecp; +use crate::bn254::ecdh; + +use crate::hmac; +use crate::rand::RAND; + +const GROUP: usize = ecdh::EGS; +const POINT: usize = 2*ecdh::EFS+1; + +const MAX_LABEL: usize = 20; // may need adjustment + +#[allow(non_snake_case)] +fn reverse (x: &mut [u8]) { + let lx=x.len(); + for i in 0..lx/2 { + let ch=x[i]; + x[i]=x[lx-i-1]; + x[lx-i-1]=ch; + } +} +/* +fn printbinary(array: &[u8]) { + for i in 0..array.len() { + print!("{:02X}", array[i]) + } + println!("") +} +*/ + +#[allow(non_snake_case)] +fn labeledExtract(prk: &mut [u8],salt: Option<&[u8]>,suite_id: &[u8],label: &str,ikm: Option<&[u8]>) { + let rfc="HPKE-v1"; + let prefix1=rfc.as_bytes(); + let prefix2=label.as_bytes(); + let mut likm: [u8; 18+MAX_LABEL+2*POINT] = [0; 18+MAX_LABEL+2*POINT]; + let mut k=0; + for i in 0..prefix1.len() { + likm[k]=prefix1[i]; + k+=1; + } + for i in 0..suite_id.len() { + likm[k]=suite_id[i]; + k+=1; + } + for i in 0..prefix2.len() { + likm[k]=prefix2[i]; + k+=1; + } + if let Some(sikm) = ikm { + for i in 0..sikm.len() { + likm[k]=sikm[i]; + k+=1; + } + } + hmac::hkdf_extract(hmac::MC_SHA2,ecp::HASH_TYPE,prk,salt,&likm[0..k]); +} + +#[allow(non_snake_case)] +fn labeledExpand(okm: &mut [u8],prk: &[u8],suite_id: &[u8],label: &str,info: Option<&[u8]>,el: usize) { + let mut ar: [u8; 2] = [0; 2]; + let rfc="HPKE-v1"; + let prefix1=rfc.as_bytes(); + let prefix2=label.as_bytes(); + hmac::inttobytes(el,&mut ar); + let mut linfo: [u8; 20+MAX_LABEL+3*POINT] = [0; 20+MAX_LABEL+3*POINT]; + linfo[0]=ar[0]; + linfo[1]=ar[1]; + let mut k=2; + for i in 0..prefix1.len() { + linfo[k]=prefix1[i]; + k+=1; + } + for i in 0..suite_id.len() { + linfo[k]=suite_id[i]; + k+=1; + } + for i in 0..prefix2.len() { + linfo[k]=prefix2[i]; + k+=1; + } + if let Some(sinfo) = info { + for i in 0..sinfo.len() { + linfo[k]=sinfo[i]; + k+=1; + } + } + hmac:: hkdf_expand(hmac::MC_SHA2,ecp::HASH_TYPE,okm,el,prk,&linfo[0..k]); +} + +#[allow(non_snake_case)] +fn extractAndExpand(config_id: usize,okm: &mut [u8],dh: &[u8],context: &[u8]) { + let kem = config_id&255; + let txt="KEM"; + let mut suite_id: [u8;5] = [0;5]; + let mut kem_id: [u8; 2] = [0; 2]; + let ckem=txt.as_bytes(); + hmac::inttobytes(kem,&mut kem_id); + let mut k=0; + for i in 0..ckem.len() { + suite_id[k]=ckem[i]; + k+=1; + } + suite_id[k]=kem_id[0]; k+=1; + suite_id[k]=kem_id[1]; + + let mut prk: [u8;ecp::HASH_TYPE]=[0;ecp::HASH_TYPE]; + labeledExtract(&mut prk,None,&suite_id,"eae_prk",Some(dh)); + labeledExpand(okm,&prk,&suite_id,"shared_secret",Some(&context),ecp::HASH_TYPE); +} + +#[allow(non_snake_case)] +pub fn deriveKeyPair(config_id: usize,mut sk: &mut [u8],mut pk: &mut [u8],seed: &[u8]) -> bool { + let mut counter=0; + let kem = config_id&255; + let txt="KEM"; + let mut suite_id: [u8;5] = [0;5]; + let mut kem_id: [u8; 2] = [0; 2]; + let ckem=txt.as_bytes(); + hmac::inttobytes(kem,&mut kem_id); + let mut k=0; + for i in 0..ckem.len() { + suite_id[k]=ckem[i]; + k+=1; + } + suite_id[k]=kem_id[0]; k+=1; + suite_id[k]=kem_id[1]; + + let mut prk: [u8;ecp::HASH_TYPE]=[0;ecp::HASH_TYPE]; + labeledExtract(&mut prk,None,&suite_id,"dkp_prk",Some(&seed)); + + //println!("prk= {:02X?}",prk); + + if kem==32 || kem==33 { // RFC7748 + labeledExpand(&mut sk,&prk,&suite_id,"sk",None,GROUP); + reverse(&mut sk); + if kem==32 { + sk[GROUP-1]&=248; + sk[0]&=127; + sk[0]|=64; + } else { + sk[GROUP-1]&=252; + sk[0]|=128; + } + } else { + let mut bit_mask=0xff; + if kem==18 { + bit_mask=1; + } + for i in 0..GROUP { + sk[i]=0; + } + while !ecdh::in_range(&sk) && counter<256 { + let mut info: [u8;1]=[0;1]; + info[0]=counter as u8; + labeledExpand(sk,&prk,&suite_id,"candidate",Some(&info),GROUP); + sk[0] &= bit_mask as u8; + counter += 1; + } + } + //for i in 0..sk.len() { +// print!({} +// println!("SK= {:02X?}",sk); + // println!("kem= {}",kem); + //println!("counter= {}",counter); + ecdh::key_pair_generate(None::<&mut RAND>, &mut sk, &mut pk); + if kem==32 || kem==33 { + reverse(&mut pk); + } + counter<256 +} + +#[allow(non_snake_case)] +pub fn encap(config_id: usize,skE: &[u8],z: &mut [u8],pkE: &[u8],pkR: &[u8]) { + let pklen=pkE.len(); + let mut dh: [u8; ecdh::EFS] = [0; ecdh::EFS]; + let mut kemcontext: [u8; 2*POINT] = [0;2*POINT]; + let kem = config_id&255; + let mut rev: [u8; POINT]=[0; POINT]; + + if kem==32 || kem==33 { + for i in 0..pklen { + rev[i]=pkR[i]; + } + reverse(&mut rev[0..pklen]); + ecdh::ecpsvdp_dh(&skE, &rev[0..pklen], &mut dh, 0); + reverse(&mut dh[0..pklen]); + } else { + ecdh::ecpsvdp_dh(&skE, &pkR, &mut dh, 0); + } + let mut k=0; + for i in 0..pklen { + kemcontext[k]=pkE[i]; + k+=1; + } + for i in 0..pklen { + kemcontext[k]=pkR[i]; + k+=1; + } +//print!("e dh= "); printbinary(&dh[0..pklen]); + extractAndExpand(config_id,z,&dh,&kemcontext[0..k]); +} + +#[allow(non_snake_case)] +pub fn decap(config_id: usize,skR: &[u8],z: &mut [u8],pkE: &[u8],pkR: &[u8]) { + let pklen=pkE.len(); + let mut dh: [u8; ecdh::EFS] = [0; ecdh::EFS]; + let mut kemcontext: [u8; 2*POINT] = [0;2*POINT]; + let mut rev: [u8; POINT]=[0; POINT]; + let kem = config_id&255; + + if kem==32 || kem==33 { + for i in 0..pklen { + rev[i]=pkE[i]; + } + reverse(&mut rev[0..pklen]); + ecdh::ecpsvdp_dh(&skR, &rev[0..pklen], &mut dh, 0); + reverse(&mut dh[0..pklen]); + } else { + ecdh::ecpsvdp_dh(&skR, &pkE, &mut dh, 0); + } + + let mut k=0; + for i in 0..pklen { + kemcontext[k]=pkE[i]; + k+=1; + } + for i in 0..pklen { // not a mistake + kemcontext[k]=pkR[i]; + k+=1; + } +//print!("d dh= "); printbinary(&dh[0..pklen]); + extractAndExpand(config_id,z,&dh,&kemcontext[0..k]); +} + +#[allow(non_snake_case)] +pub fn authencap(config_id: usize,skE: &[u8],skS: &[u8],z: &mut [u8],pkE: &[u8],pkR: &[u8],pkS: &[u8]) { + let mut dh: [u8; 2*ecdh::EFS] = [0; 2*ecdh::EFS]; + let mut dh1: [u8; ecdh::EFS] = [0; ecdh::EFS]; + + let mut kemcontext: [u8; 3*POINT] = [0;3*POINT]; + let kem = config_id&255; + let pklen=pkE.len(); + let mut rev: [u8; POINT]=[0; POINT]; + + + if kem==32 || kem==33 { + for i in 0..pklen { + rev[i]=pkR[i]; + } + reverse(&mut rev[0..pklen]); + ecdh::ecpsvdp_dh(&skE, &rev[0..pklen], &mut dh, 0); + ecdh::ecpsvdp_dh(&skS, &rev[0..pklen], &mut dh1, 0); + reverse(&mut dh[0..pklen]); + reverse(&mut dh1[0..pklen]); + } else { + ecdh::ecpsvdp_dh(&skE, &pkR, &mut dh, 0); + ecdh::ecpsvdp_dh(&skS, &pkR, &mut dh1, 0); + } + + for i in 0..ecdh::EFS { + dh[i+ecdh::EFS] = dh1[i]; + } + + for i in 0..pklen { + kemcontext[i]=pkE[i]; + kemcontext[pklen+i]= pkR[i]; + kemcontext[2*pklen+i]= pkS[i]; + } +//print!("e dh= "); printbinary(&dh[0..pklen]); +//print!("e kemcontext= "); printbinary(&kemcontext[0..3*pklen]); + extractAndExpand(config_id,z,&dh,&kemcontext[0..3*pklen]); +} + +#[allow(non_snake_case)] +pub fn authdecap(config_id: usize,skR: &[u8],z: &mut [u8],pkE: &[u8],pkR: &[u8],pkS: &[u8]) { + let mut dh: [u8; 2*ecdh::EFS] = [0; 2*ecdh::EFS]; + let mut dh1: [u8; ecdh::EFS] = [0; ecdh::EFS]; + let mut kemcontext: [u8; 3*POINT] = [0;3*POINT]; + let kem = config_id&255; + let pklen=pkE.len(); + let mut rev: [u8; POINT]=[0; POINT]; + + if kem==32 || kem==33 { + for i in 0..pklen { + rev[i]=pkE[i]; + } + reverse(&mut rev[0..pklen]); + ecdh::ecpsvdp_dh(&skR, &rev[0..pklen], &mut dh, 0); + for i in 0..pklen { + rev[i]=pkS[i]; + } + reverse(&mut rev[0..pklen]); + ecdh::ecpsvdp_dh(&skR, &rev[0..pklen], &mut dh1, 0); + reverse(&mut dh[0..pklen]); + reverse(&mut dh1[0..pklen]); + } else { + ecdh::ecpsvdp_dh(&skR, &pkE, &mut dh, 0); + ecdh::ecpsvdp_dh(&skR, &pkS, &mut dh1, 0); + } + + for i in 0..ecdh::EFS { + dh[i+ecdh::EFS] = dh1[i]; + } + + for i in 0..pklen { + kemcontext[i]=pkE[i]; + kemcontext[pklen+i]= pkR[i]; + kemcontext[2*pklen+i]= pkS[i]; + } +//print!("d dh= "); printbinary(&dh[0..pklen]); +//print!("d kemcontext= "); printbinary(&kemcontext[0..3*pklen]); + extractAndExpand(config_id,z,&dh,&kemcontext[0..3*pklen]); +} + +#[allow(non_snake_case)] +pub fn keyschedule(config_id: usize,key: &mut [u8],nonce: &mut [u8],exp_secret: &mut [u8],mode: usize,z: &mut [u8],info: &[u8],psk: Option<&[u8]>,pskID: Option<&[u8]>) { + + let mut context: [u8; 1+2*ecp::HASH_TYPE] = [0; 1+2*ecp::HASH_TYPE]; + let kem=config_id&255; + let kdf=(config_id>>8)&3; + let aead=(config_id>>10)&3; + + let txt="HPKE"; + let ckem=txt.as_bytes(); + let mut suite_id: [u8;10] = [0;10]; + let mut num: [u8; 2] = [0; 2]; + + let mut k=0; + for i in 0..ckem.len() { + suite_id[k]=ckem[i]; + k+=1; + } + hmac::inttobytes(kem,&mut num); + suite_id[k]=num[0]; k+=1; + suite_id[k]=num[1]; k+=1; + hmac::inttobytes(kdf,&mut num); + suite_id[k]=num[0]; k+=1; + suite_id[k]=num[1]; k+=1; + hmac::inttobytes(aead,&mut num); + suite_id[k]=num[0]; k+=1; + suite_id[k]=num[1]; + + let mut k=0; + let mut h: [u8; 64] = [0; 64]; + let mut secret: [u8; 64] = [0; 64]; + + context[k]=mode as u8; k+=1; + + labeledExtract(&mut h,None,&suite_id,"psk_id_hash",pskID); + for i in 0..ecp::HASH_TYPE { + context[k] = h[i]; k+=1; + } + labeledExtract(&mut h,None,&suite_id,"info_hash",Some(&info)); + for i in 0..ecp::HASH_TYPE { + context[k] = h[i]; k+=1; + } + + //labeledExtract(&mut h,None,&suite_id,"psk_hash",psk); + + //labeledExtract(&mut secret,Some(&h),&suite_id,"secret",Some(z)); + + labeledExtract(&mut secret,Some(z),&suite_id,"secret",psk); + + labeledExpand(key,&secret,&suite_id,"key",Some(&context[0..k]),ecp::AESKEY); + labeledExpand(nonce,&secret,&suite_id,"base_nonce",Some(&context[0..k]),12); + labeledExpand(exp_secret,&secret,&suite_id,"exp",Some(&context[0..k]),ecp::HASH_TYPE); +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/mod.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/mod.rs new file mode 100644 index 000000000000..89eca15de70e --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/mod.rs @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +pub mod big; +pub mod bls; +pub mod dbig; +pub mod ecp; +pub mod ecp2; +pub mod fp; +pub mod fp12; +pub mod fp2; +pub mod fp4; +pub mod mpin; +pub mod pair; +pub mod rom; diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/mpin.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/mpin.rs new file mode 100644 index 000000000000..3b3121e5dda5 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/mpin.rs @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::bn254::big; +use crate::bn254::big::BIG; +use crate::bn254::ecp; +use crate::bn254::ecp::ECP; +use crate::bn254::ecp2::ECP2; +use crate::bn254::fp12::FP12; +use crate::bn254::pair; +use crate::bn254::rom; +use crate::bn254::fp::FP; +use crate::bn254::dbig::DBIG; + +use crate::hmac; +use crate::rand::RAND; + +/* MPIN 128-bit API Functions */ + +/* Configure mode of operation */ + +pub const EFS: usize = big::MODBYTES as usize; +pub const EGS: usize = big::MODBYTES as usize; +pub const BAD_PARAMS: isize = -11; +pub const INVALID_POINT: isize = -14; +pub const WRONG_ORDER: isize = -18; +pub const BAD_PIN: isize = -19; +pub const SHA256: usize = 32; +pub const SHA384: usize = 48; +pub const SHA512: usize = 64; + +/* Configure your PIN here */ + +pub const MAXPIN: i32 = 10000; /* PIN less than this */ +pub const PBLEN: i32 = 14; /* Number of bits in PIN */ + +fn ceil(a: usize,b: usize) -> usize { + (a-1)/b+1 +} + +#[allow(non_snake_case)] +pub fn encode_to_curve(dst: &[u8],id: &[u8],hcid: &mut [u8]) { + let q = BIG::new_ints(&rom::MODULUS); + let k=q.nbits(); + let r = BIG::new_ints(&rom::CURVE_ORDER); + let m=r.nbits(); + let el=ceil(k+ceil(m,2),8); + let mut okm: [u8;512]=[0;512]; + hmac::xmd_expand(hmac::MC_SHA2,ecp::HASH_TYPE,&mut okm,el,&dst,&id); + let mut fd: [u8;256]=[0;256]; + for j in 0..el { + fd[j]=okm[j]; + } + let mut dx=DBIG::frombytes(&fd[0..el]); + let u=FP::new_big(&dx.dmod(&q)); + let mut P=ECP::map2point(&u); + P.cfp(); + P.affine(); + P.tobytes(hcid,false); +} + +/* create random secret S */ +pub fn random_generate(rng: &mut RAND, s: &mut [u8]) -> isize { + let r = BIG::new_ints(&rom::CURVE_ORDER); + let sc = BIG::randtrunc(&r, 16 * ecp::AESKEY, rng); + sc.tobytes(s); + 0 +} + +/* Extract PIN from TOKEN for identity CID */ +#[allow(non_snake_case)] +pub fn extract_pin(cid: &[u8], pin: i32, token: &mut [u8]) -> isize { + let mut P = ECP::frombytes(&token); + if P.is_infinity() { + return INVALID_POINT; + } + let mut R = ECP::frombytes(&cid); + if R.is_infinity() { + return INVALID_POINT; + } + + R = R.pinmul(pin%MAXPIN, PBLEN); + P.sub(&R); + P.tobytes(token, false); + 0 +} + +/* Implement step 2 on client side of MPin protocol */ +#[allow(non_snake_case)] +pub fn client_2(x: &[u8], y: &[u8], sec: &mut [u8]) -> isize { + let r = BIG::new_ints(&rom::CURVE_ORDER); + let mut P = ECP::frombytes(sec); + if P.is_infinity() { + return INVALID_POINT; + } + + let mut px = BIG::frombytes(x); + let py = BIG::frombytes(y); + px.add(&py); + px.rmod(&r); + + P = pair::g1mul(&P, &px); + P.neg(); + P.tobytes(sec, false); + 0 +} + +/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */ +#[allow(non_snake_case)] +pub fn get_client_secret(s: &mut [u8], idhtc: &[u8], cst: &mut [u8]) -> isize { + let sx=BIG::frombytes(s); + let P=ECP::frombytes(idhtc); + if P.is_infinity() { + return INVALID_POINT; + } + pair::g1mul(&P, &sx).tobytes(cst, false); + 0 +} + +/* Implement step 1 on client side of MPin protocol */ +#[allow(non_snake_case)] +pub fn client_1( + cid: &[u8], + rng: Option<&mut RAND>, + x: &mut [u8], + pin: usize, + token: &[u8], + sec: &mut [u8], + xid: &mut [u8] +) -> isize { + let r = BIG::new_ints(&rom::CURVE_ORDER); + let sx: BIG; + + if let Some(rd) = rng { + sx = BIG::randtrunc(&r, 16 * ecp::AESKEY, rd); + sx.tobytes(x); + } else { + sx = BIG::frombytes(x); + } + let mut P=ECP::frombytes(cid); + if P.is_infinity() { + return INVALID_POINT; + } + + let mut T = ECP::frombytes(&token); + if T.is_infinity() { + return INVALID_POINT; + } + + let W = P.pinmul((pin as i32) % MAXPIN, PBLEN); + T.add(&W); + + P = pair::g1mul(&P, &sx); + P.tobytes(xid, false); + + T.tobytes(sec, false); + 0 +} + + +/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */ +#[allow(non_snake_case)] +pub fn get_server_secret(s: &[u8], sst: &mut [u8]) -> isize { + let mut Q = ECP2::generator(); + let sc = BIG::frombytes(s); + Q = pair::g2mul(&Q, &sc); + Q.tobytes(sst,false); + 0 +} + +/* Implement step 2 of MPin protocol on server side */ +#[allow(non_snake_case)] +pub fn server( + hid: &[u8], + y: &[u8], + sst: &[u8], + xid: &[u8], + msec: &[u8], +) -> isize { + let Q = ECP2::generator(); + let sQ = ECP2::frombytes(&sst); + if sQ.is_infinity() { + return INVALID_POINT; + } + let mut R = ECP::frombytes(&xid); + if R.is_infinity() { + return INVALID_POINT; + } + + let sy = BIG::frombytes(&y); + let mut P = ECP::frombytes(&hid); + if P.is_infinity() { + return INVALID_POINT; + } + + P = pair::g1mul(&P, &sy); + P.add(&R); + R = ECP::frombytes(&msec); + if R.is_infinity() { + return INVALID_POINT; + } + + let mut g: FP12; + g = pair::ate2(&Q, &R, &sQ, &P); + g = pair::fexp(&g); + + if !g.isunity() { + return BAD_PIN; + } + 0 +} + diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/pair.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/pair.rs new file mode 100644 index 000000000000..360fe0fcd07a --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/pair.rs @@ -0,0 +1,1038 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::bn254::big::BIG; +use crate::bn254::ecp; +use crate::bn254::ecp::ECP; +use crate::bn254::ecp2::ECP2; +use crate::bn254::fp::FP; +use crate::bn254::fp12; +use crate::bn254::fp12::FP12; +use crate::bn254::fp2::FP2; +use crate::bn254::fp4::FP4; +use crate::bn254::rom; + +#[allow(unused_imports)] +use crate::bn254::dbig::DBIG; + +#[allow(non_snake_case)] +fn dbl(A: &mut ECP2, aa: &mut FP2, bb: &mut FP2, cc: &mut FP2) { + cc.copy(&A.getpx()); //X + let mut yy = FP2::new_copy(&A.getpy()); //Y + bb.copy(&A.getpz()); //Z + + aa.copy(&yy); //Y + aa.mul(&bb); //YZ + cc.sqr(); //X^2 + yy.sqr(); //Y^2 + bb.sqr(); //Z^2 + + aa.dbl(); + aa.neg(); aa.norm(); + aa.mul_ip(); + aa.norm(); + + let sb = 3 * rom::CURVE_B_I; + bb.imul(sb); + cc.imul(3); + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + yy.mul_ip(); + yy.norm(); + cc.mul_ip(); + cc.norm(); + } + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + bb.mul_ip(); + bb.norm(); + } + bb.sub(&yy); + bb.norm(); + A.dbl(); +} + +#[allow(non_snake_case)] +fn add(A: &mut ECP2, B: &ECP2, aa: &mut FP2, bb: &mut FP2, cc: &mut FP2) { + + aa.copy(&A.getpx()); // X1 + cc.copy(&A.getpy()); // Y1 + let mut t1 = FP2::new_copy(&A.getpz()); // Z1 + bb.copy(&A.getpz()); // Z1 + + t1.mul(&B.getpy()); // T1=Z1.Y2 + bb.mul(&B.getpx()); // T2=Z1.X2 + + aa.sub(&bb); + aa.norm(); // X1=X1-Z1.X2 + cc.sub(&t1); + cc.norm(); // Y1=Y1-Z1.Y2 + + t1.copy(&aa); // T1=X1-Z1.X2 + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + aa.mul_ip(); + aa.norm(); + } + + t1.mul(&B.getpy()); // T1=(X1-Z1.X2).Y2 + + bb.copy(&cc); // T2=Y1-Z1.Y2 + bb.mul(&B.getpx()); // T2=(Y1-Z1.Y2).X2 + bb.sub(&t1); + bb.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 + + cc.neg(); + cc.norm(); // Y1=-(Y1-Z1.Y2).Xs + + A.add(B); +} + +#[allow(non_snake_case)] +fn linedbl(A: &mut ECP2, qx: &FP, qy: &FP) -> FP12 { + let mut a = FP4::new(); + let mut b = FP4::new(); + let mut c = FP4::new(); + let mut aa = FP2::new(); + let mut bb = FP2::new(); + let mut cc = FP2::new(); + + dbl(A,&mut aa,&mut bb,&mut cc); + + cc.pmul(qx); + aa.pmul(qy); + + a.copy(&FP4::new_fp2s(&aa, &bb)); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + b.copy(&FP4::new_fp2(&cc)); // L(0,1) | L(0,0) | L(1,0) + } + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + c.copy(&FP4::new_fp2(&cc)); + c.times_i(); + } + let mut res= FP12::new_fp4s(&a, &b, &c); + res.settype(fp12::SPARSER); + res +} + +#[allow(non_snake_case)] +fn lineadd(A: &mut ECP2, B: &ECP2, qx: &FP, qy: &FP) -> FP12 { + let mut a = FP4::new(); + let mut b = FP4::new(); + let mut c = FP4::new(); + let mut aa = FP2::new(); + let mut bb = FP2::new(); + let mut cc = FP2::new(); + + add(A,B,&mut aa,&mut bb,&mut cc); + + cc.pmul(qx); + aa.pmul(qy); + + a.copy(&FP4::new_fp2s(&aa, &bb)); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs + if ecp::SEXTIC_TWIST == ecp::D_TYPE { + b.copy(&FP4::new_fp2(&cc)); // L(0,1) | L(0,0) | L(1,0) + } + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + c.copy(&FP4::new_fp2(&cc)); + c.times_i(); + } + let mut res= FP12::new_fp4s(&a, &b, &c); + res.settype(fp12::SPARSER); + res +} + +/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */ +#[allow(non_snake_case)] +fn lbits(n3: &mut BIG,n: &mut BIG) -> usize { + n.copy(&BIG::new_ints(&rom::CURVE_BNX)); + if ecp::CURVE_PAIRING_TYPE==ecp::BN { + n.pmul(6); + if ecp::SIGN_OF_X==ecp::POSITIVEX { + n.inc(2); + } else { + n.dec(2); + } + } + n.norm(); + n3.copy(&n); + n3.pmul(3); + n3.norm(); + n3.nbits() +} + +/* prepare for multi-pairing */ +pub fn initmp() -> [FP12; ecp::ATE_BITS] { + [FP12::new_int(1); ecp::ATE_BITS] +} + +/* basic Miller loop */ +pub fn miller(r:&mut [FP12]) -> FP12 { + let mut res=FP12::new_int(1); + for i in (1..ecp::ATE_BITS).rev() { + res.sqr(); + res.ssmul(&r[i]); + r[i].zero(); + } + + if ecp::SIGN_OF_X==ecp::NEGATIVEX { + res.conj(); + } + res.ssmul(&r[0]); + r[0].zero(); + res +} + +fn pack(aa: &FP2,bb: &FP2,cc: &FP2) -> FP4 { + let mut i=FP2::new_copy(cc); + i.inverse(None); + let mut a=FP2::new_copy(aa); + let mut b=FP2::new_copy(bb); + a.mul(&i); + b.mul(&i); + FP4::new_fp2s(&a,&b) +} + +fn unpack(t: &FP4, qx: &FP, qy: &FP) -> FP12 { + let b:FP4; + let mut c:FP4; + let w=FP2::new_fp(qx); + + let mut aa=t.geta(); + let bb=t.getb(); + aa.pmul(qy); + let a=FP4::new_fp2s(&aa,&bb); + + if ecp::SEXTIC_TWIST==ecp::D_TYPE { + b=FP4::new_fp2(&w); + c=FP4::new(); + } else { + b=FP4::new(); + c=FP4::new_fp2(&w); c.times_i(); + } + let mut v=FP12::new_fp4s(&a,&b,&c); + v.settype(fp12::SPARSEST); + v +} + +#[allow(non_snake_case)] +pub fn precomp(T: &mut [FP4],GV: &ECP2) { + let mut n = BIG::new(); + let mut n3 = BIG::new(); + let nb=lbits(&mut n3,&mut n); + let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + let mut aa = FP2::new(); + let mut bb = FP2::new(); + let mut cc = FP2::new(); + + let mut P=ECP2::new(); P.copy(GV); + + if (ecp::CURVE_PAIRING_TYPE==ecp::BN) && (ecp::SEXTIC_TWIST==ecp::M_TYPE) { + f.inverse(None); + f.norm(); + } + + let mut A = ECP2::new(); + + A.copy(&P); + let mut NP = ECP2::new(); + NP.copy(&P); + NP.neg(); + let mut j=0; + + for i in (1..nb-1).rev() { + dbl(&mut A,&mut aa,&mut bb,&mut cc); + T[j].copy(&pack(&aa,&bb,&cc)); j+=1; + let bt=n3.bit(i)-n.bit(i); + if bt==1 { + add(&mut A,&P,&mut aa,&mut bb,&mut cc); + T[j].copy(&pack(&aa,&bb,&cc)); j+=1; + } + if bt==-1 { + add(&mut A,&NP,&mut aa,&mut bb,&mut cc); + T[j].copy(&pack(&aa,&bb,&cc)); j+=1; + } + } +/* R-ate fixup required for BN curves */ + if ecp::CURVE_PAIRING_TYPE==ecp::BN { + if ecp::SIGN_OF_X==ecp::NEGATIVEX { + A.neg(); + } + let mut K = ECP2::new(); + K.copy(&P); + K.frob(&f); + add(&mut A,&K,&mut aa,&mut bb,&mut cc); + T[j].copy(&pack(&aa,&bb,&cc)); j+=1; + K.frob(&f); + K.neg(); + add(&mut A,&K,&mut aa,&mut bb,&mut cc); + T[j].copy(&pack(&aa,&bb,&cc)); + } +} + +/* Accumulate another set of line functions for n-pairing, assuming precomputation on G2 */ +#[allow(non_snake_case)] +pub fn another_pc(r:&mut [FP12],T: &[FP4],QV: &ECP) { + let mut n = BIG::new(); + let mut n3 = BIG::new(); + + if QV.is_infinity() { + return; + } + + let nb=lbits(&mut n3,&mut n); + + let mut Q = ECP::new(); + Q.copy(QV); + Q.affine(); + let qx = FP::new_copy(&Q.getpx()); + let qy = FP::new_copy(&Q.getpy()); + + let mut j=0; + for i in (1..nb-1).rev() { + let mut lv=unpack(&T[j],&qx,&qy); j+=1; + let bt=n3.bit(i)-n.bit(i); + if bt==1 { + let lv2=unpack(&T[j],&qx,&qy); j+=1; + lv.smul(&lv2); + } + if bt==-1 { + let lv2=unpack(&T[j],&qx,&qy); j+=1; + lv.smul(&lv2); + } + r[i].ssmul(&lv); + } + if ecp::CURVE_PAIRING_TYPE==ecp::BN { + let mut lv=unpack(&T[j],&qx,&qy); j+=1; + let lv2=unpack(&T[j],&qx,&qy); + lv.smul(&lv2); + r[0].ssmul(&lv); + } +} + +/* Accumulate another set of line functions for n-pairing */ +#[allow(non_snake_case)] +pub fn another(r:&mut [FP12],P1: &ECP2,Q1: &ECP) { + let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + let mut n = BIG::new(); + let mut n3 = BIG::new(); + let mut K = ECP2::new(); + + if Q1.is_infinity() { + return; + } +// P is needed in affine form for line function, Q for (Qx,Qy) extraction + let mut P = ECP2::new(); + P.copy(P1); + P.affine(); + let mut Q = ECP::new(); + Q.copy(Q1); + Q.affine(); + + if (ecp::CURVE_PAIRING_TYPE==ecp::BN) && (ecp::SEXTIC_TWIST==ecp::M_TYPE) { + f.inverse(None); + f.norm(); + } + + let qx = FP::new_copy(&Q.getpx()); + let qy = FP::new_copy(&Q.getpy()); + let mut A = ECP2::new(); + + A.copy(&P); + let mut NP = ECP2::new(); + NP.copy(&P); + NP.neg(); + + let nb=lbits(&mut n3,&mut n); + + for i in (1..nb-1).rev() { + let mut lv=linedbl(&mut A,&qx,&qy); + + let bt=n3.bit(i)-n.bit(i); + if bt==1 { + let lv2=lineadd(&mut A,&P,&qx,&qy); + lv.smul(&lv2); + } + if bt==-1 { + let lv2=lineadd(&mut A,&NP,&qx,&qy); + lv.smul(&lv2); + } + r[i].ssmul(&lv); + } + +/* R-ate fixup required for BN curves */ + if ecp::CURVE_PAIRING_TYPE==ecp::BN { + if ecp::SIGN_OF_X==ecp::NEGATIVEX { + A.neg(); + } + K.copy(&P); + K.frob(&f); + let mut lv=lineadd(&mut A,&K,&qx,&qy); + K.frob(&f); + K.neg(); + let lv2=lineadd(&mut A,&K,&qx,&qy); + lv.smul(&lv2); + r[0].ssmul(&lv); + } +} + +#[allow(non_snake_case)] +/* Optimal R-ate pairing */ +pub fn ate(P1: &ECP2, Q1: &ECP) -> FP12 { + let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + let mut n = BIG::new(); + let mut n3 = BIG::new(); + let mut K = ECP2::new(); + + if Q1.is_infinity() { + return FP12::new_int(1); + } + + if (ecp::CURVE_PAIRING_TYPE == ecp::BN) && (ecp::SEXTIC_TWIST == ecp::M_TYPE) { + f.inverse(None); + f.norm(); + } + let mut P = ECP2::new(); + P.copy(P1); + P.affine(); + let mut Q = ECP::new(); + Q.copy(Q1); + Q.affine(); + + let qx = FP::new_copy(&Q.getpx()); + let qy = FP::new_copy(&Q.getpy()); + + let mut A = ECP2::new(); + let mut r = FP12::new_int(1); + + A.copy(&P); + let mut NP = ECP2::new(); + NP.copy(&P); + NP.neg(); + + let nb=lbits(&mut n3,&mut n); + + for i in (1..nb - 1).rev() { + r.sqr(); + let mut lv = linedbl(&mut A, &qx, &qy); + let bt = n3.bit(i) - n.bit(i); + if bt == 1 { + let lv2 = lineadd(&mut A, &P, &qx, &qy); + lv.smul(&lv2); + } + if bt == -1 { + let lv2 = lineadd(&mut A, &NP, &qx, &qy); + lv.smul(&lv2); + } + r.ssmul(&lv); + } + + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + r.conj(); + } + + /* R-ate fixup required for BN curves */ + + if ecp::CURVE_PAIRING_TYPE == ecp::BN { + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + A.neg(); + } + + K.copy(&P); + K.frob(&f); + + let mut lv = lineadd(&mut A, &K, &qx, &qy); + K.frob(&f); + K.neg(); + let lv2 = lineadd(&mut A, &K, &qx, &qy); + lv.smul(&lv2); + r.ssmul(&lv); + } + + r +} + +#[allow(non_snake_case)] +/* Optimal R-ate double pairing e(P,Q).e(R,S) */ +pub fn ate2(P1: &ECP2, Q1: &ECP, R1: &ECP2, S1: &ECP) -> FP12 { + let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + let mut n = BIG::new(); + let mut n3 = BIG::new(); + let mut K = ECP2::new(); + + if Q1.is_infinity() { + return ate(R1,S1); + } + if S1.is_infinity() { + return ate(P1,Q1); + } + + if (ecp::CURVE_PAIRING_TYPE == ecp::BN) && (ecp::SEXTIC_TWIST == ecp::M_TYPE) { + f.inverse(None); + f.norm(); + } + + let mut P = ECP2::new(); + P.copy(P1); + P.affine(); + let mut Q = ECP::new(); + Q.copy(Q1); + Q.affine(); + let mut R = ECP2::new(); + R.copy(R1); + R.affine(); + let mut S = ECP::new(); + S.copy(S1); + S.affine(); + + let qx = FP::new_copy(&Q.getpx()); + let qy = FP::new_copy(&Q.getpy()); + + let sx = FP::new_copy(&S.getpx()); + let sy = FP::new_copy(&S.getpy()); + + let mut A = ECP2::new(); + let mut B = ECP2::new(); + let mut r = FP12::new_int(1); + + A.copy(&P); + B.copy(&R); + + let mut NP = ECP2::new(); + NP.copy(&P); + NP.neg(); + let mut NR = ECP2::new(); + NR.copy(&R); + NR.neg(); + + let nb=lbits(&mut n3,&mut n); + + for i in (1..nb - 1).rev() { + r.sqr(); + let mut lv = linedbl(&mut A, &qx, &qy); + let lv2 = linedbl(&mut B, &sx, &sy); + lv.smul(&lv2); + r.ssmul(&lv); + let bt = n3.bit(i) - n.bit(i); + if bt == 1 { + lv = lineadd(&mut A, &P, &qx, &qy); + let lv2 = lineadd(&mut B, &R, &sx, &sy); + lv.smul(&lv2); + r.ssmul(&lv); + } + if bt == -1 { + lv = lineadd(&mut A, &NP, &qx, &qy); + let lv2 = lineadd(&mut B, &NR, &sx, &sy); + lv.smul(&lv2); + r.ssmul(&lv); + } + } + + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + r.conj(); + } + + /* R-ate fixup */ + if ecp::CURVE_PAIRING_TYPE == ecp::BN { + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + A.neg(); + B.neg(); + } + K.copy(&P); + K.frob(&f); + + let mut lv = lineadd(&mut A, &K, &qx, &qy); + K.frob(&f); + K.neg(); + let mut lv2 = lineadd(&mut A, &K, &qx, &qy); + lv.smul(&lv2); + r.ssmul(&lv); + + K.copy(&R); + K.frob(&f); + + lv = lineadd(&mut B, &K, &sx, &sy); + K.frob(&f); + K.neg(); + lv2 = lineadd(&mut B, &K, &sx, &sy); + lv.smul(&lv2); + r.ssmul(&lv); + + } + + r +} + +/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */ +pub fn fexp(m: &FP12) -> FP12 { + let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + let x = BIG::new_ints(&rom::CURVE_BNX); + let mut r = FP12::new_copy(m); + + /* Easy part of final exp */ + let mut lv = FP12::new_copy(&r); + lv.inverse(); + r.conj(); + + r.mul(&lv); + lv.copy(&r); + r.frob(&f); + r.frob(&f); + r.mul(&lv); + + /* Hard part of final exp */ + if ecp::CURVE_PAIRING_TYPE == ecp::BN { + lv.copy(&r); + lv.frob(&f); + let mut x0 = FP12::new_copy(&lv); + x0.frob(&f); + lv.mul(&r); + x0.mul(&lv); + x0.frob(&f); + let mut x1 = FP12::new_copy(&r); + x1.conj(); + let mut x4 = r.pow(&x); + if ecp::SIGN_OF_X == ecp::POSITIVEX { + x4.conj(); + } + + let mut x3 = FP12::new_copy(&x4); + x3.frob(&f); + + let mut x2 = x4.pow(&x); + if ecp::SIGN_OF_X == ecp::POSITIVEX { + x2.conj(); + } + let mut x5 = FP12::new_copy(&x2); + x5.conj(); + lv = x2.pow(&x); + if ecp::SIGN_OF_X == ecp::POSITIVEX { + lv.conj(); + } + x2.frob(&f); + r.copy(&x2); + r.conj(); + + x4.mul(&r); + x2.frob(&f); + + r.copy(&lv); + r.frob(&f); + lv.mul(&r); + + lv.usqr(); + lv.mul(&x4); + lv.mul(&x5); + r.copy(&x3); + r.mul(&x5); + r.mul(&lv); + lv.mul(&x2); + r.usqr(); + r.mul(&lv); + r.usqr(); + lv.copy(&r); + lv.mul(&x1); + r.mul(&x0); + lv.usqr(); + r.mul(&lv); + r.reduce(); + } else { + +// See https://eprint.iacr.org/2020/875.pdf + let mut y1 = FP12::new_copy(&r); + y1.usqr(); + y1.mul(&r); // y1=r^3 + + let mut y0 = FP12::new_copy(&r.pow(&x)); + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + y0.conj(); + } + let mut t0 = FP12::new_copy(&r); t0.conj(); + r.copy(&y0); + r.mul(&t0); + + y0.copy(&r.pow(&x)); + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + y0.conj(); + } + t0.copy(&r); t0.conj(); + r.copy(&y0); + r.mul(&t0); + +// ^(x+p) + y0.copy(&r.pow(&x)); + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + y0.conj(); + } + t0.copy(&r); + t0.frob(&f); + r.copy(&y0); + r.mul(&t0); + +// ^(x^2+p^2-1) + y0.copy(&r.pow(&x)); + y0.copy(&y0.pow(&x)); + t0.copy(&r); + t0.frob(&f); t0.frob(&f); + y0.mul(&t0); + t0.copy(&r); t0.conj(); + r.copy(&y0); + r.mul(&t0); + + r.mul(&y1); + r.reduce(); + } + r +} + +#[allow(non_snake_case)] +/* GLV method */ +fn glv(ee: &BIG) -> [BIG; 2] { + let mut u: [BIG; 2] = [BIG::new(), BIG::new()]; + let q = BIG::new_ints(&rom::CURVE_ORDER); + if ecp::CURVE_PAIRING_TYPE == ecp::BN { +/* */ + let mut t = BIG::new(); + let mut v: [BIG; 2] = [BIG::new(), BIG::new()]; + + for i in 0..2 { + t.copy(&BIG::new_ints(&rom::CURVE_W[i])); // why not just t=new BIG(ROM.CURVE_W[i]); + let mut d: DBIG = BIG::mul(&t, &ee); + v[i].copy(&d.ctdiv(&q,t.nbits())); + } + u[0].copy(&ee); + for i in 0..2 { + for j in 0..2 { + t = BIG::new_ints(&rom::CURVE_SB[j][i]); + t = BIG::modmul(&mut v[j], &mut t, &q); + u[i].add(&q); + u[i].sub(&t); + u[i].ctmod(&q,1); + } + } +/* */ + } else { + let x = BIG::new_ints(&rom::CURVE_BNX); + let x2 = BIG::smul(&x, &x); + let bd=q.nbits()-x2.nbits(); + u[0].copy(&ee); + u[0].ctmod(&x2,bd); + u[1].copy(&ee); + u[1].ctdiv(&x2,bd); + u[1].rsub(&q); + } + u +} + +#[allow(non_snake_case)] +/* Galbraith & Scott Method */ +pub fn gs(ee: &BIG) -> [BIG; 4] { + let mut u: [BIG; 4] = [BIG::new(), BIG::new(), BIG::new(), BIG::new()]; + let q = BIG::new_ints(&rom::CURVE_ORDER); + if ecp::CURVE_PAIRING_TYPE == ecp::BN { +/* */ + let mut t = BIG::new(); + + let mut v: [BIG; 4] = [BIG::new(), BIG::new(), BIG::new(), BIG::new()]; + for i in 0..4 { + t.copy(&BIG::new_ints(&rom::CURVE_WB[i])); + let mut d: DBIG = BIG::mul(&t, &ee); + v[i].copy(&d.ctdiv(&q,t.nbits())); + } + u[0].copy(&ee); + for i in 0..4 { + for j in 0..4 { + t = BIG::new_ints(&rom::CURVE_BB[j][i]); + t = BIG::modmul(&mut v[j], &mut t, &q); + u[i].add(&q); + u[i].sub(&t); + u[i].ctmod(&q,1); + } + } +/* */ + } else { + let x = BIG::new_ints(&rom::CURVE_BNX); + let bd=q.nbits()-x.nbits(); // fixed + + let mut w = BIG::new_copy(&ee); + for i in 0..3 { + u[i].copy(&w); + u[i].ctmod(&x,bd); + w.ctdiv(&x,bd); + } + u[3].copy(&w); + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + let mut t = BIG::new(); + t.copy(&BIG::modneg(&u[1], &q)); + u[1].copy(&t); + t.copy(&BIG::modneg(&u[3], &q)); + u[3].copy(&t); + } + } + u +} + +#[allow(non_snake_case)] +/* Multiply P by e in group G1 */ +pub fn g1mul(P: &ECP, e: &BIG) -> ECP { + let mut R = ECP::new(); + let q = BIG::new_ints(&rom::CURVE_ORDER); + let mut ee= BIG::new_copy(e); + ee.rmod(&q); + if rom::USE_GLV { + R.copy(P); + let mut Q = ECP::new(); + Q.copy(P); + Q.affine(); + + let mut cru = FP::new_big(&BIG::new_ints(&rom::CRU)); + let mut u = glv(&ee); + Q.mulx(&mut cru); + + let mut np = u[0].nbits(); + let mut t: BIG = BIG::modneg(&u[0], &q); + let mut nn = t.nbits(); + if nn < np { + u[0].copy(&t); + R.neg(); + } + + np = u[1].nbits(); + t = BIG::modneg(&u[1], &q); + nn = t.nbits(); + if nn < np { + u[1].copy(&t); + Q.neg(); + } + u[0].norm(); + u[1].norm(); + R = R.mul2(&u[0], &Q, &u[1]); + } else { + R = P.clmul(&ee,&q); + } + R +} + +#[allow(non_snake_case)] +/* Multiply P by e in group G2 */ +pub fn g2mul(P: &ECP2, e: &BIG) -> ECP2 { + let mut R = ECP2::new(); + let q = BIG::new_ints(&rom::CURVE_ORDER); + let mut ee= BIG::new_copy(e); + ee.rmod(&q); + if rom::USE_GS_G2 { + let mut Q: [ECP2; 4] = [ECP2::new(), ECP2::new(), ECP2::new(), ECP2::new()]; + let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + + let mut u = gs(&ee); + let mut T = ECP2::new(); + + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + f.inverse(None); + f.norm(); + } + + let mut t = BIG::new(); + Q[0].copy(&P); + for i in 1..4 { + T.copy(&Q[i - 1]); + Q[i].copy(&T); + Q[i].frob(&f); + } + for i in 0..4 { + let np = u[i].nbits(); + t.copy(&BIG::modneg(&u[i], &q)); + let nn = t.nbits(); + if nn < np { + u[i].copy(&t); + Q[i].neg(); + } + u[i].norm(); + } + + R.copy(&ECP2::mul4(&Q, &u)); + } else { + R.copy(&P.mul(&ee)); + } + R +} + +/* f=f^e */ +/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */ +pub fn gtpow(d: &FP12, e: &BIG) -> FP12 { + let mut r = FP12::new(); + let q = BIG::new_ints(&rom::CURVE_ORDER); + let mut ee= BIG::new_copy(e); + ee.rmod(&q); + if rom::USE_GS_GT { + let mut g: [FP12; 4] = [FP12::new(), FP12::new(), FP12::new(), FP12::new()]; + let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + + let mut t = BIG::new(); + let mut u = gs(&ee); + let mut w = FP12::new(); + + g[0].copy(&d); + for i in 1..4 { + w.copy(&g[i - 1]); + g[i].copy(&w); + g[i].frob(&f); + } + for i in 0..4 { + let np = u[i].nbits(); + t.copy(&BIG::modneg(&u[i], &q)); + let nn = t.nbits(); + if nn < np { + u[i].copy(&t); + g[i].conj(); + } + u[i].norm(); + } + r.copy(&FP12::pow4(&g, &u)); + } else { + r.copy(&d.pow(&ee)); + } + r +} + +/* test G1 group membership */ +#[allow(non_snake_case)] +pub fn g1member(P: &ECP) -> bool { + //let q = BIG::new_ints(&rom::CURVE_ORDER); + if P.is_infinity() { + return false; + } + if ecp::CURVE_PAIRING_TYPE != ecp::BN { + let x = BIG::new_ints(&rom::CURVE_BNX); + let mut cru = FP::new_big(&BIG::new_ints(&rom::CRU)); + let mut W=ECP::new(); W.copy(P); W.mulx(&mut cru); + let mut T=P.mul(&x); + if P.equals(&T) {return false;} // P is of low order + + T=T.mul(&x); T.neg(); + if !W.equals(&T) { + return false; + } + +// Not needed +// W.add(P); T.mulx(&mut cru); W.add(&T); +// if !W.is_infinity() { +// return false; +// } +/* + let W=P.mul(&q); + if !W.is_infinity() { + return false; + } */ + } + true +} + +/* test G2 group membership */ +#[allow(non_snake_case)] +pub fn g2member(P: &ECP2) -> bool { + if P.is_infinity() { + return false; + } + let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + if ecp::SEXTIC_TWIST == ecp::M_TYPE { + f.inverse(None); + f.norm(); + } + let x = BIG::new_ints(&rom::CURVE_BNX); + + let mut W=ECP2::new(); + let mut T=P.mul(&x); + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + T.neg(); + } + if ecp::CURVE_PAIRING_TYPE == ecp::BN { +//https://eprint.iacr.org/2022/348.pdf + W.copy(&T); + W.frob(&f); + T.add(P); + T.add(&W); + W.frob(&f); + T.add(&W); + W.frob(&f); + W.dbl(); + } else { +//https://eprint.iacr.org/2021/1130 + W.copy(P); W.frob(&f); + } + + if !W.equals(&T) { + return false; + } + true +} + +/* Check that m is in cyclotomic sub-group */ +/* Check that m!=1, conj(m)*m==1, and m.m^{p^4}=m^{p^2} */ +pub fn gtcyclotomic(m: &FP12) -> bool { + if m.isunity() { + return false; + } + let mut r = FP12::new_copy(&m); + r.conj(); + r.mul(&m); + if !r.isunity() { + return false; + } + let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + r.copy(&m); r.frob(&f); r.frob(&f); + let mut w = FP12::new_copy(&r); w.frob(&f); w.frob(&f); + w.mul(&m); + if !w.equals(&r) { + return false; + } + return true; +} + +/* test for full GT group membership */ +pub fn gtmember(m: &FP12) -> bool { + if !gtcyclotomic(m) { + return false; + } + let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB)); + let x = BIG::new_ints(&rom::CURVE_BNX); + + let mut r=FP12::new(); + let mut t=m.pow(&x); + if ecp::SIGN_OF_X == ecp::NEGATIVEX { + t.conj(); + } + if ecp::CURVE_PAIRING_TYPE == ecp::BN { +//https://eprint.iacr.org/2022/348.pdf + r.copy(&t); + r.frob(&f); + t.mul(m); + t.mul(&r); + r.frob(&f); + t.mul(&r); + r.frob(&f); + r.usqr(); + } else { +//https://eprint.iacr.org/2021/1130 + r.copy(m); r.frob(&f); + } + + if !r.equals(&t) { + return false; + } + + true +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/rom.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/rom.rs new file mode 100644 index 000000000000..b53157c96674 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/bn254/rom.rs @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::arch::Chunk; +use crate::bn254::big::NLEN; + +// BN254 Modulus +// Base Bits= 56 +pub const MODULUS: [Chunk; NLEN] = [0x13, 0x13A7, 0x80000000086121, 0x40000001BA344D, 0x25236482]; +pub const ROI: [Chunk; NLEN] = [0x12, 0x13A7, 0x80000000086121, 0x40000001BA344D, 0x25236482]; +pub const R2MODP: [Chunk; NLEN] = [ + 0x2F2A96FF5E7E39, + 0x64E8642B96F13C, + 0x9926F7B00C7146, + 0x8321E7B4DACD24, + 0x1D127A2E, +]; +pub const MCONST: Chunk = 0x435E50D79435E5; +pub const SQRTM3:[Chunk;NLEN]=[0x4,0x60C,0x3CF0F,0x4000000126CD89,0x25236482]; +pub const FRA: [Chunk; NLEN] = [ + 0x7DE6C06F2A6DE9, + 0x74924D3F77C2E1, + 0x50A846953F8509, + 0x212E7C8CB6499B, + 0x1B377619, +]; +pub const FRB: [Chunk; NLEN] = [ + 0x82193F90D5922A, + 0x8B6DB2C08850C5, + 0x2F57B96AC8DC17, + 0x1ED1837503EAB2, + 0x9EBEE69, +]; + +pub const CURVE_COF_I: isize = 1; +pub const CURVE_B_I: isize = 2; +pub const CURVE_B: [Chunk; NLEN] = [0x2, 0x0, 0x0, 0x0, 0x0]; +pub const CURVE_ORDER: [Chunk; NLEN] = [ + 0xD, + 0x800000000010A1, + 0x8000000007FF9F, + 0x40000001BA344D, + 0x25236482, +]; +pub const CURVE_GX: [Chunk; NLEN] = [0x12, 0x13A7, 0x80000000086121, 0x40000001BA344D, 0x25236482]; +pub const CURVE_GY: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0]; +pub const CURVE_HTPC:[Chunk;NLEN]=[0x1,0x0,0x0,0x0,0x0]; +pub const CURVE_BNX: [Chunk; NLEN] = [0x80000000000001, 0x40, 0x0, 0x0, 0x0]; +pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0]; +pub const CRU: [Chunk; NLEN] = [0x80000000000007, 0x6CD, 0x40000000024909, 0x49B362, 0x0]; +pub const CURVE_PXA: [Chunk; NLEN] = [ + 0xEE4224C803FB2B, + 0x8BBB4898BF0D91, + 0x7E8C61EDB6A464, + 0x519EB62FEB8D8C, + 0x61A10BB, +]; +pub const CURVE_PXB: [Chunk; NLEN] = [ + 0x8C34C1E7D54CF3, + 0x746BAE3784B70D, + 0x8C5982AA5B1F4D, + 0xBA737833310AA7, + 0x516AAF9, +]; +pub const CURVE_PYA: [Chunk; NLEN] = [ + 0xF0E07891CD2B9A, + 0xAE6BDBE09BD19, + 0x96698C822329BD, + 0x6BAF93439A90E0, + 0x21897A0, +]; +pub const CURVE_PYB: [Chunk; NLEN] = [ + 0x2D1AEC6B3ACE9B, + 0x6FFD739C9578A, + 0x56F5F38D37B090, + 0x7C8B15268F6D44, + 0xEBB2B0E, +]; +pub const CURVE_W: [[Chunk; NLEN]; 2] = [ + [0x3, 0x80000000000204, 0x6181, 0x0, 0x0], + [0x1, 0x81, 0x0, 0x0, 0x0], +]; +pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [ + [ + [0x4, 0x80000000000285, 0x6181, 0x0, 0x0], + [0x1, 0x81, 0x0, 0x0, 0x0], + ], + [ + [0x1, 0x81, 0x0, 0x0, 0x0], + [0xA, 0xE9D, 0x80000000079E1E, 0x40000001BA344D, 0x25236482], + ], +]; +pub const CURVE_WB: [[Chunk; NLEN]; 4] = [ + [0x80000000000000, 0x80000000000040, 0x2080, 0x0, 0x0], + [0x80000000000005, 0x54A, 0x8000000001C707, 0x312241, 0x0], + [ + 0x80000000000003, + 0x800000000002C5, + 0xC000000000E383, + 0x189120, + 0x0, + ], + [0x80000000000001, 0x800000000000C1, 0x2080, 0x0, 0x0], +]; +pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [ + [ + [ + 0x8000000000000D, + 0x80000000001060, + 0x8000000007FF9F, + 0x40000001BA344D, + 0x25236482, + ], + [ + 0x8000000000000C, + 0x80000000001060, + 0x8000000007FF9F, + 0x40000001BA344D, + 0x25236482, + ], + [ + 0x8000000000000C, + 0x80000000001060, + 0x8000000007FF9F, + 0x40000001BA344D, + 0x25236482, + ], + [0x2, 0x81, 0x0, 0x0, 0x0], + ], + [ + [0x1, 0x81, 0x0, 0x0, 0x0], + [ + 0x8000000000000C, + 0x80000000001060, + 0x8000000007FF9F, + 0x40000001BA344D, + 0x25236482, + ], + [ + 0x8000000000000D, + 0x80000000001060, + 0x8000000007FF9F, + 0x40000001BA344D, + 0x25236482, + ], + [ + 0x8000000000000C, + 0x80000000001060, + 0x8000000007FF9F, + 0x40000001BA344D, + 0x25236482, + ], + ], + [ + [0x2, 0x81, 0x0, 0x0, 0x0], + [0x1, 0x81, 0x0, 0x0, 0x0], + [0x1, 0x81, 0x0, 0x0, 0x0], + [0x1, 0x81, 0x0, 0x0, 0x0], + ], + [ + [0x80000000000002, 0x40, 0x0, 0x0, 0x0], + [0x2, 0x102, 0x0, 0x0, 0x0], + [ + 0xA, + 0x80000000001020, + 0x8000000007FF9F, + 0x40000001BA344D, + 0x25236482, + ], + [0x80000000000002, 0x40, 0x0, 0x0, 0x0], + ], +]; + +pub const USE_GLV: bool = true; +pub const USE_GS_G2: bool = true; +pub const USE_GS_GT: bool = true; +pub const GT_STRONG: bool = false; diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/dilithium.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/dilithium.rs new file mode 100644 index 000000000000..8f5b186b640e --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/dilithium.rs @@ -0,0 +1,1248 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Dilithium API high-level functions. Constant time where it matters. Slow (spends nearly all of its time running SHA3) but small. + +Note that the Matrix A is calculated on-the-fly to keep memory requirement minimal +But this makes all stages much slower +Note that +1. Matrix A can just be generated randomly for Key generation (without using SHA3 which is very slow) +2. A precalculated A can be included in the public key, for use by signature and verification (which blows up public key size) +3. Precalculating A for signature calculation means that the A does not have to re-calculated for each attempt to find a good signature + +Might be simpler to wait for hardware support for SHA3! + + M.Scott 30/09/2021 +*/ + +use crate::sha3; +use crate::sha3::SHA3; + +//q= 8380417 +const LGN: usize = 8; +const DEGREE: usize = 1 << LGN; +const PRIME: i32 = 0x7fe001; +const D: usize = 13; +const TD: usize = 23 - D; + +const ONE: i32 = 0x3FFE00; // R mod Q +const COMBO: i32 = 0xA3FA; // ONE*inv mod Q +const ND: u32 = 0xFC7FDFFF; // 1/(R-Q) mod R +const R2MODP: u64 = 0x2419FF; // R^2 mod Q + +const MAXLG: usize = 19; +const MAXK: usize = 8; // could reduce these if not using highest security +const MAXL: usize = 7; +const YBYTES: usize = ((MAXLG + 1) * DEGREE) / 8; + +pub const SK_SIZE_2: usize = 32 * 3 + DEGREE * (4 * 13 + 4 * 3 + 4 * 3) / 8; +pub const PK_SIZE_2: usize = (4 * DEGREE * TD) / 8 + 32; +pub const SIG_SIZE_2: usize = (DEGREE * 4 * (17 + 1)) / 8 + 80 + 4 + 32; + +pub const SK_SIZE_3: usize = 32 * 3 + DEGREE * (6 * 13 + 5 * 4 + 6 * 4) / 8; +pub const PK_SIZE_3: usize = (6 * DEGREE * TD) / 8 + 32; +pub const SIG_SIZE_3: usize = (DEGREE * 5 * (19 + 1)) / 8 + 55 + 6 + 32; + +pub const SK_SIZE_5: usize = 32 * 3 + DEGREE * (8 * 13 + 7 * 3 + 8 * 3) / 8; +pub const PK_SIZE_5: usize = (8 * DEGREE * TD) / 8 + 32; +pub const SIG_SIZE_5: usize = (DEGREE * 7 * (19 + 1)) / 8 + 75 + 8 + 32; + +// parameters for each security level +// tau,gamma1,gamma2,K,L,eta,lg(2*eta+1),omega +const PARAMS_2: [usize; 8] = [39, 17, 88, 4, 4, 2, 3, 80]; +const PARAMS_3: [usize; 8] = [49, 19, 32, 6, 5, 4, 4, 55]; +const PARAMS_5: [usize; 8] = [60, 19, 32, 8, 7, 2, 3, 75]; + +const ROOTS: [i32; 256] = [ + 0x3ffe00, 0x64f7, 0x581103, 0x77f504, 0x39e44, 0x740119, 0x728129, 0x71e24, 0x1bde2b, 0x23e92b, + 0x7a64ae, 0x5ff480, 0x2f9a75, 0x53db0a, 0x2f7a49, 0x28e527, 0x299658, 0xfa070, 0x6f65a5, + 0x36b788, 0x777d91, 0x6ecaa1, 0x27f968, 0x5fb37c, 0x5f8dd7, 0x44fae8, 0x6a84f8, 0x4ddc99, + 0x1ad035, 0x7f9423, 0x3d3201, 0x445c5, 0x294a67, 0x17620, 0x2ef4cd, 0x35dec5, 0x668504, + 0x49102d, 0x5927d5, 0x3bbeaf, 0x44f586, 0x516e7d, 0x368a96, 0x541e42, 0x360400, 0x7b4a4e, + 0x23d69c, 0x77a55e, 0x65f23e, 0x66cad7, 0x357e1e, 0x458f5a, 0x35843f, 0x5f3618, 0x67745d, + 0x38738c, 0xc63a8, 0x81b9a, 0xe8f76, 0x3b3853, 0x3b8534, 0x58dc31, 0x1f9d54, 0x552f2e, + 0x43e6e6, 0x688c82, 0x47c1d0, 0x51781a, 0x69b65e, 0x3509ee, 0x2135c7, 0x67afbc, 0x6caf76, + 0x1d9772, 0x419073, 0x709cf7, 0x4f3281, 0x4fb2af, 0x4870e1, 0x1efca, 0x3410f2, 0x70de86, + 0x20c638, 0x296e9f, 0x5297a4, 0x47844c, 0x799a6e, 0x5a140a, 0x75a283, 0x6d2114, 0x7f863c, + 0x6be9f8, 0x7a0bde, 0x1495d4, 0x1c4563, 0x6a0c63, 0x4cdbea, 0x40af0, 0x7c417, 0x2f4588, 0xad00, + 0x6f16bf, 0xdcd44, 0x3c675a, 0x470bcb, 0x7fbe7f, 0x193948, 0x4e49c1, 0x24756c, 0x7ca7e0, + 0xb98a1, 0x6bc809, 0x2e46c, 0x49a809, 0x3036c2, 0x639ff7, 0x5b1c94, 0x7d2ae1, 0x141305, + 0x147792, 0x139e25, 0x67b0e1, 0x737945, 0x69e803, 0x51cea3, 0x44a79d, 0x488058, 0x3a97d9, + 0x1fea93, 0x33ff5a, 0x2358d4, 0x3a41f8, 0x4cdf73, 0x223dfb, 0x5a8ba0, 0x498423, 0x412f5, + 0x252587, 0x6d04f1, 0x359b5d, 0x4a28a1, 0x4682fd, 0x6d9b57, 0x4f25df, 0xdbe5e, 0x1c5e1a, + 0xde0e6, 0xc7f5a, 0x78f83, 0x67428b, 0x7f3705, 0x77e6fd, 0x75e022, 0x503af7, 0x1f0084, + 0x30ef86, 0x49997e, 0x77dcd7, 0x742593, 0x4901c3, 0x53919, 0x4610c, 0x5aad42, 0x3eb01b, + 0x3472e7, 0x4ce03c, 0x1a7cc7, 0x31924, 0x2b5ee5, 0x291199, 0x585a3b, 0x134d71, 0x3de11c, + 0x130984, 0x25f051, 0x185a46, 0x466519, 0x1314be, 0x283891, 0x49bb91, 0x52308a, 0x1c853f, + 0x1d0b4b, 0x6fd6a7, 0x6b88bf, 0x12e11b, 0x4d3e3f, 0x6a0d30, 0x78fde5, 0x1406c7, 0x327283, + 0x61ed6f, 0x6c5954, 0x1d4099, 0x590579, 0x6ae5ae, 0x16e405, 0xbdbe7, 0x221de8, 0x33f8cf, + 0x779935, 0x54aa0d, 0x665ff9, 0x63b158, 0x58711c, 0x470c13, 0x910d8, 0x463e20, 0x612659, + 0x251d8b, 0x2573b7, 0x7d5c90, 0x1ddd98, 0x336898, 0x2d4bb, 0x6d73a8, 0x4f4cbf, 0x27c1c, + 0x18aa08, 0x2dfd71, 0xc5ca5, 0x19379a, 0x478168, 0x646c3e, 0x51813d, 0x35c539, 0x3b0115, + 0x41dc0, 0x21c4f7, 0x70fbf5, 0x1a35e7, 0x7340e, 0x795d46, 0x1a4cd0, 0x645caf, 0x1d2668, + 0x666e99, 0x6f0634, 0x7be5db, 0x455fdc, 0x530765, 0x5dc1b0, 0x7973de, 0x5cfd0a, 0x2cc93, + 0x70f806, 0x189c2a, 0x49c5aa, 0x776a51, 0x3bcf2c, 0x7f234f, 0x6b16e0, 0x3c15ca, 0x155e68, + 0x72f6b7, 0x1e29ce, +]; +const IROOTS: [i32; 256] = [ + 0x3ffe00, 0x7f7b0a, 0x7eafd, 0x27cefe, 0x78c1dd, 0xd5ed8, 0xbdee8, 0x7c41bd, 0x56fada, + 0x5065b8, 0x2c04f7, 0x50458c, 0x1feb81, 0x57b53, 0x5bf6d6, 0x6401d6, 0x7b9a3c, 0x42ae00, + 0x4bde, 0x650fcc, 0x320368, 0x155b09, 0x3ae519, 0x20522a, 0x202c85, 0x57e699, 0x111560, + 0x86270, 0x492879, 0x107a5c, 0x703f91, 0x5649a9, 0x2ab0d3, 0x6042ad, 0x2703d0, 0x445acd, + 0x44a7ae, 0x71508b, 0x77c467, 0x737c59, 0x476c75, 0x186ba4, 0x20a9e9, 0x4a5bc2, 0x3a50a7, + 0x4a61e3, 0x19152a, 0x19edc3, 0x83aa3, 0x5c0965, 0x495b3, 0x49dc01, 0x2bc1bf, 0x49556b, + 0x2e7184, 0x3aea7b, 0x442152, 0x26b82c, 0x36cfd4, 0x195afd, 0x4a013c, 0x50eb34, 0x7e69e1, + 0x56959a, 0x454828, 0x375fa9, 0x3b3864, 0x2e115e, 0x15f7fe, 0xc66bc, 0x182f20, 0x6c41dc, + 0x6b686f, 0x6bccfc, 0x2b520, 0x24c36d, 0x1c400a, 0x4fa93f, 0x3637f8, 0x7cfb95, 0x1417f8, + 0x744760, 0x33821, 0x5b6a95, 0x319640, 0x66a6b9, 0x2182, 0x38d436, 0x4378a7, 0x7212bd, + 0x10c942, 0x7f3301, 0x509a79, 0x781bea, 0x7bd511, 0x330417, 0x15d39e, 0x639a9e, 0x6b4a2d, + 0x5d423, 0x13f609, 0x59c5, 0x12beed, 0xa3d7e, 0x25cbf7, 0x64593, 0x385bb5, 0x2d485d, 0x567162, + 0x5f19c9, 0xf017b, 0x4bcf0f, 0x7df037, 0x376f20, 0x302d52, 0x30ad80, 0xf430a, 0x3e4f8e, + 0x62488f, 0x13308b, 0x183045, 0x5eaa3a, 0x4ad613, 0x1629a3, 0x2e67e7, 0x381e31, 0x17537f, + 0x3bf91b, 0x61b633, 0xce94a, 0x6a8199, 0x43ca37, 0x14c921, 0xbcb2, 0x4410d5, 0x875b0, 0x361a57, + 0x6743d7, 0xee7fb, 0x7d136e, 0x22e2f7, 0x66c23, 0x221e51, 0x2cd89c, 0x3a8025, 0x3fa26, + 0x10d9cd, 0x197168, 0x62b999, 0x1b8352, 0x659331, 0x682bb, 0x78abf3, 0x65aa1a, 0xee40c, + 0x5e1b0a, 0x7bc241, 0x44deec, 0x4a1ac8, 0x2e5ec4, 0x1b73c3, 0x385e99, 0x66a867, 0x73835c, + 0x51e290, 0x6735f9, 0x7d63e5, 0x309342, 0x126c59, 0x7d0b46, 0x4c7769, 0x620269, 0x28371, + 0x5a6c4a, 0x5ac276, 0x1eb9a8, 0x39a1e1, 0x76cf29, 0x38d3ee, 0x276ee5, 0x1c2ea9, 0x198008, + 0x2b35f4, 0x846cc, 0x4be732, 0x5dc219, 0x74041a, 0x68fbfc, 0x14fa53, 0x26da88, 0x629f68, + 0x1386ad, 0x1df292, 0x4d6d7e, 0x6bd93a, 0x6e21c, 0x15d2d1, 0x32a1c2, 0x6cfee6, 0x145742, + 0x10095a, 0x62d4b6, 0x635ac2, 0x2daf77, 0x362470, 0x57a770, 0x6ccb43, 0x397ae8, 0x6785bb, + 0x59efb0, 0x6cd67d, 0x41fee5, 0x6c9290, 0x2785c6, 0x56ce68, 0x54811c, 0x7cc6dd, 0x65633a, + 0x32ffc5, 0x4b6d1a, 0x412fe6, 0x2532bf, 0x7b7ef5, 0x7aa6e8, 0x36de3e, 0xbba6e, 0x8032a, + 0x364683, 0x4ef07b, 0x60df7d, 0x2fa50a, 0x9ffdf, 0x7f904, 0xa8fc, 0x189d76, 0x78507e, 0x7360a7, + 0x71ff1b, 0x6381e7, 0x7221a3, 0x30ba22, 0x1244aa, 0x395d04, 0x35b760, 0x4a44a4, 0x12db10, + 0x5aba7a, 0x7bcd0c, 0x365bde, 0x255461, 0x5da206, 0x33008e, 0x459e09, 0x5c872d, 0x4be0a7, + 0x5ff56e, +]; + +/* Montgomery stuff */ + +fn redc(t: u64) -> i32 { + let m = (t as u32).wrapping_mul(ND); + (((m as u64) * (PRIME as u64) + t) >> 32) as i32 +} + +fn nres(x: i32) -> i32 { + redc((x as u64) * R2MODP) +} + +fn modmul(a: i32, b: i32) -> i32 { + redc((a as u64) * (b as u64)) +} + +fn poly_pos(p: &mut [i32]) { + for j in 0..DEGREE { + p[j] += (p[j] >> 31) & PRIME; + } +} +// NTT code + +// Important! +// nres(x); ntt(x) +// nres(y); ntt(y) +// z=x*y +// intt(z); +// redc(z); + +// is equivalent to (note that nres() and redc() cancel out) + +// ntt(x); +// nres(y); ntt(y); +// z=x*y +// intt(z) + +// is equivalent to + +// ntt(x) +// ntt(y) +// z=x*y +// intt(z) +// nres(z) + +// In all cases z ends up in normal (non-Montgomery) form! +// So the conversion to Montgomery form can be "pushed" through the calculation. + +// Here intt(z) <- intt(z);nres(z); +// Combining is more efficient +// note that ntt() and intt() are not mutually inverse + +/* Cooley-Tukey NTT */ +/* Excess of 2 allowed on input - coefficients must be < 2*PRIME */ +fn ntt(x: &mut [i32]) { + let mut t = DEGREE / 2; + let q = PRIME; + + /* Make positive */ + poly_pos(x); + + let mut m = 1; + while m < DEGREE { + let mut k = 0; + for i in 0..m { + let s = ROOTS[m + i]; + for j in k..k + t { + let u = x[j]; + let v = modmul(x[j + t], s); + x[j] = u + v; + x[j + t] = u + 2 * q - v; + } + k += 2 * t; + } + t /= 2; + m *= 2; + } +} + +/* Gentleman-Sande INTT */ +/* Excess of 2 allowed on input - coefficients must be < 2*PRIME */ +/* Output fully reduced */ + +const NTTL: usize = 2; // maybe could be 1? + +fn intt(x: &mut [i32]) { + let mut t = 1; + let q = PRIME; + let mut m = DEGREE / 2; + let mut n = LGN; + while m >= 1 { + let lim = NTTL >> n; + n -= 1; + let mut k = 0; + for i in 0..m { + let s = IROOTS[m + i]; + for j in k..k + t { + let u: i32; + let v: i32; + if m < NTTL && j < k + lim { + u = modmul(x[j], ONE); + v = modmul(x[j + t], ONE); + } else { + u = x[j]; + v = x[j + t]; + } + x[j] = u + v; + let w = u + ((DEGREE / NTTL) as i32) * q - v; + x[j + t] = modmul(w, s); + } + k += 2 * t; + } + t *= 2; + m /= 2; + } + + // fully reduce, nres combined with 1/DEGREE + for j in 0..DEGREE { + x[j] = modmul(x[j], COMBO); + x[j] -= q; + x[j] += (x[j] >> 31) & q; + } +} + +fn nres_it(p: &mut [i32]) { + for i in 0..DEGREE { + p[i] = nres(p[i]); + } +} + +fn redc_it(p: &mut [i32]) { + for i in 0..DEGREE { + p[i] = redc(p[i] as u64); + } +} + +fn poly_copy(p1: &mut [i32], p3: &[i32]) { + for i in 0..DEGREE { + p1[i] = p3[i]; + } +} + +fn poly_scopy(p1: &mut [i32], p3: &[i8]) { + for i in 0..DEGREE { + p1[i] = p3[i] as i32; + } +} + +fn poly_mcopy(p1: &mut [i32], p3: &[i16]) { + for i in 0..DEGREE { + p1[i] = p3[i] as i32; + } +} + +fn poly_zero(p1: &mut [i32]) { + for i in 0..DEGREE { + p1[i] = 0; + } +} + +fn poly_negate(p1: &mut [i32], p3: &[i32]) { + for i in 0..DEGREE { + p1[i] = PRIME - p3[i]; + } +} + +fn poly_mul(p1: &mut [i32], p3: &[i32]) { + for i in 0..DEGREE { + p1[i] = modmul(p1[i], p3[i]); + } +} + +fn poly_add(p1: &mut [i32], p3: &[i32]) { + for i in 0..DEGREE { + p1[i] += p3[i]; + } +} + +fn poly_sub(p1: &mut [i32], p3: &[i32]) { + for i in 0..DEGREE { + p1[i] += PRIME - p3[i]; + } +} + +/* reduces inputs < 2q */ +fn poly_soft_reduce(poly: &mut [i32]) { + for i in 0..DEGREE { + let e = poly[i] - PRIME; + poly[i] = e + ((e >> 31) & PRIME); + } +} + +/* fully reduces modulo q */ +fn poly_hard_reduce(poly: &mut [i32]) { + for i in 0..DEGREE { + let mut e = modmul(poly[i], ONE); + e -= PRIME; + poly[i] = e + ((e >> 31) & PRIME); + } +} + +// Generate a[i][j] from rho +fn expandaij(rho: &[u8], aij: &mut [i32], i: usize, j: usize) { + let mut buff: [u8; 4 * DEGREE] = [0; 4 * DEGREE]; + let mut sh = SHA3::new(sha3::SHAKE128); + for m in 0..32 { + sh.process(rho[m]) + } + sh.process(j as u8); + sh.process(i as u8); + sh.shake(&mut buff, 4 * DEGREE); + let mut m = 0; + let mut n = 0; + while m < DEGREE { + let b0 = buff[n] as u32; + let b1 = buff[n + 1] as u32; + let b2 = buff[n + 2] as u32; + let cf = (((b2 & 0x7f) << 16) + (b1 << 8) + b0) as i32; + n += 3; + if cf >= PRIME { + continue; + } + aij[m] = cf; + m += 1; + } +} + +// array t has ab active bits per word +// extract bytes from array of words +// if mx!=0 then -mx<=t[i]<=+mx +fn nextbyte32(ab: usize, mx: usize, t: &[i32], ptr: &mut usize, bts: &mut usize) -> u8 { + let mut left = ab - *bts; + let mut w = t[*ptr]; + let mxm = mx as i32; + if mxm != 0 { + w = mxm - w; + } + let mut r = w >> *bts; + let mut i = 0; + while left < 8 { + i += 1; + w = t[(*ptr) + i]; + if mxm != 0 { + w = mxm - w; + } + r |= w << left; + left += ab; + } + *bts += 8; + while *bts >= ab { + *bts -= ab; + *ptr += 1; + } + r as u8 +} + +fn nextbyte16(ab: usize, mx: usize, t: &[i16], ptr: &mut usize, bts: &mut usize) -> u8 { + let mut left = ab - *bts; + let mut w = t[*ptr]; + let mxm = mx as i16; + if mxm != 0 { + w = mxm - w; + } + let mut r = w >> *bts; + let mut i = 0; + while left < 8 { + i += 1; + w = t[(*ptr) + i]; + if mxm != 0 { + w = mxm - w; + } + r |= w << left; + left += ab; + } + *bts += 8; + while *bts >= ab { + *bts -= ab; + *ptr += 1; + } + r as u8 +} + +fn nextbyte8(ab: usize, mx: usize, t: &[i8], ptr: &mut usize, bts: &mut usize) -> u8 { + let mut left = ab - *bts; + let mut w = t[*ptr]; + let mxm = mx as i8; + if mxm != 0 { + w = mxm - w; + } + let mut r = w >> *bts; + let mut i = 0; + while left < 8 { + i += 1; + w = t[(*ptr) + i]; + if mxm != 0 { + w = mxm - w; + } + r |= w << left; + left += ab; + } + *bts += 8; + while *bts >= ab { + *bts -= ab; + *ptr += 1; + } + r as u8 +} + +fn nextword(ab: usize, mx: usize, t: &[u8], ptr: &mut usize, bts: &mut usize) -> i32 { + let mut r = (t[*ptr] >> *bts) as i32; + let mxm = mx as i32; + let mask = (1 << ab) - 1; + let mut w: i32; + let mut i = 0; + let mut gotbits = 8 - *bts; + while gotbits < ab { + i += 1; + w = t[(*ptr) + i] as i32; + r |= w << gotbits; + gotbits += 8; + } + *bts += ab; + while *bts >= 8 { + *bts -= 8; + *ptr += 1; + } + w = r & mask; + if mxm != 0 { + w = mxm - w; + } + w +} + +fn pack_pk(params: &[usize], pk: &mut [u8], rho: &[u8], t1: &[i16]) { + let ck = params[3]; + for i in 0..32 { + pk[i] = rho[i]; + } + let mut ptr = 0 as usize; + let mut bts = 0 as usize; + let mut n = 32; + for _ in 0..(ck * DEGREE * TD) / 8 { + pk[n] = nextbyte16(TD, 0, t1, &mut ptr, &mut bts); + n += 1; + } +} + +fn unpack_pk(params: &[usize], rho: &mut [u8], t1: &mut [i16], pk: &[u8]) { + let ck = params[3]; + for i in 0..32 { + rho[i] = pk[i]; + } + let mut ptr = 0 as usize; + let mut bts = 0 as usize; + for i in 0..ck * DEGREE { + t1[i] = nextword(TD, 0, &pk[32..], &mut ptr, &mut bts) as i16; + } +} + +fn pack_sk( + params: &[usize], + sk: &mut [u8], + rho: &[u8], + bk: &[u8], + tr: &[u8], + s1: &[i8], + s2: &[i8], + t0: &[i16], +) { + let ck = params[3]; + let el = params[4]; + let eta = params[5]; + let lg2eta1 = params[6]; + + for i in 0..32 { + sk[i] = rho[i]; + } + let mut n = 32; + for i in 0..32 { + sk[n] = bk[i]; + n += 1; + } + for i in 0..32 { + sk[n] = tr[i]; + n += 1; + } + let mut ptr = 0 as usize; + let mut bts = 0 as usize; + + for _ in 0..(el * DEGREE * lg2eta1) / 8 { + sk[n] = nextbyte8(lg2eta1, eta, s1, &mut ptr, &mut bts); + n += 1; + } + ptr = 0; + bts = 0; + for _ in 0..(ck * DEGREE * lg2eta1) / 8 { + sk[n] = nextbyte8(lg2eta1, eta, s2, &mut ptr, &mut bts); + n += 1; + } + ptr = 0; + bts = 0; + for _ in 0..(ck * DEGREE * D) / 8 { + sk[n] = nextbyte16(D, 1 << (D - 1), t0, &mut ptr, &mut bts); + n += 1; + } +} + +fn unpack_sk( + params: &[usize], + rho: &mut [u8], + bk: &mut [u8], + tr: &mut [u8], + s1: &mut [i8], + s2: &mut [i8], + t0: &mut [i16], + sk: &[u8], +) { + let ck = params[3]; + let el = params[4]; + let eta = params[5]; + let lg2eta1 = params[6]; + + for i in 0..32 { + rho[i] = sk[i]; + } + let mut n = 32; + for i in 0..32 { + bk[i] = sk[n]; + n += 1; + } + for i in 0..32 { + tr[i] = sk[n]; + n += 1; + } + let mut ptr = 0 as usize; + let mut bts = 0 as usize; + + for i in 0..el * DEGREE { + s1[i] = nextword(lg2eta1, eta, &sk[n..], &mut ptr, &mut bts) as i8; + } + n += ptr; + ptr = 0; + bts = 0; + for i in 0..ck * DEGREE { + s2[i] = nextword(lg2eta1, eta, &sk[n..], &mut ptr, &mut bts) as i8; + } + n += ptr; + ptr = 0; + bts = 0; + for i in 0..ck * DEGREE { + t0[i] = nextword(D, 1 << (D - 1), &sk[n..], &mut ptr, &mut bts) as i16; + } +} + +// pack signature - changes z +fn pack_sig(params: &[usize], sig: &mut [u8], z: &mut [i32], ct: &[u8], h: &[u8]) { + let lg = params[1]; + let gamma1 = 1 << lg; + let ck = params[3]; + let el = params[4]; + let omega = params[7]; + + for i in 0..32 { + sig[i] = ct[i]; + } + let mut n = 32; + let mut ptr = 0 as usize; + let mut bts = 0 as usize; + + for i in 0..el { + let row = DEGREE * i; + for m in 0..DEGREE { + let mut t = z[row + m]; + if t > PRIME / 2 { + t -= PRIME; + } + t = gamma1 - t; + z[row + m] = t; + } + } + for _ in 0..(el * DEGREE * (lg + 1)) / 8 { + sig[n] = nextbyte32(lg + 1, 0, z, &mut ptr, &mut bts); + n += 1; + } + for i in 0..omega + ck { + sig[n] = h[i]; + n += 1; + } +} + +fn unpack_sig(params: &[usize], z: &mut [i32], ct: &mut [u8], h: &mut [u8], sig: &[u8]) { + let lg = params[1]; + let gamma1 = 1 << lg; + let ck = params[3]; + let el = params[4]; + let omega = params[7]; + + for i in 0..32 { + ct[i] = sig[i]; + } + + let mut ptr = 0 as usize; + let mut bts = 0 as usize; + + for i in 0..el * DEGREE { + let mut t = nextword(lg + 1, 0, &sig[32..], &mut ptr, &mut bts); + t = gamma1 - t; + if t < 0 { + t += PRIME; + } + z[i] = t; + } + let mut m = 32 + (el * DEGREE * (lg + 1)) / 8; + for i in 0..omega + ck { + h[i] = sig[m]; + m += 1; + } +} + +fn sample_sn(params: &[usize], rhod: &[u8], s: &mut [i8], n: usize) { + let mut buff: [u8; 272] = [0; 272]; + let mut sh = SHA3::new(sha3::SHAKE256); + for m in 0..64 { + sh.process(rhod[m]); + } + sh.process((n & 0xff) as u8); + sh.process(((n >> 8) & 0xff) as u8); + sh.shake(&mut buff, 272); + + let eta = params[5]; + let lg2eta1 = params[6]; + + let mut ptr = 0 as usize; + let mut bts = 0 as usize; + for m in 0..DEGREE { + loop { + s[m] = nextword(lg2eta1, 0, &buff, &mut ptr, &mut bts) as i8; + if s[m] <= 2 * (eta as i8) { + break; + } + } + s[m] = (eta as i8) - s[m]; + } +} + +fn sample_y(params: &[usize], k: usize, rhod: &[u8], y: &mut [i32]) { + let lg = params[1]; + let gamma1 = 1 << lg; + let el = params[4]; + let mut buff: [u8; YBYTES] = [0; YBYTES]; + for i in 0..el { + let row = DEGREE * i; + let mut sh = SHA3::new(sha3::SHAKE256); + for j in 0..64 { + sh.process(rhod[j]); + } + let ki = k + i; + sh.process((ki & 0xff) as u8); + sh.process((ki >> 8) as u8); + sh.shake(&mut buff, ((lg + 1) * DEGREE) / 8); + + let mut ptr = 0 as usize; + let mut bts = 0 as usize; + + for m in 0..DEGREE { + let mut w = nextword(lg + 1, 0, &buff, &mut ptr, &mut bts); + w = gamma1 - w; + let t = w >> 31; + y[row + m] = w + (PRIME & t); + } + } +} + +fn crh1(params: &[usize], h: &mut [u8], rho: &[u8], t1: &[i16]) { + let mut sh = SHA3::new(sha3::SHAKE256); + for j in 0..32 { + sh.process(rho[j]); + } + let ck = params[3]; + let mut ptr = 0 as usize; + let mut bts = 0 as usize; + + for _ in 0..(ck * DEGREE * TD) / 8 { + sh.process(nextbyte16(TD, 0, t1, &mut ptr, &mut bts)); + } + sh.shake(h, 32); +} + +fn crh2(h: &mut [u8], tr: &[u8], mess: &[u8]) { + let mut sh = SHA3::new(sha3::SHAKE256); + for j in 0..32 { + sh.process(tr[j]); + } + for j in 0..mess.len() { + sh.process(mess[j]); + } + sh.shake(h, 64); +} + +fn crh3(h: &mut [u8], bk: &[u8], mu: &[u8]) { + let mut sh = SHA3::new(sha3::SHAKE256); + for j in 0..32 { + sh.process(bk[j]); + } + for j in 0..64 { + sh.process(mu[j]); + } + sh.shake(h, 64); +} + +fn h4(params: &[usize], ct: &mut [u8], mu: &[u8], w1: &[i8]) { + let ck = params[3]; + let dv = params[2]; + let mut w1b = 4; + if dv == 88 { + w1b = 6; + } + let mut sh = SHA3::new(sha3::SHAKE256); + for j in 0..64 { + sh.process(mu[j]); + } + + let mut ptr = 0 as usize; + let mut bts = 0 as usize; + + for _ in 0..(ck * DEGREE * w1b) / 8 { + sh.process(nextbyte8(w1b, 0, w1, &mut ptr, &mut bts)); + } + sh.shake(ct, 32); +} + +fn sampleinball(params: &[usize], ct: &[u8], c: &mut [i32]) { + let tau = params[0]; + let mut buff: [u8; 136] = [0; 136]; + let mut signs: [u8; 8] = [0; 8]; + let mut sh = SHA3::new(sha3::SHAKE256); + for j in 0..32 { + sh.process(ct[j]); + } + sh.shake(&mut buff, 136); + for i in 0..8 { + signs[i] = buff[i]; + } + let mut k = 8; + let mut b = 0; + poly_zero(c); + let mut j: usize; + let mut n = 1; + let mut sn = signs[0]; + for i in DEGREE - tau..DEGREE { + loop { + j = buff[k] as usize; + k += 1; + if j <= i { + break; + } + } + c[i] = c[j]; + c[j] = 1 - 2 * ((sn as i32) & 1); + sn >>= 1; + b += 1; + if b == 8 { + sn = signs[n]; + n += 1; + b = 0; + } + } +} + +fn p2r(r0: &mut i32) -> i16 { + let d = (1 << D) as i32; + let r1 = (*r0 + d / 2 - 1) >> D; + *r0 -= r1 << D; + r1 as i16 +} + +fn power2round(t: &[i32], t0: &mut [i16], t1: &mut [i16]) { + for m in 0..DEGREE { + let mut w = t[m]; + t1[m] = p2r(&mut w); + t0[m] = w as i16; + } +} + +fn decompose_lo(params: &[usize], a: i32) -> i32 { + let dv = params[2]; + let mut a1 = (a + 127) >> 7; + let gamma2: i32; + if dv == 32 { + a1 = (a1 * 1025 + (1 << 21)) >> 22; + a1 &= 15; + gamma2 = (PRIME - 1) / 32; + } else { + // 88 + a1 = (a1 * 11275 + (1 << 23)) >> 24; + a1 ^= ((43 - a1) >> 31) & a1; + gamma2 = (PRIME - 1) / 88; + } + + let mut a0 = a - a1 * 2 * gamma2; + a0 -= (((PRIME - 1) / 2 - a0) >> 31) & PRIME; + a0 += (a0 >> 31) & PRIME; + a0 +} + +fn decompose_hi(params: &[usize], a: i32) -> i8 { + let dv = params[2]; + + let mut a1 = (a + 127) >> 7; + if dv == 32 { + a1 = (a1 * 1025 + (1 << 21)) >> 22; + a1 &= 15; + } else { + // 88 + a1 = (a1 * 11275 + (1 << 23)) >> 24; + a1 ^= ((43 - a1) >> 31) & a1; + } + a1 as i8 +} + +fn lobits(params: &[usize], r0: &mut [i32], r: &[i32]) { + for m in 0..DEGREE { + r0[m] = decompose_lo(params, r[m]); + } +} + +fn hibits(params: &[usize], r1: &mut [i8], r: &[i32]) { + for m in 0..DEGREE { + r1[m] = decompose_hi(params, r[m]); + } +} + +fn makepartialhint(params: &[usize], h: &mut [u8], hptr: usize, z: &[i32], r: &[i32]) -> usize { + let mut ptr = hptr; + let omega = params[7]; + for m in 0..DEGREE { + let a0 = decompose_hi(params, r[m]); + let mut rz = r[m] + z[m]; + rz -= PRIME; + rz = rz + ((rz >> 31) & PRIME); + let a1 = decompose_hi(params, rz); + if a0 != a1 { + if ptr >= omega { + return omega + 1; + } + h[ptr] = (m & 0xff) as u8; + ptr += 1; + } + } + ptr +} + +fn usepartialhint( + params: &[usize], + r: &mut [i8], + h: &[u8], + hptr: usize, + i: usize, + w: &[i32], +) -> usize { + let mut ptr = hptr; + let dv = params[2] as i32; + let omega = params[7]; + let md = (dv / 2) as i8; + + for m in 0..DEGREE { + let mut a1 = decompose_hi(params, w[m]); + if m == h[ptr] as usize && ptr < h[omega + i] as usize { + ptr += 1; + let a0 = decompose_lo(params, w[m]); + if a0 <= PRIME / 2 { + a1 += 1; + if a1 >= md { + a1 -= md; + } + } else { + a1 -= 1; + if a1 < 0 { + a1 += md; + } + } + } + r[m] = a1; + } + ptr +} + +fn infinity_norm(w: &[i32]) -> i32 { + let mut n = 0 as i32; + for m in 0..DEGREE { + let mut az = w[m]; + if az > PRIME / 2 { + az = PRIME - az; + } + if az > n { + n = az; + } + } + n +} + +fn keypair(params: &[usize], tau: &[u8], sk: &mut [u8], pk: &mut [u8]) { + let mut sh = SHA3::new(sha3::SHAKE256); + let mut buff: [u8; 128] = [0; 128]; + let mut rho: [u8; 32] = [0; 32]; + let mut rhod: [u8; 64] = [0; 64]; + let mut bk: [u8; 32] = [0; 32]; + let mut tr: [u8; 32] = [0; 32]; + let mut aij: [i32; DEGREE] = [0; DEGREE]; + let mut s1: [i8; MAXL * DEGREE] = [0; MAXL * DEGREE]; + let mut s2: [i8; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let mut t0: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let mut t1: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let mut w: [i32; DEGREE] = [0; DEGREE]; + let mut r: [i32; DEGREE] = [0; DEGREE]; + + let ck = params[3]; + let el = params[4]; + + for i in 0..32 { + sh.process(tau[i]); + } + sh.shake(&mut buff, 128); + for i in 0..32 { + rho[i] = buff[i]; + bk[i] = buff[i + 96]; + } + for i in 0..64 { + rhod[i] = buff[i + 32]; + } + + for i in 0..el { + let row = DEGREE * i; + sample_sn(params, &rhod, &mut s1[row..], i); + } + + for i in 0..ck { + let row = DEGREE * i; + sample_sn(params, &rhod, &mut s2[row..], el + i); + poly_zero(&mut r); + for j in 0..el { + poly_scopy(&mut w, &s1[j * DEGREE..]); + ntt(&mut w); + expandaij(&rho, &mut aij, i, j); + poly_mul(&mut w, &aij); + poly_add(&mut r, &w); + } + poly_hard_reduce(&mut r); + intt(&mut r); + poly_scopy(&mut w, &s2[row..]); + poly_pos(&mut w); + poly_add(&mut r, &w); + poly_soft_reduce(&mut r); + power2round(&r, &mut t0[row..], &mut t1[row..]); + } + crh1(params, &mut tr, &rho, &t1); + pack_pk(params, pk, &rho, &t1); + pack_sk(params, sk, &rho, &bk, &tr, &s1, &s2, &t0); +} + +fn signature(params: &[usize], sk: &[u8], m: &[u8], sig: &mut [u8]) -> usize { + let mut rho: [u8; 32] = [0; 32]; + let mut bk: [u8; 32] = [0; 32]; + let mut ct: [u8; 32] = [0; 32]; + let mut tr: [u8; 32] = [0; 32]; + let mut mu: [u8; 64] = [0; 64]; + let mut rhod: [u8; 64] = [0; 64]; + let mut hint: [u8; 100] = [0; 100]; + + //let mut aij: [i32; DEGREE] = [0; DEGREE]; + let mut s1: [i8; MAXL * DEGREE] = [0; MAXL * DEGREE]; + let mut s2: [i8; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let mut t0: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + + let mut y: [i32; MAXL * DEGREE] = [0; MAXL * DEGREE]; + let mut ay: [i32; MAXK * DEGREE] = [0; MAXK * DEGREE]; + + let mut w1: [i8; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let mut c: [i32; DEGREE] = [0; DEGREE]; + let mut w: [i32; DEGREE] = [0; DEGREE]; + let mut r: [i32; DEGREE] = [0; DEGREE]; + + let tau = params[0]; + let lg = params[1]; + let gamma1 = (1 << lg) as i32; + let dv = params[2] as i32; + let gamma2 = (PRIME - 1) / dv; + let ck = params[3]; + let el = params[4]; + let eta = params[5]; + let beta = (tau * eta) as i32; + let omega = params[7]; + + unpack_sk( + params, &mut rho, &mut bk, &mut tr, &mut s1, &mut s2, &mut t0, &sk, + ); + + // signature + crh2(&mut mu, &tr, m); + crh3(&mut rhod, &bk, &mu); + let mut k = 0; + + loop { + let fk = k * el; + k += 1; + sample_y(params, fk, &rhod, &mut y); + // NTT y + for i in 0..el { + let row = DEGREE * i; + ntt(&mut y[row..]); + } + // Calculate ay + for i in 0..ck { + let row = DEGREE * i; + poly_zero(&mut r); + for j in 0..el { + poly_copy(&mut w, &y[j * DEGREE..]); + expandaij(&rho, &mut c, i, j); + poly_mul(&mut w, &c); + poly_add(&mut r, &w); + } + poly_hard_reduce(&mut r); + intt(&mut r); + poly_copy(&mut ay[row..], &r); + // Calculate w1 + hibits(params, &mut w1[row..], &ay[row..]); + } + // Calculate c + h4(params, &mut ct, &mu, &w1); + sampleinball(params, &ct, &mut c); + let mut badone = false; + // Calculate z=y+c.s1 + ntt(&mut c); + for i in 0..el { + let row = DEGREE * i; + poly_scopy(&mut w, &s1[row..]); + ntt(&mut w); + poly_mul(&mut w, &c); + + nres_it(&mut w); + poly_add(&mut y[row..], &w); // re-use y for z + redc_it(&mut y[row..]); // unNTT y + intt(&mut y[row..]); + + poly_soft_reduce(&mut y[row..]); + if infinity_norm(&y[row..]) >= gamma1 - beta { + badone = true; + break; + } + } + if badone { + continue; + } + // Calculate ay=w-c.s2 and r0=lobits(w-c.s2) + let mut nh = 0; + for i in 0..omega + ck { + hint[i] = 0; + } + for i in 0..ck { + let row = DEGREE * i; + poly_scopy(&mut w, &s2[row..]); + ntt(&mut w); + poly_mul(&mut w, &c); + intt(&mut w); + poly_sub(&mut ay[row..], &w); + poly_soft_reduce(&mut ay[row..]); + lobits(params, &mut w, &ay[row..]); + if infinity_norm(&w) >= gamma2 - beta { + badone = true; + break; + } + poly_mcopy(&mut w, &t0[row..]); + ntt(&mut w); + poly_mul(&mut w, &c); + intt(&mut w); + poly_negate(&mut r, &w); + if infinity_norm(&r) >= gamma2 { + badone = true; + break; + } + poly_sub(&mut ay[row..], &r); + poly_soft_reduce(&mut ay[row..]); + nh = makepartialhint(params, &mut hint, nh, &r, &ay[row..]); + if nh > omega { + badone = true; + break; + } + hint[omega + i] = nh as u8; + } + if badone { + continue; + } + break; + } + pack_sig(params, sig, &mut y, &ct, &hint); + k +} + +fn verify(params: &[usize], pk: &[u8], m: &[u8], sig: &[u8]) -> bool { + let mut rho: [u8; 32] = [0; 32]; + let mut ct: [u8; 32] = [0; 32]; + let mut cct: [u8; 32] = [0; 32]; + let mut tr: [u8; 32] = [0; 32]; + let mut mu: [u8; 64] = [0; 64]; + let mut hint: [u8; 100] = [0; 100]; + + let mut z: [i32; MAXL * DEGREE] = [0; MAXL * DEGREE]; + let mut t1: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let mut w1d: [i8; MAXK * DEGREE] = [0; MAXK * DEGREE]; + + let mut aij: [i32; DEGREE] = [0; DEGREE]; + let mut c: [i32; DEGREE] = [0; DEGREE]; + let mut w: [i32; DEGREE] = [0; DEGREE]; + let mut r: [i32; DEGREE] = [0; DEGREE]; + + let tau = params[0]; + let lg = params[1]; + let gamma1 = (1 << lg) as i32; + let ck = params[3]; + let el = params[4]; + let eta = params[5]; + let beta = (tau * eta) as i32; + let omega = params[7]; + + unpack_pk(params, &mut rho, &mut t1, pk); + unpack_sig(params, &mut z, &mut ct, &mut hint, sig); + + for i in 0..el { + let row = DEGREE * i; + if infinity_norm(&z[row..]) >= gamma1 - beta { + return false; + } + ntt(&mut z[row..]); + } + crh1(params, &mut tr, &rho, &t1); + crh2(&mut mu, &tr, m); + + sampleinball(params, &ct, &mut c); + ntt(&mut c); + + // Calculate az + let mut hints = 0; + for i in 0..ck { + let row = DEGREE * i; + poly_zero(&mut r); + for j in 0..el { + poly_copy(&mut w, &z[j * DEGREE..]); + expandaij(&rho, &mut aij, i, j); + poly_mul(&mut w, &aij); + poly_add(&mut r, &w); + } + poly_hard_reduce(&mut r); + + // Calculate Az-ct1.2^d + for m in 0..DEGREE { + w[m] = ((t1[row + m]) as i32) << D; + } + ntt(&mut w); + poly_mul(&mut w, &c); + poly_sub(&mut r, &w); + intt(&mut r); + + hints = usepartialhint(params, &mut w1d[row..], &mut hint, hints, i, &r); + if hints > omega { + return false; + } + } + + h4(params, &mut cct, &mu, &w1d); + + for i in 0..32 { + if ct[i] != cct[i] { + return false; + } + } + true +} + +// Dilithium API + +pub fn keypair_2(tau: &[u8], sk: &mut [u8], pk: &mut [u8]) { + keypair(&PARAMS_2, tau, sk, pk); +} + +pub fn signature_2(sk: &[u8], m: &[u8], sig: &mut [u8]) -> usize { + signature(&PARAMS_2, sk, m, sig) +} + +pub fn verify_2(pk: &[u8], m: &[u8], sig: &[u8]) -> bool { + verify(&PARAMS_2, pk, m, sig) +} + +pub fn keypair_3(tau: &[u8], sk: &mut [u8], pk: &mut [u8]) { + keypair(&PARAMS_3, tau, sk, pk); +} + +pub fn signature_3(sk: &[u8], m: &[u8], sig: &mut [u8]) -> usize { + signature(&PARAMS_3, sk, m, sig) +} + +pub fn verify_3(pk: &[u8], m: &[u8], sig: &[u8]) -> bool { + verify(&PARAMS_3, pk, m, sig) +} + +pub fn keypair_5(tau: &[u8], sk: &mut [u8], pk: &mut [u8]) { + keypair(&PARAMS_5, tau, sk, pk); +} + +pub fn signature_5(sk: &[u8], m: &[u8], sig: &mut [u8]) -> usize { + signature(&PARAMS_5, sk, m, sig) +} + +pub fn verify_5(pk: &[u8], m: &[u8], sig: &[u8]) -> bool { + verify(&PARAMS_5, pk, m, sig) +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/gcm.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/gcm.rs new file mode 100644 index 000000000000..27c20d0c0581 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/gcm.rs @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const GCM_NB: usize = 4; +const GCM_ACCEPTING_HEADER: usize = 0; +const GCM_ACCEPTING_CIPHER: usize = 1; +const GCM_NOT_ACCEPTING_MORE: usize = 2; +const GCM_FINISHED: usize = 3; +//const GCM_ENCRYPTING: usize = 0; +//const GCM_DECRYPTING: usize = 1; + +use crate::aes; +use crate::aes::AES; + +pub struct GCM { + table: [[u32; 4]; 128], + statex: [u8; 16], + y_0: [u8; 16], + // counter: usize, + lena: [u32; 2], + lenc: [u32; 2], + status: usize, + a: AES, +} + +impl GCM { + fn pack(b: [u8; 4]) -> u32 { + /* pack bytes into a 32-bit Word */ + ((b[0] as u32) << 24) | ((b[1] as u32) << 16) | ((b[2] as u32) << 8) | (b[3] as u32) + } + + fn unpack(a: u32) -> [u8; 4] { + /* unpack bytes from a word */ + [ + ((a >> 24) & 0xff) as u8, + ((a >> 16) & 0xff) as u8, + ((a >> 8) & 0xff) as u8, + (a & 0xff) as u8, + ] + } + + fn precompute(&mut self, h: &[u8]) { + let mut b: [u8; 4] = [0; 4]; + let mut j = 0; + for i in 0..GCM_NB { + b[0] = h[j]; + b[1] = h[j + 1]; + b[2] = h[j + 2]; + b[3] = h[j + 3]; + self.table[0][i] = GCM::pack(b); + j += 4; + } + for i in 1..128 { + let mut c: u32 = 0; + for j in 0..GCM_NB { + self.table[i][j] = c | (self.table[i - 1][j]) >> 1; + c = self.table[i - 1][j] << 31; + } + if c != 0 { + self.table[i][0] ^= 0xE1000000 + } /* irreducible polynomial */ + } + } + + fn gf2mul(&mut self) { + /* gf2m mul - Z=H*X mod 2^128 */ + let mut p: [u32; 4] = [0; 4]; + + for i in 0..4 { + p[i] = 0 + } + let mut j: usize = 8; + let mut m = 0; + for i in 0..128 { + j -= 1; + let mut c = ((self.statex[m] >> j) & 1) as u32; + c = (!c).wrapping_add(1); // + 1; + for k in 0..GCM_NB { + p[k] ^= self.table[i][k] & c + } + if j == 0 { + j = 8; + m += 1; + if m == 16 { + break; + } + } + } + j = 0; + for i in 0..GCM_NB { + let b = GCM::unpack(p[i]); + self.statex[j] = b[0]; + self.statex[j + 1] = b[1]; + self.statex[j + 2] = b[2]; + self.statex[j + 3] = b[3]; + j += 4; + } + } + + fn wrap(&mut self) { + /* Finish off GHASH */ + let mut f: [u32; 4] = [0; 4]; + let mut el: [u8; 16] = [0; 16]; + + /* convert lengths from bytes to bits */ + f[0] = (self.lena[0] << 3) | (self.lena[1] & 0xE0000000) >> 29; + f[1] = self.lena[1] << 3; + f[2] = (self.lenc[0] << 3) | (self.lenc[1] & 0xE0000000) >> 29; + f[3] = self.lenc[1] << 3; + let mut j = 0; + for i in 0..GCM_NB { + let b = GCM::unpack(f[i]); + el[j] = b[0]; + el[j + 1] = b[1]; + el[j + 2] = b[2]; + el[j + 3] = b[3]; + j += 4; + } + for i in 0..16 { + self.statex[i] ^= el[i] + } + self.gf2mul(); + } + + fn ghash(&mut self, plain: &[u8], len: usize) -> bool { + if self.status == GCM_ACCEPTING_HEADER { + self.status = GCM_ACCEPTING_CIPHER + } + if self.status != GCM_ACCEPTING_CIPHER { + return false; + } + + let mut j = 0; + while j < len { + for i in 0..16 { + if j >= len { + break; + } + self.statex[i] ^= plain[j]; + j += 1; + self.lenc[1] += 1; + if self.lenc[1] == 0 { + self.lenc[0] += 1 + } + } + self.gf2mul(); + } + if len % 16 != 0 { + self.status = GCM_NOT_ACCEPTING_MORE + } + true + } + + /* Initialize GCM mode */ + pub fn init(&mut self, nk: usize, key: &[u8], niv: usize, iv: &[u8]) { + /* iv size niv is usually 12 bytes (96 bits). AES key size nk can be 16,24 or 32 bytes */ + let mut h: [u8; 16] = [0; 16]; + + for i in 0..16 { + h[i] = 0; + self.statex[i] = 0 + } + + self.a = AES::new(); + + self.a.init(aes::ECB, nk, key, None); + self.a.ecb_encrypt(&mut h); /* E(K,0) */ + self.precompute(&h); + + self.lena[0] = 0; + self.lenc[0] = 0; + self.lena[1] = 0; + self.lenc[1] = 0; + if niv == 12 { + for i in 0..12 { + self.a.f[i] = iv[i] + } + let b = GCM::unpack(1); + self.a.f[12] = b[0]; + self.a.f[13] = b[1]; + self.a.f[14] = b[2]; + self.a.f[15] = b[3]; /* initialise IV */ + for i in 0..16 { + self.y_0[i] = self.a.f[i] + } + } else { + self.status = GCM_ACCEPTING_CIPHER; + self.ghash(iv, niv); /* GHASH(H,0,IV) */ + self.wrap(); + for i in 0..16 { + self.a.f[i] = self.statex[i]; + self.y_0[i] = self.a.f[i]; + self.statex[i] = 0 + } + self.lena[0] = 0; + self.lenc[0] = 0; + self.lena[1] = 0; + self.lenc[1] = 0; + } + self.status = GCM_ACCEPTING_HEADER; + } + + pub fn new() -> GCM { + GCM { + table: [[0; 4]; 128], + statex: [0; 16], + y_0: [0; 16], + //counter:0, + lena: [0; 2], + lenc: [0; 2], + status: 0, + a: AES::new(), + } + } + + /* Add Header data - included but not encrypted */ + pub fn add_header(&mut self, header: &[u8], len: usize) -> bool { + /* Add some header. Won't be encrypted, but will be authenticated. len is length of header */ + if self.status != GCM_ACCEPTING_HEADER { + return false; + } + let mut j = 0; + while j < len { + for i in 0..16 { + if j >= len { + break; + } + self.statex[i] ^= header[j]; + j += 1; + self.lena[1] += 1; + if self.lena[1] == 0 { + self.lena[0] += 1 + } + } + self.gf2mul(); + } + if len % 16 != 0 { + self.status = GCM_ACCEPTING_CIPHER + } + true + } + + /* Add Plaintext - included and encrypted */ + /* if plain is None - encrypts cipher in place */ + pub fn add_plain(&mut self, cipher: &mut [u8], plain: Option<&[u8]>, len: usize) -> bool { + let mut cb: [u8; 16] = [0; 16]; + let mut b: [u8; 4] = [0; 4]; + + let mut counter: u32; + if self.status == GCM_ACCEPTING_HEADER { + self.status = GCM_ACCEPTING_CIPHER + } + if self.status != GCM_ACCEPTING_CIPHER { + return false; + } + + let mut j = 0; + while j < len { + b[0] = self.a.f[12]; + b[1] = self.a.f[13]; + b[2] = self.a.f[14]; + b[3] = self.a.f[15]; + counter = GCM::pack(b); + counter += 1; + b = GCM::unpack(counter); + self.a.f[12] = b[0]; + self.a.f[13] = b[1]; + self.a.f[14] = b[2]; + self.a.f[15] = b[3]; /* increment counter */ + for i in 0..16 { + cb[i] = self.a.f[i] + } + self.a.ecb_encrypt(&mut cb); /* encrypt it */ + + for i in 0..16 { + if j >= len { + break; + } + + if let Some(sp) = plain { + cipher[j] = sp[j] ^ cb[i]; + } else { + cipher[j] ^= cb[i]; + } + + self.statex[i] ^= cipher[j]; + j += 1; + self.lenc[1] += 1; + if self.lenc[1] == 0 { + self.lenc[0] += 1 + } + } + self.gf2mul() + } + if len % 16 != 0 { + self.status = GCM_NOT_ACCEPTING_MORE + } + true + } + + /* Add Ciphertext - decrypts to plaintext */ + /* if cipher is None - decrypts plain in place */ + pub fn add_cipher(&mut self, plain: &mut [u8], cipher: Option<&[u8]>, len: usize) -> bool { + let mut cb: [u8; 16] = [0; 16]; + let mut b: [u8; 4] = [0; 4]; + + let mut counter: u32; + + if self.status == GCM_ACCEPTING_HEADER { + self.status = GCM_ACCEPTING_CIPHER + } + if self.status != GCM_ACCEPTING_CIPHER { + return false; + } + + let mut j = 0; + while j < len { + b[0] = self.a.f[12]; + b[1] = self.a.f[13]; + b[2] = self.a.f[14]; + b[3] = self.a.f[15]; + counter = GCM::pack(b); + counter += 1; + b = GCM::unpack(counter); + self.a.f[12] = b[0]; + self.a.f[13] = b[1]; + self.a.f[14] = b[2]; + self.a.f[15] = b[3]; /* increment counter */ + for i in 0..16 { + cb[i] = self.a.f[i] + } + self.a.ecb_encrypt(&mut cb); /* encrypt it */ + for i in 0..16 { + if j >= len { + break; + } + let oc: u8; + if let Some(sc) = cipher { + oc = sc[j]; + } else { + oc = plain[j]; + } + + plain[j] = oc ^ cb[i]; + self.statex[i] ^= oc; + j += 1; + self.lenc[1] += 1; + if self.lenc[1] == 0 { + self.lenc[0] += 1 + } + } + self.gf2mul() + } + if len % 16 != 0 { + self.status = GCM_NOT_ACCEPTING_MORE + } + true + } + + /* Finish and extract Tag */ + pub fn finish(&mut self, tag: &mut [u8], extract: bool) { + /* Finish off GHASH and extract tag (MAC) */ + self.wrap(); + /* extract tag */ + if extract { + self.a.ecb_encrypt(&mut (self.y_0)); /* E(K,Y0) */ + for i in 0..16 { + self.y_0[i] ^= self.statex[i] + } + for i in 0..16 { + tag[i] = self.y_0[i]; + self.y_0[i] = 0; + self.statex[i] = 0 + } + } + self.status = GCM_FINISHED; + self.a.end(); + } + + pub fn hex2bytes(hex: &[u8], bin: &mut [u8]) { + let len = hex.len(); + + for i in 0..len / 2 { + let mut v: u8; + let mut c = hex[2 * i]; + if c >= b'0' && c <= b'9' { + v = c - b'0'; + } else if c >= b'A' && c <= b'F' { + v = c - b'A' + 10; + } else if c >= b'a' && c <= b'f' { + v = c - b'a' + 10; + } else { + v = 0; + } + v <<= 4; + c = hex[2 * i + 1]; + if c >= b'0' && c <= b'9' { + v += c - b'0'; + } else if c >= b'A' && c <= b'F' { + v += c - b'A' + 10; + } else if c >= b'a' && c <= b'f' { + v += c - b'a' + 10; + } else { + v = 0; + } + bin[i] = v; + } + } +} + +pub fn encrypt(c: &mut [u8], t: &mut [u8], k: &[u8], iv: &[u8], h: &[u8], p: &[u8]) { + let mut g = GCM::new(); + g.init(k.len(), k, iv.len(), iv); + g.add_header(h, h.len()); + g.add_plain(c, Some(p), p.len()); + g.finish(t, true) +} + +pub fn decrypt(p: &mut [u8], t: &mut [u8], k: &[u8], iv: &[u8], h: &[u8], c: &[u8]) { + let mut g = GCM::new(); + g.init(k.len(), k, iv.len(), iv); + g.add_header(h, h.len()); + g.add_cipher(p, Some(c), c.len()); + g.finish(t, true); +} + +/* +fn main() +{ + let kt=b"feffe9928665731c6d6a8f9467308308"; + let mt=b"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"; + let ht=b"feedfacedeadbeeffeedfacedeadbeefabaddad2"; + let nt=b"9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b"; +// Tag should be 619cc5aefffe0bfa462af43c1699d050 + + let mut gcm=GCM::new(); + + let len=mt.len()/2; + let lenh=ht.len()/2; + let lenk=kt.len()/2; + let leniv=nt.len()/2; + + //let mut t:[u8;16]=[0;16]; // Tag + let mut k:[u8;16]=[0;16]; // AES Key + let mut h:[u8;64]=[0;64]; // Header - to be included in Authentication, but not encrypted + let mut n:[u8;100]=[0;100]; // IV - Initialisation vector + let mut m:[u8;100]=[0;100]; // Plaintext to be encrypted/authenticated + let mut c:[u8;100]=[0;100]; // Ciphertext + let mut p:[u8;100]=[0;100]; // Recovered Plaintext + + GCM::hex2bytes(mt,&mut m); + GCM::hex2bytes(ht,&mut h); + GCM::hex2bytes(kt,&mut k); + GCM::hex2bytes(nt,&mut n); + + println!("Plaintext="); + for i in 0..len {print!("{:02x}",m[i])} + println!(""); + + gcm.init(lenk,&k,leniv,&n); + + gcm.add_header(&h,lenh); + gcm.add_plain(&mut c,&m,len); + let mut t=gcm.finish(true); + + println!("Ciphertext="); + for i in 0..len {print!("{:02x}",c[i])} + println!(""); + + println!("Tag="); + for i in 0..16 {print!("{:02x}",t[i])} + println!(""); + + gcm.init(lenk,&k,leniv,&n); + + gcm.add_header(&h,lenh); + gcm.add_cipher(&mut p,&c,len); + t=gcm.finish(true); + + println!("Plaintext="); + for i in 0..len {print!("{:02x}",p[i])} + println!(""); + + println!("Tag="); + for i in 0..16 {print!("{:02x}",t[i])} + println!(""); + +} +*/ diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash256.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash256.rs new file mode 100644 index 000000000000..9c23f9b54121 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash256.rs @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const HASH256_H0: u32 = 0x6A09E667; +const HASH256_H1: u32 = 0xBB67AE85; +const HASH256_H2: u32 = 0x3C6EF372; +const HASH256_H3: u32 = 0xA54FF53A; +const HASH256_H4: u32 = 0x510E527F; +const HASH256_H5: u32 = 0x9B05688C; +const HASH256_H6: u32 = 0x1F83D9AB; +const HASH256_H7: u32 = 0x5BE0CD19; + +const HASH256_K: [u32; 64] = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +]; + +pub struct HASH256 { + length: [u32; 2], + h: [u32; 8], + w: [u32; 64], +} + +impl HASH256 { + fn s(n: u32, x: u32) -> u32 { + ((x) >> n) | ((x) << (32 - n)) + } + fn r(n: u32, x: u32) -> u32 { + (x) >> n + } + + fn ch(x: u32, y: u32, z: u32) -> u32 { + (x & y) ^ (!(x) & z) + } + + fn maj(x: u32, y: u32, z: u32) -> u32 { + (x & y) ^ (x & z) ^ (y & z) + } + fn sig0(x: u32) -> u32 { + HASH256::s(2, x) ^ HASH256::s(13, x) ^ HASH256::s(22, x) + } + + fn sig1(x: u32) -> u32 { + HASH256::s(6, x) ^ HASH256::s(11, x) ^ HASH256::s(25, x) + } + + fn theta0(x: u32) -> u32 { + HASH256::s(7, x) ^ HASH256::s(18, x) ^ HASH256::r(3, x) + } + + fn theta1(x: u32) -> u32 { + HASH256::s(17, x) ^ HASH256::s(19, x) ^ HASH256::r(10, x) + } + + pub fn as_bytes(&self, array: &mut [u8]) { + let mut ptr = 0; + for i in 0..2 { + let mut t = self.length[i]; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = t as u8; + ptr += 1; + } + for i in 0..8 { + let mut t = self.h[i]; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = t as u8; + ptr += 1; + } + for i in 0..64 { + let mut t = self.w[i]; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = t as u8; + ptr += 1; + } + } + + pub fn from_bytes(&mut self, array: &[u8]) { + let mut ptr = 0; + for i in 0..2 { + let mut t = array[ptr + 3] as u32; + t = 256 * t + (array[ptr + 2] as u32); + t = 256 * t + (array[ptr + 1] as u32); + t = 256 * t + (array[ptr] as u32); + self.length[i] = t; + ptr += 4; + } + for i in 0..8 { + let mut t = array[ptr + 3] as u32; + t = 256 * t + (array[ptr + 2] as u32); + t = 256 * t + (array[ptr + 1] as u32); + t = 256 * t + (array[ptr] as u32); + self.h[i] = t; + ptr += 4; + } + for i in 0..64 { + let mut t = array[ptr + 3] as u32; + t = 256 * t + (array[ptr + 2] as u32); + t = 256 * t + (array[ptr + 1] as u32); + t = 256 * t + (array[ptr] as u32); + self.w[i] = t; + ptr += 4; + } + } + + fn transform(&mut self) { + /* basic transformation step */ + for j in 16..64 { + self.w[j] = HASH256::theta1(self.w[j - 2]) + .wrapping_add(self.w[j - 7]) + .wrapping_add(HASH256::theta0(self.w[j - 15])) + .wrapping_add(self.w[j - 16]); + } + let mut a = self.h[0]; + let mut b = self.h[1]; + let mut c = self.h[2]; + let mut d = self.h[3]; + let mut e = self.h[4]; + let mut f = self.h[5]; + let mut g = self.h[6]; + let mut hh = self.h[7]; + for j in 0..64 { + /* 64 times - mush it up */ + let t1 = hh + .wrapping_add(HASH256::sig1(e)) + .wrapping_add(HASH256::ch(e, f, g)) + .wrapping_add(HASH256_K[j]) + .wrapping_add(self.w[j]); + let t2 = HASH256::sig0(a).wrapping_add(HASH256::maj(a, b, c)); + hh = g; + g = f; + f = e; + e = d.wrapping_add(t1); + d = c; + c = b; + b = a; + a = t1.wrapping_add(t2); + } + self.h[0] = self.h[0].wrapping_add(a); + self.h[1] = self.h[1].wrapping_add(b); + self.h[2] = self.h[2].wrapping_add(c); + self.h[3] = self.h[3].wrapping_add(d); + self.h[4] = self.h[4].wrapping_add(e); + self.h[5] = self.h[5].wrapping_add(f); + self.h[6] = self.h[6].wrapping_add(g); + self.h[7] = self.h[7].wrapping_add(hh); + } + + /* Initialise Hash function */ + pub fn init(&mut self) { + /* initialise */ + for i in 0..64 { + self.w[i] = 0 + } + self.length[0] = 0; + self.length[1] = 0; + self.h[0] = HASH256_H0; + self.h[1] = HASH256_H1; + self.h[2] = HASH256_H2; + self.h[3] = HASH256_H3; + self.h[4] = HASH256_H4; + self.h[5] = HASH256_H5; + self.h[6] = HASH256_H6; + self.h[7] = HASH256_H7; + } + + pub fn new() -> HASH256 { + let mut nh = HASH256 { + length: [0; 2], + h: [0; 8], + w: [0; 64], + }; + nh.init(); + nh + } + + pub fn new_copy(hh: &HASH256) -> HASH256 { + let mut nh = HASH256 { + length: [0; 2], + h: [0; 8], + w: [0; 64], + }; + nh.length[0] = hh.length[0]; + nh.length[1] = hh.length[1]; + for i in 0..64 { + nh.w[i] = hh.w[i]; + } + for i in 0..8 { + nh.h[i] = hh.h[i]; + } + nh + } + + /* process a single byte */ + pub fn process(&mut self, byt: u8) { + /* process the next message byte */ + let cnt = ((self.length[0] / 32) % 16) as usize; + self.w[cnt] <<= 8; + self.w[cnt] |= byt as u32; + self.length[0] += 8; + if self.length[0] == 0 { + self.length[1] += 1; + self.length[0] = 0 + } + if (self.length[0] % 512) == 0 { + self.transform() + } + } + + /* process an array of bytes */ + + pub fn process_array(&mut self, b: &[u8]) { + for i in 0..b.len() { + self.process(b[i]) + } + } + + /* process a 32-bit integer */ + pub fn process_num(&mut self, n: i32) { + self.process(((n >> 24) & 0xff) as u8); + self.process(((n >> 16) & 0xff) as u8); + self.process(((n >> 8) & 0xff) as u8); + self.process((n & 0xff) as u8); + } + + /* Generate 32-byte Hash */ + pub fn hash(&mut self) -> [u8; 32] { + /* pad message and finish - supply digest */ + let mut digest: [u8; 32] = [0; 32]; + let len0 = self.length[0]; + let len1 = self.length[1]; + self.process(0x80); + while (self.length[0] % 512) != 448 { + self.process(0) + } + self.w[14] = len1; + self.w[15] = len0; + self.transform(); + for i in 0..32 { + /* convert to bytes */ + digest[i] = ((self.h[i / 4] >> (8 * (3 - i % 4))) & 0xff) as u8; + } + self.init(); + digest + } + + pub fn continuing_hash(&self) -> [u8; 32] { + let mut sh = HASH256::new_copy(self); + sh.hash() + } +} + +//248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1 +/* +fn main() { + let s = String::from("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + let test = s.into_bytes(); + let mut sh=HASH256::new(); + + for i in 0..test.len(){ + sh.process(test[i]); + } + + let digest=sh.hash(); + for i in 0..32 {print!("{:02x}",digest[i])} +} +*/ diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash384.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash384.rs new file mode 100644 index 000000000000..4c0b8e2d72f3 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash384.rs @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const HASH384_H0: u64 = 0xcbbb9d5dc1059ed8; +const HASH384_H1: u64 = 0x629a292a367cd507; +const HASH384_H2: u64 = 0x9159015a3070dd17; +const HASH384_H3: u64 = 0x152fecd8f70e5939; +const HASH384_H4: u64 = 0x67332667ffc00b31; +const HASH384_H5: u64 = 0x8eb44a8768581511; +const HASH384_H6: u64 = 0xdb0c2e0d64f98fa7; +const HASH384_H7: u64 = 0x47b5481dbefa4fa4; + +const HASH384_K: [u64; 80] = [ + 0x428a2f98d728ae22, + 0x7137449123ef65cd, + 0xb5c0fbcfec4d3b2f, + 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, + 0x59f111f1b605d019, + 0x923f82a4af194f9b, + 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, + 0x12835b0145706fbe, + 0x243185be4ee4b28c, + 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, + 0x80deb1fe3b1696b1, + 0x9bdc06a725c71235, + 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, + 0xefbe4786384f25e3, + 0x0fc19dc68b8cd5b5, + 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, + 0x4a7484aa6ea6e483, + 0x5cb0a9dcbd41fbd4, + 0x76f988da831153b5, + 0x983e5152ee66dfab, + 0xa831c66d2db43210, + 0xb00327c898fb213f, + 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, + 0xd5a79147930aa725, + 0x06ca6351e003826f, + 0x142929670a0e6e70, + 0x27b70a8546d22ffc, + 0x2e1b21385c26c926, + 0x4d2c6dfc5ac42aed, + 0x53380d139d95b3df, + 0x650a73548baf63de, + 0x766a0abb3c77b2a8, + 0x81c2c92e47edaee6, + 0x92722c851482353b, + 0xa2bfe8a14cf10364, + 0xa81a664bbc423001, + 0xc24b8b70d0f89791, + 0xc76c51a30654be30, + 0xd192e819d6ef5218, + 0xd69906245565a910, + 0xf40e35855771202a, + 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, + 0x1e376c085141ab53, + 0x2748774cdf8eeb99, + 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, + 0x4ed8aa4ae3418acb, + 0x5b9cca4f7763e373, + 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, + 0x78a5636f43172f60, + 0x84c87814a1f0ab72, + 0x8cc702081a6439ec, + 0x90befffa23631e28, + 0xa4506cebde82bde9, + 0xbef9a3f7b2c67915, + 0xc67178f2e372532b, + 0xca273eceea26619c, + 0xd186b8c721c0c207, + 0xeada7dd6cde0eb1e, + 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, + 0x0a637dc5a2c898a6, + 0x113f9804bef90dae, + 0x1b710b35131c471b, + 0x28db77f523047d84, + 0x32caab7b40c72493, + 0x3c9ebe0a15c9bebc, + 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, + 0x597f299cfc657e2a, + 0x5fcb6fab3ad6faec, + 0x6c44198c4a475817, +]; + +pub struct HASH384 { + length: [u64; 2], + h: [u64; 8], + w: [u64; 80], +} + +impl HASH384 { + fn s(n: u64, x: u64) -> u64 { + ((x) >> n) | ((x) << (64 - n)) + } + fn r(n: u64, x: u64) -> u64 { + (x) >> n + } + + fn ch(x: u64, y: u64, z: u64) -> u64 { + (x & y) ^ (!(x) & z) + } + + fn maj(x: u64, y: u64, z: u64) -> u64 { + (x & y) ^ (x & z) ^ (y & z) + } + + fn sig0(x: u64) -> u64 { + HASH384::s(28, x) ^ HASH384::s(34, x) ^ HASH384::s(39, x) + } + + fn sig1(x: u64) -> u64 { + HASH384::s(14, x) ^ HASH384::s(18, x) ^ HASH384::s(41, x) + } + + fn theta0(x: u64) -> u64 { + HASH384::s(1, x) ^ HASH384::s(8, x) ^ HASH384::r(7, x) + } + + fn theta1(x: u64) -> u64 { + HASH384::s(19, x) ^ HASH384::s(61, x) ^ HASH384::r(6, x) + } + + pub fn as_bytes(&self, array: &mut [u8]) { + let mut ptr = 0; + for i in 0..2 { + let mut t = self.length[i]; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = t as u8; + ptr += 1; + } + for i in 0..8 { + let mut t = self.h[i]; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = t as u8; + ptr += 1; + } + for i in 0..80 { + let mut t = self.w[i]; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = t as u8; + ptr += 1; + } + } + + pub fn from_bytes(&mut self, array: &[u8]) { + let mut ptr = 0; + for i in 0..2 { + let mut t = array[ptr + 7] as u64; + t = 256 * t + (array[ptr + 6] as u64); + t = 256 * t + (array[ptr + 5] as u64); + t = 256 * t + (array[ptr + 4] as u64); + t = 256 * t + (array[ptr + 3] as u64); + t = 256 * t + (array[ptr + 2] as u64); + t = 256 * t + (array[ptr + 1] as u64); + t = 256 * t + (array[ptr] as u64); + self.length[i] = t; + ptr += 8; + } + for i in 0..8 { + let mut t = array[ptr + 7] as u64; + t = 256 * t + (array[ptr + 6] as u64); + t = 256 * t + (array[ptr + 5] as u64); + t = 256 * t + (array[ptr + 4] as u64); + t = 256 * t + (array[ptr + 3] as u64); + t = 256 * t + (array[ptr + 2] as u64); + t = 256 * t + (array[ptr + 1] as u64); + t = 256 * t + (array[ptr] as u64); + self.h[i] = t; + ptr += 8; + } + for i in 0..80 { + let mut t = array[ptr + 7] as u64; + t = 256 * t + (array[ptr + 6] as u64); + t = 256 * t + (array[ptr + 5] as u64); + t = 256 * t + (array[ptr + 4] as u64); + t = 256 * t + (array[ptr + 3] as u64); + t = 256 * t + (array[ptr + 2] as u64); + t = 256 * t + (array[ptr + 1] as u64); + t = 256 * t + (array[ptr] as u64); + self.w[i] = t; + ptr += 8; + } + } + + fn transform(&mut self) { + /* basic transformation step */ + for j in 16..80 { + self.w[j] = HASH384::theta1(self.w[j - 2]) + .wrapping_add(self.w[j - 7]) + .wrapping_add(HASH384::theta0(self.w[j - 15])) + .wrapping_add(self.w[j - 16]); + } + let mut a = self.h[0]; + let mut b = self.h[1]; + let mut c = self.h[2]; + let mut d = self.h[3]; + let mut e = self.h[4]; + let mut f = self.h[5]; + let mut g = self.h[6]; + let mut hh = self.h[7]; + for j in 0..80 { + /* 64 times - mush it up */ + let t1 = hh + .wrapping_add(HASH384::sig1(e)) + .wrapping_add(HASH384::ch(e, f, g)) + .wrapping_add(HASH384_K[j]) + .wrapping_add(self.w[j]); + let t2 = HASH384::sig0(a).wrapping_add(HASH384::maj(a, b, c)); + hh = g; + g = f; + f = e; + e = d.wrapping_add(t1); + d = c; + c = b; + b = a; + a = t1.wrapping_add(t2); + } + self.h[0] = self.h[0].wrapping_add(a); + self.h[1] = self.h[1].wrapping_add(b); + self.h[2] = self.h[2].wrapping_add(c); + self.h[3] = self.h[3].wrapping_add(d); + self.h[4] = self.h[4].wrapping_add(e); + self.h[5] = self.h[5].wrapping_add(f); + self.h[6] = self.h[6].wrapping_add(g); + self.h[7] = self.h[7].wrapping_add(hh); + } + + /* Initialise Hash function */ + pub fn init(&mut self) { + /* initialise */ + for i in 0..64 { + self.w[i] = 0 + } + self.length[0] = 0; + self.length[1] = 0; + self.h[0] = HASH384_H0; + self.h[1] = HASH384_H1; + self.h[2] = HASH384_H2; + self.h[3] = HASH384_H3; + self.h[4] = HASH384_H4; + self.h[5] = HASH384_H5; + self.h[6] = HASH384_H6; + self.h[7] = HASH384_H7; + } + + pub fn new() -> HASH384 { + let mut nh = HASH384 { + length: [0; 2], + h: [0; 8], + w: [0; 80], + }; + nh.init(); + nh + } + + pub fn new_copy(hh: &HASH384) -> HASH384 { + let mut nh = HASH384 { + length: [0; 2], + h: [0; 8], + w: [0; 80], + }; + nh.length[0] = hh.length[0]; + nh.length[1] = hh.length[1]; + for i in 0..80 { + nh.w[i] = hh.w[i]; + } + for i in 0..8 { + nh.h[i] = hh.h[i]; + } + nh + } + + /* process a single byte */ + pub fn process(&mut self, byt: u8) { + /* process the next message byte */ + let cnt = ((self.length[0] / 64) % 16) as usize; + self.w[cnt] <<= 8; + self.w[cnt] |= byt as u64; + self.length[0] += 8; + if self.length[0] == 0 { + self.length[1] += 1; + self.length[0] = 0 + } + if (self.length[0] % 1024) == 0 { + self.transform() + } + } + + /* process an array of bytes */ + + pub fn process_array(&mut self, b: &[u8]) { + for i in 0..b.len() { + self.process(b[i]) + } + } + + /* process a 32-bit integer */ + pub fn process_num(&mut self, n: i32) { + self.process(((n >> 24) & 0xff) as u8); + self.process(((n >> 16) & 0xff) as u8); + self.process(((n >> 8) & 0xff) as u8); + self.process((n & 0xff) as u8); + } + + /* Generate 32-byte Hash */ + pub fn hash(&mut self) -> [u8; 48] { + /* pad message and finish - supply digest */ + let mut digest: [u8; 48] = [0; 48]; + let len0 = self.length[0]; + let len1 = self.length[1]; + self.process(0x80); + while (self.length[0] % 1024) != 896 { + self.process(0) + } + self.w[14] = len1; + self.w[15] = len0; + self.transform(); + for i in 0..48 { + /* convert to bytes */ + digest[i] = ((self.h[i / 8] >> (8 * (7 - i % 8))) & 0xff) as u8; + } + self.init(); + digest + } + pub fn continuing_hash(&self) -> [u8; 48] { + let mut sh = HASH384::new_copy(self); + sh.hash() + } +} + +//09330c33f71147e8 3d192fc782cd1b47 53111b173b3b05d2 2fa08086e3b0f712 fcc7c71a557e2db9 66c3e9fa91746039 +/* +fn main() { + let s = String::from("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); + let test = s.into_bytes(); + let mut sh=HASH384::new(); + + for i in 0..test.len(){ + sh.process(test[i]); + } + + let digest=sh.hash(); + for i in 0..48 {print!("{:02x}",digest[i])} +} */ diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash512.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash512.rs new file mode 100644 index 000000000000..206f9762549d --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hash512.rs @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const HASH512_H0: u64 = 0x6a09e667f3bcc908; +const HASH512_H1: u64 = 0xbb67ae8584caa73b; +const HASH512_H2: u64 = 0x3c6ef372fe94f82b; +const HASH512_H3: u64 = 0xa54ff53a5f1d36f1; +const HASH512_H4: u64 = 0x510e527fade682d1; +const HASH512_H5: u64 = 0x9b05688c2b3e6c1f; +const HASH512_H6: u64 = 0x1f83d9abfb41bd6b; +const HASH512_H7: u64 = 0x5be0cd19137e2179; + +const HASH512_K: [u64; 80] = [ + 0x428a2f98d728ae22, + 0x7137449123ef65cd, + 0xb5c0fbcfec4d3b2f, + 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, + 0x59f111f1b605d019, + 0x923f82a4af194f9b, + 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, + 0x12835b0145706fbe, + 0x243185be4ee4b28c, + 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, + 0x80deb1fe3b1696b1, + 0x9bdc06a725c71235, + 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, + 0xefbe4786384f25e3, + 0x0fc19dc68b8cd5b5, + 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, + 0x4a7484aa6ea6e483, + 0x5cb0a9dcbd41fbd4, + 0x76f988da831153b5, + 0x983e5152ee66dfab, + 0xa831c66d2db43210, + 0xb00327c898fb213f, + 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, + 0xd5a79147930aa725, + 0x06ca6351e003826f, + 0x142929670a0e6e70, + 0x27b70a8546d22ffc, + 0x2e1b21385c26c926, + 0x4d2c6dfc5ac42aed, + 0x53380d139d95b3df, + 0x650a73548baf63de, + 0x766a0abb3c77b2a8, + 0x81c2c92e47edaee6, + 0x92722c851482353b, + 0xa2bfe8a14cf10364, + 0xa81a664bbc423001, + 0xc24b8b70d0f89791, + 0xc76c51a30654be30, + 0xd192e819d6ef5218, + 0xd69906245565a910, + 0xf40e35855771202a, + 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, + 0x1e376c085141ab53, + 0x2748774cdf8eeb99, + 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, + 0x4ed8aa4ae3418acb, + 0x5b9cca4f7763e373, + 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, + 0x78a5636f43172f60, + 0x84c87814a1f0ab72, + 0x8cc702081a6439ec, + 0x90befffa23631e28, + 0xa4506cebde82bde9, + 0xbef9a3f7b2c67915, + 0xc67178f2e372532b, + 0xca273eceea26619c, + 0xd186b8c721c0c207, + 0xeada7dd6cde0eb1e, + 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, + 0x0a637dc5a2c898a6, + 0x113f9804bef90dae, + 0x1b710b35131c471b, + 0x28db77f523047d84, + 0x32caab7b40c72493, + 0x3c9ebe0a15c9bebc, + 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, + 0x597f299cfc657e2a, + 0x5fcb6fab3ad6faec, + 0x6c44198c4a475817, +]; + +pub struct HASH512 { + length: [u64; 2], + h: [u64; 8], + w: [u64; 80], +} + +impl HASH512 { + fn s(n: u64, x: u64) -> u64 { + ((x) >> n) | ((x) << (64 - n)) + } + fn r(n: u64, x: u64) -> u64 { + (x) >> n + } + + fn ch(x: u64, y: u64, z: u64) -> u64 { + (x & y) ^ (!(x) & z) + } + + fn maj(x: u64, y: u64, z: u64) -> u64 { + (x & y) ^ (x & z) ^ (y & z) + } + + fn sig0(x: u64) -> u64 { + HASH512::s(28, x) ^ HASH512::s(34, x) ^ HASH512::s(39, x) + } + + fn sig1(x: u64) -> u64 { + HASH512::s(14, x) ^ HASH512::s(18, x) ^ HASH512::s(41, x) + } + + fn theta0(x: u64) -> u64 { + HASH512::s(1, x) ^ HASH512::s(8, x) ^ HASH512::r(7, x) + } + + fn theta1(x: u64) -> u64 { + HASH512::s(19, x) ^ HASH512::s(61, x) ^ HASH512::r(6, x) + } + + pub fn as_bytes(&self, array: &mut [u8]) { + let mut ptr = 0; + for i in 0..2 { + let mut t = self.length[i]; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = t as u8; + ptr += 1; + } + for i in 0..8 { + let mut t = self.h[i]; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = t as u8; + ptr += 1; + } + for i in 0..80 { + let mut t = self.w[i]; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = (t % 256) as u8; + t /= 256; + ptr += 1; + array[ptr] = t as u8; + ptr += 1; + } + } + + pub fn from_bytes(&mut self, array: &[u8]) { + let mut ptr = 0; + for i in 0..2 { + let mut t = array[ptr + 7] as u64; + t = 256 * t + (array[ptr + 6] as u64); + t = 256 * t + (array[ptr + 5] as u64); + t = 256 * t + (array[ptr + 4] as u64); + t = 256 * t + (array[ptr + 3] as u64); + t = 256 * t + (array[ptr + 2] as u64); + t = 256 * t + (array[ptr + 1] as u64); + t = 256 * t + (array[ptr] as u64); + self.length[i] = t; + ptr += 8; + } + for i in 0..8 { + let mut t = array[ptr + 7] as u64; + t = 256 * t + (array[ptr + 6] as u64); + t = 256 * t + (array[ptr + 5] as u64); + t = 256 * t + (array[ptr + 4] as u64); + t = 256 * t + (array[ptr + 3] as u64); + t = 256 * t + (array[ptr + 2] as u64); + t = 256 * t + (array[ptr + 1] as u64); + t = 256 * t + (array[ptr] as u64); + self.h[i] = t; + ptr += 8; + } + for i in 0..80 { + let mut t = array[ptr + 7] as u64; + t = 256 * t + (array[ptr + 6] as u64); + t = 256 * t + (array[ptr + 5] as u64); + t = 256 * t + (array[ptr + 4] as u64); + t = 256 * t + (array[ptr + 3] as u64); + t = 256 * t + (array[ptr + 2] as u64); + t = 256 * t + (array[ptr + 1] as u64); + t = 256 * t + (array[ptr] as u64); + self.w[i] = t; + ptr += 8; + } + } + + fn transform(&mut self) { + /* basic transformation step */ + for j in 16..80 { + self.w[j] = HASH512::theta1(self.w[j - 2]) + .wrapping_add(self.w[j - 7]) + .wrapping_add(HASH512::theta0(self.w[j - 15])) + .wrapping_add(self.w[j - 16]); + } + let mut a = self.h[0]; + let mut b = self.h[1]; + let mut c = self.h[2]; + let mut d = self.h[3]; + let mut e = self.h[4]; + let mut f = self.h[5]; + let mut g = self.h[6]; + let mut hh = self.h[7]; + for j in 0..80 { + /* 64 times - mush it up */ + let t1 = hh + .wrapping_add(HASH512::sig1(e)) + .wrapping_add(HASH512::ch(e, f, g)) + .wrapping_add(HASH512_K[j]) + .wrapping_add(self.w[j]); + let t2 = HASH512::sig0(a).wrapping_add(HASH512::maj(a, b, c)); + hh = g; + g = f; + f = e; + e = d.wrapping_add(t1); + d = c; + c = b; + b = a; + a = t1.wrapping_add(t2); + } + self.h[0] = self.h[0].wrapping_add(a); + self.h[1] = self.h[1].wrapping_add(b); + self.h[2] = self.h[2].wrapping_add(c); + self.h[3] = self.h[3].wrapping_add(d); + self.h[4] = self.h[4].wrapping_add(e); + self.h[5] = self.h[5].wrapping_add(f); + self.h[6] = self.h[6].wrapping_add(g); + self.h[7] = self.h[7].wrapping_add(hh); + } + + /* Initialise Hash function */ + pub fn init(&mut self) { + /* initialise */ + for i in 0..64 { + self.w[i] = 0 + } + self.length[0] = 0; + self.length[1] = 0; + self.h[0] = HASH512_H0; + self.h[1] = HASH512_H1; + self.h[2] = HASH512_H2; + self.h[3] = HASH512_H3; + self.h[4] = HASH512_H4; + self.h[5] = HASH512_H5; + self.h[6] = HASH512_H6; + self.h[7] = HASH512_H7; + } + + pub fn new() -> HASH512 { + let mut nh = HASH512 { + length: [0; 2], + h: [0; 8], + w: [0; 80], + }; + nh.init(); + nh + } + + pub fn new_copy(hh: &HASH512) -> HASH512 { + let mut nh = HASH512 { + length: [0; 2], + h: [0; 8], + w: [0; 80], + }; + nh.length[0] = hh.length[0]; + nh.length[1] = hh.length[1]; + for i in 0..80 { + nh.w[i] = hh.w[i]; + } + for i in 0..8 { + nh.h[i] = hh.h[i]; + } + nh + } + + /* process a single byte */ + pub fn process(&mut self, byt: u8) { + /* process the next message byte */ + let cnt = ((self.length[0] / 64) % 16) as usize; + self.w[cnt] <<= 8; + self.w[cnt] |= byt as u64; + self.length[0] += 8; + if self.length[0] == 0 { + self.length[1] += 1; + self.length[0] = 0 + } + if (self.length[0] % 1024) == 0 { + self.transform() + } + } + + /* process an array of bytes */ + + pub fn process_array(&mut self, b: &[u8]) { + for i in 0..b.len() { + self.process(b[i]) + } + } + + /* process a 32-bit integer */ + pub fn process_num(&mut self, n: i32) { + self.process(((n >> 24) & 0xff) as u8); + self.process(((n >> 16) & 0xff) as u8); + self.process(((n >> 8) & 0xff) as u8); + self.process((n & 0xff) as u8); + } + + /* Generate 32-byte Hash */ + pub fn hash(&mut self) -> [u8; 64] { + /* pad message and finish - supply digest */ + let mut digest: [u8; 64] = [0; 64]; + let len0 = self.length[0]; + let len1 = self.length[1]; + self.process(0x80); + while (self.length[0] % 1024) != 896 { + self.process(0) + } + self.w[14] = len1; + self.w[15] = len0; + self.transform(); + for i in 0..64 { + /* convert to bytes */ + digest[i] = ((self.h[i / 8] >> (8 * (7 - i % 8))) & 0xff) as u8; + } + self.init(); + digest + } + pub fn continuing_hash(&self) -> [u8; 64] { + let mut sh = HASH512::new_copy(self); + sh.hash() + } +} + +//8e959b75dae313da 8cf4f72814fc143f 8f7779c6eb9f7fa1 7299aeadb6889018 501d289e4900f7e4 331b99dec4b5433a c7d329eeb6dd2654 5e96e55b874be909 +/* +fn main() { + let s = String::from("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); + let test = s.into_bytes(); + let mut sh=HASH512::new(); + + for i in 0..test.len(){ + sh.process(test[i]); + } + + let digest=sh.hash(); + for i in 0..64 {print!("{:02x}",digest[i])} +} */ diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hmac.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hmac.rs new file mode 100644 index 000000000000..1b2f3e894e8a --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/hmac.rs @@ -0,0 +1,943 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::hash256::HASH256; +use crate::hash384::HASH384; +use crate::hash512::HASH512; +use crate::rand::RAND; +use crate::sha3::SHA3; + +pub const MC_SHA2: usize = 2; +pub const MC_SHA3: usize = 3; +pub const SHA256: usize = 32; +pub const SHA384: usize = 48; +pub const SHA512: usize = 64; + +#[allow(non_snake_case)] +/* General Purpose Hash function */ +#[allow(clippy::too_many_arguments)] +pub fn GPhashit( + hash: usize, + sha: usize, + w: &mut [u8], + pad: usize, + zpad: usize, + a: Option<&[u8]>, + n: isize, + b: Option<&[u8]>, +) { + let mut r: [u8; 64] = [0; 64]; + + if hash == MC_SHA2 { + if sha == SHA256 { + let mut h = HASH256::new(); + for _ in 0..zpad { + h.process(0); + } + if let Some(x) = a { + h.process_array(x); + } + if n >= 0 { + h.process_num(n as i32) + } + if let Some(x) = b { + h.process_array(x); + } + let hs = h.hash(); + for i in 0..sha { + r[i] = hs[i]; + } + } + if sha == SHA384 { + let mut h = HASH384::new(); + for _ in 0..zpad { + h.process(0); + } + if let Some(x) = a { + h.process_array(x); + } + if n >= 0 { + h.process_num(n as i32) + } + if let Some(x) = b { + h.process_array(x); + } + let hs = h.hash(); + for i in 0..sha { + r[i] = hs[i]; + } + } + if sha == SHA512 { + let mut h = HASH512::new(); + for _ in 0..zpad { + h.process(0); + } + if let Some(x) = a { + h.process_array(x); + } + if n >= 0 { + h.process_num(n as i32) + } + if let Some(x) = b { + h.process_array(x); + } + let hs = h.hash(); + for i in 0..sha { + r[i] = hs[i]; + } + } + } + if hash == MC_SHA3 { + let mut h = SHA3::new(sha); + for _ in 0..zpad { + h.process(0); + } + if let Some(x) = a { + h.process_array(x); + } + if n >= 0 { + h.process_num(n as i32) + } + if let Some(x) = b { + h.process_array(x); + } + h.hash(&mut r); + } + + if pad == 0 { + for i in 0..sha { + w[i] = r[i] + } + } else if pad <= sha { + for i in 0..pad { + w[i] = r[i] + } + } else { + for i in 0..sha { + w[i + pad - sha] = r[i] + } + for i in 0..(pad - sha) { + w[i] = 0 + } + } +} + +#[allow(non_snake_case)] +pub fn SPhashit(hash: usize, sha: usize, w: &mut [u8], a: Option<&[u8]>) { + GPhashit(hash, sha, w, 0, 0, a, -1, None); +} + +pub fn inttobytes(n: usize, b: &mut [u8]) { + let mut i = b.len(); + let mut m = n; + while m > 0 && i > 0 { + i -= 1; + b[i] = (m & 0xff) as u8; + m /= 256; + } +} + +pub fn kdf2(hash: usize, sha: usize, z: &[u8], p: Option<&[u8]>, olen: usize, k: &mut [u8]) { + /* NOTE: the parameter olen is the length of the output K in bytes */ + let hlen = sha; + let mut lk = 0; + + let mut cthreshold = olen / hlen; + if olen % hlen != 0 { + cthreshold += 1 + } + + for counter in 1..cthreshold + 1 { + let mut b: [u8; 64] = [0; 64]; + GPhashit(hash, sha, &mut b, 0, 0, Some(z), counter as isize, p); + if lk + hlen > olen { + for i in 0..(olen % hlen) { + k[lk] = b[i]; + lk += 1 + } + } else { + for i in 0..hlen { + k[lk] = b[i]; + lk += 1 + } + } + } +} + +/* Password based Key Derivation Function */ +/* Input password p, salt s, and repeat count */ +/* Output key of length olen */ +pub fn pbkdf2( + hash: usize, + sha: usize, + pass: &[u8], + salt: &[u8], + rep: usize, + olen: usize, + k: &mut [u8], +) { + let mut d = olen / sha; + if olen % sha != 0 { + d += 1 + } + let mut f: [u8; 64] = [0; 64]; + let mut u: [u8; 64] = [0; 64]; + let mut ku: [u8; 64] = [0; 64]; + let mut s: [u8; 36] = [0; 36]; // Maximum salt of 32 bytes + 4 + let mut n: [u8; 4] = [0; 4]; + + let sl = salt.len(); + let mut kp = 0; + for i in 0..d { + for j in 0..sl { + s[j] = salt[j] + } + inttobytes(i + 1, &mut n); + for j in 0..4 { + s[sl + j] = n[j] + } + + hmac1(hash, sha, &mut f, sha, &s[0..sl + 4], pass); + + for j in 0..sha { + u[j] = f[j] + } + + for _ in 1..rep { + hmac1(hash, sha, &mut ku, sha, &u, pass); + for m in 0..sha { + u[m] = ku[m]; + f[m] ^= u[m] + } + } + for j in 0..sha { + if kp < olen { + k[kp] = f[j] + } + kp += 1 + } + } +} + +fn blksize(hash: usize, sha: usize) -> usize { + let mut lb = 0; + if hash == MC_SHA2 { + lb = 64; + if sha > 32 { + lb = 128; + } + } + if hash == MC_SHA3 { + lb = 200 - 2 * sha; + } + lb +} + +/* Calculate HMAC of m using key k. HMAC is tag of length olen (which is length of tag) */ +pub fn hmac1(hash: usize, sha: usize, tag: &mut [u8], olen: usize, k: &[u8], m: &[u8]) -> bool { + /* Input is from an octet m * + * olen is requested output length in bytes. k is the key * + * The output is the calculated tag */ + let mut b: [u8; 64] = [0; 64]; /* Not good */ + let mut k0: [u8; 128] = [0; 128]; + + let lb = blksize(hash, sha); + if lb == 0 { + return false; + } + + for i in 0..lb { + k0[i] = 0 + } + + if k.len() > lb { + SPhashit(hash, sha, &mut b, Some(k)); + //GPhashit(hash, sha, &mut b,0,0,k, 0, None); + for i in 0..sha { + k0[i] = b[i] + } + } else { + for i in 0..k.len() { + k0[i] = k[i] + } + } + + for i in 0..lb { + k0[i] ^= 0x36 + } + + GPhashit(hash, sha, &mut b, 0, 0, Some(&k0[0..lb]), -1, Some(m)); + + for i in 0..lb { + k0[i] ^= 0x6a + } + GPhashit( + hash, + sha, + tag, + olen, + 0, + Some(&k0[0..lb]), + -1, + Some(&b[0..sha]), + ); + + true +} + +pub fn hkdf_extract(hash: usize, hlen: usize, prk: &mut [u8], salt: Option<&[u8]>, ikm: &[u8]) { + if let Some(x) = salt { + hmac1(hash, hlen, prk, hlen, x, ikm); + } else { + let h: [u8; 64] = [0; 64]; + hmac1(hash, hlen, prk, hlen, &h[0..hlen], ikm); + } +} + +pub fn hkdf_expand(hash: usize, hlen: usize, okm: &mut [u8], olen: usize, prk: &[u8], info: &[u8]) { + let n = olen / hlen; + let flen = olen % hlen; + + let mut t: [u8; 1024] = [0; 1024]; // >= info.length+hlen+1 + let mut k: [u8; 64] = [0; 64]; + + let mut l = 0; + let mut m = 0; + for i in 1..=n { + for j in 0..info.len() { + t[l] = info[j]; + l += 1; + } + t[l] = i as u8; + l += 1; + hmac1(hash, hlen, &mut k, hlen, prk, &t[0..l]); + l = 0; + for j in 0..hlen { + okm[m] = k[j]; + m += 1; + t[l] = k[j]; + l += 1; + } + } + if flen > 0 { + for j in 0..info.len() { + t[l] = info[j]; + l += 1; + } + t[l] = (n + 1) as u8; + l += 1; + hmac1(hash, hlen, &mut k, flen, prk, &t[0..l]); + for j in 0..flen { + okm[m] = k[j]; + m += 1; + } + } +} + +fn ceil(a: usize, b: usize) -> usize { + (a - 1) / b + 1 +} + +pub fn xof_expand(hlen: usize, okm: &mut [u8], olen: usize, dst: &[u8], msg: &[u8]) { + let mut h = SHA3::new(hlen); + for i in 0..msg.len() { + h.process(msg[i]); + } + h.process(((olen >> 8) & 0xff) as u8); + h.process((olen & 0xff) as u8); + + for i in 0..dst.len() { + h.process(dst[i]); + } + h.process((dst.len() & 0xff) as u8); + + h.shake(okm, olen); +} + +pub fn xmd_expand(hash: usize, hlen: usize, okm: &mut [u8], olen: usize, dst: &[u8], msg: &[u8]) { + let mut w: [u8; 64] = [0; 64]; + if dst.len() >= 256 { + GPhashit( + hash, + hlen, + &mut w, + 0, + 0, + Some(b"H2C-OVERSIZE-DST-"), + -1, + Some(&dst), + ); + xmd_expand_short_dst(hash, hlen, okm, olen, &w[0..hlen], msg); + } else { + xmd_expand_short_dst(hash, hlen, okm, olen, dst, msg); + } +} + +// Assumes dst.len() < 256. +fn xmd_expand_short_dst( + hash: usize, + hlen: usize, + okm: &mut [u8], + olen: usize, + dst: &[u8], + msg: &[u8], +) { + let mut tmp: [u8; 260] = [0; 260]; + let mut h0: [u8; 64] = [0; 64]; + let mut h1: [u8; 64] = [0; 64]; + let mut h2: [u8; 64] = [0; 64]; + + let ell = ceil(olen, hlen); + let blk = blksize(hash, hlen); + tmp[0] = ((olen >> 8) & 0xff) as u8; + tmp[1] = (olen & 0xff) as u8; + tmp[2] = 0; + + for j in 0..dst.len() { + tmp[3 + j] = dst[j]; + } + tmp[3 + dst.len()] = (dst.len() & 0xff) as u8; + + GPhashit( + hash, + hlen, + &mut h0, + 0, + blk, + Some(msg), + -1, + Some(&tmp[0..dst.len() + 4]), + ); + + let mut k = 0; + for i in 1..=ell { + for j in 0..hlen { + h1[j] ^= h0[j]; + h2[j] = h1[j]; + } + tmp[0] = i as u8; + + for j in 0..dst.len() { + tmp[1 + j] = dst[j]; + } + tmp[1 + dst.len()] = (dst.len() & 0xff) as u8; + + GPhashit( + hash, + hlen, + &mut h1, + 0, + 0, + Some(&h2[0..hlen]), + -1, + Some(&tmp[0..dst.len() + 2]), + ); + for j in 0..hlen { + okm[k] = h1[j]; + k += 1; + if k == olen { + break; + } + } + } +} + +/* Mask Generation Function */ + +pub fn mgf1(sha: usize, z: &[u8], olen: usize, k: &mut [u8]) { + let hlen = sha; + + let mut j = 0; + for i in 0..k.len() { + k[i] = 0 + } + + let mut cthreshold = olen / hlen; + if olen % hlen != 0 { + cthreshold += 1 + } + for counter in 0..cthreshold { + let mut b: [u8; 64] = [0; 64]; + GPhashit(MC_SHA2, sha, &mut b, 0, 0, Some(z), counter as isize, None); + //hashit(sha, Some(z), counter as isize, &mut b); + + if j + hlen > olen { + for i in 0..(olen % hlen) { + k[j] = b[i]; + j += 1 + } + } else { + for i in 0..hlen { + k[j] = b[i]; + j += 1 + } + } + } +} + +pub fn mgf1xor(sha: usize, z: &[u8], olen: usize, k: &mut [u8]) { + let hlen = sha; + let mut j = 0; + + let mut cthreshold = olen / hlen; + if olen % hlen != 0 { + cthreshold += 1 + } + for counter in 0..cthreshold { + let mut b: [u8; 64] = [0; 64]; + GPhashit(MC_SHA2, sha, &mut b, 0, 0, Some(z), counter as isize, None); + + if j + hlen > olen { + for i in 0..(olen % hlen) { + k[j] ^= b[i]; + j += 1 + } + } else { + for i in 0..hlen { + k[j] ^= b[i]; + j += 1 + } + } + } +} + +// PKCS 1.5 +/* SHAXXX identifier strings */ +const SHA256ID: [u8; 19] = [ + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, + 0x00, 0x04, 0x20, +]; +const SHA384ID: [u8; 19] = [ + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, + 0x00, 0x04, 0x30, +]; +const SHA512ID: [u8; 19] = [ + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, + 0x00, 0x04, 0x40, +]; + +pub fn pkcs15(sha: usize, m: &[u8], w: &mut [u8], rfs: usize) -> bool { + let olen = rfs; + let hlen = sha; + let idlen = 19; + let mut b: [u8; 64] = [0; 64]; /* Not good */ + + if olen < idlen + hlen + 10 { + return false; + } + SPhashit(MC_SHA2, sha, &mut b, Some(m)); + + for i in 0..w.len() { + w[i] = 0 + } + let mut i = 0; + w[i] = 0; + i += 1; + w[i] = 1; + i += 1; + for _ in 0..olen - idlen - hlen - 3 { + w[i] = 0xff; + i += 1 + } + w[i] = 0; + i += 1; + if hlen == SHA256 { + for j in 0..idlen { + w[i] = SHA256ID[j]; + i += 1 + } + } + if hlen == SHA384 { + for j in 0..idlen { + w[i] = SHA384ID[j]; + i += 1 + } + } + if hlen == SHA512 { + for j in 0..idlen { + w[i] = SHA512ID[j]; + i += 1 + } + } + for j in 0..hlen { + w[i] = b[j]; + i += 1 + } + true +} + +// Alternate PKCS 1.5 +/* SHAXXX identifier strings */ +const SHA256IDB: [u8; 17] = [ + 0x30, 0x2f, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x04, + 0x20, +]; +const SHA384IDB: [u8; 17] = [ + 0x30, 0x3f, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x04, + 0x30, +]; +const SHA512IDB: [u8; 17] = [ + 0x30, 0x4f, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x04, + 0x40, +]; + +pub fn pkcs15b(sha: usize, m: &[u8], w: &mut [u8], rfs: usize) -> bool { + let olen = rfs; + let hlen = sha; + let idlen = 17; + let mut b: [u8; 64] = [0; 64]; /* Not good */ + + if olen < idlen + hlen + 10 { + return false; + } + SPhashit(MC_SHA2, sha, &mut b, Some(m)); + for i in 0..w.len() { + w[i] = 0 + } + let mut i = 0; + w[i] = 0; + i += 1; + w[i] = 1; + i += 1; + for _ in 0..olen - idlen - hlen - 3 { + w[i] = 0xff; + i += 1 + } + w[i] = 0; + i += 1; + if hlen == SHA256 { + for j in 0..idlen { + w[i] = SHA256IDB[j]; + i += 1 + } + } + if hlen == SHA384 { + for j in 0..idlen { + w[i] = SHA384IDB[j]; + i += 1 + } + } + if hlen == SHA512 { + for j in 0..idlen { + w[i] = SHA512IDB[j]; + i += 1 + } + } + for j in 0..hlen { + w[i] = b[j]; + i += 1 + } + true +} + +pub fn pss_encode(sha: usize, m: &[u8], rng: &mut RAND, f: &mut [u8], rfs: usize) -> bool { + let emlen = rfs; + let embits = 8 * emlen - 1; + let hlen = sha; + let mut h: [u8; 64] = [0; 64]; + let mut salt: [u8; 64] = [0; 64]; + let mut md: [u8; 136] = [0; 136]; + for i in 0..hlen { + salt[i] = rng.getbyte() + } + let mask = (0xff as u8) >> (8 * emlen - embits); + SPhashit(MC_SHA2, sha, &mut h, Some(m)); + if emlen < hlen + hlen + 2 { + return false; + } + for i in 0..8 { + md[i] = 0; + } + for i in 0..hlen { + md[8 + i] = h[i]; + } + for i in 0..hlen { + md[8 + hlen + i] = salt[i]; + } + SPhashit(MC_SHA2, sha, &mut h, Some(&md[0..8 + hlen + hlen])); + for i in 0..emlen - hlen - hlen - 2 { + f[i] = 0; + } + f[emlen - hlen - hlen - 2] = 0x01; + for i in 0..hlen { + f[emlen + i - hlen - hlen - 1] = salt[i]; + } + mgf1xor(sha, &h[0..hlen], emlen - hlen - 1, f); + f[0] &= mask; + for i in 0..hlen { + f[emlen + i - hlen - 1] = h[i]; + } + f[emlen - 1] = 0xbc as u8; + true +} + +pub fn pss_verify(sha: usize, m: &[u8], f: &[u8]) -> bool { + let emlen = f.len(); + let embits = 8 * emlen - 1; + let hlen = sha; + let mut db: [u8; 512] = [0; 512]; + let mut hmask: [u8; 64] = [0; 64]; + let mut h: [u8; 64] = [0; 64]; + let mut salt: [u8; 64] = [0; 64]; + let mut md: [u8; 136] = [0; 136]; + let mask = (0xff as u8) >> (8 * emlen - embits); + + SPhashit(MC_SHA2, sha, &mut hmask, Some(m)); + if emlen < hlen + hlen + 2 { + return false; + } + if f[emlen - 1] != 0xbc as u8 { + return false; + } + if (f[0] & (!mask)) != 0 { + return false; + } + for i in 0..emlen - hlen - 1 { + db[i] = f[i] + } + for i in 0..hlen { + h[i] = f[emlen + i - hlen - 1] + } + mgf1xor(sha, &h[0..hlen], emlen - hlen - 1, &mut db); + db[0] &= mask; + + let mut k = 0 as u8; + for i in 0..emlen - hlen - hlen - 2 { + k |= db[i] + } + if k != 0 { + return false; + } + if db[emlen - hlen - hlen - 2] != 0x01 { + return false; + } + for i in 0..hlen { + salt[i] = db[emlen + i - hlen - hlen - 1] + } + for i in 0..8 { + md[i] = 0 + } + for i in 0..hlen { + md[8 + i] = hmask[i] + } + for i in 0..hlen { + md[8 + hlen + i] = salt[i] + } + SPhashit(MC_SHA2, sha, &mut hmask, Some(&md[0..8 + hlen + hlen])); + k = 0; + for i in 0..hlen { + k |= h[i] - hmask[i]; + } + if k != 0 { + return false; + } + true +} + +/* OAEP Message Encoding for Encryption */ +pub fn oaep_encode( + sha: usize, + m: &[u8], + rng: &mut RAND, + p: Option<&[u8]>, + f: &mut [u8], + rfs: usize, +) -> bool { + let olen = rfs - 1; + let mlen = m.len(); + + let hlen = sha; + + let mut seed: [u8; 64] = [0; 64]; + + let seedlen = hlen; + if mlen > olen - hlen - seedlen - 1 { + return false; + } + + let mut dbmask: [u8; 512] = [0; 512]; + + SPhashit(MC_SHA2, sha, f, p); + //hashit(sha, p, -1, f); + let slen = olen - mlen - hlen - seedlen - 1; + + for i in 0..slen { + f[hlen + i] = 0 + } + f[hlen + slen] = 1; + for i in 0..mlen { + f[hlen + slen + 1 + i] = m[i] + } + + for i in 0..seedlen { + seed[i] = rng.getbyte() + } + + mgf1(sha, &seed[0..seedlen], olen - seedlen, &mut dbmask); + + for i in 0..olen - seedlen { + dbmask[i] ^= f[i] + } + + mgf1(sha, &dbmask[0..olen - seedlen], seedlen, f); + + for i in 0..seedlen { + f[i] ^= seed[i] + } + + for i in 0..olen - seedlen { + f[i + seedlen] = dbmask[i] + } + + /* pad to length rfs */ + let d = 1; + for i in (d..rfs).rev() { + f[i] = f[i - d]; + } + for i in (0..d).rev() { + f[i] = 0; + } + true +} + +/* OAEP Message Decoding for Decryption */ +pub fn oaep_decode(sha: usize, p: Option<&[u8]>, f: &mut [u8], rfs: usize) -> usize { + let olen = rfs - 1; + + let hlen = sha; + let mut seed: [u8; 64] = [0; 64]; + let seedlen = hlen; + let mut chash: [u8; 64] = [0; 64]; + + if olen < seedlen + hlen + 1 { + return 0; + } + let mut dbmask: [u8; 512] = [0; 512]; + + if f.len() < rfs { + let d = rfs - f.len(); + for i in (d..rfs).rev() { + f[i] = f[i - d]; + } + for i in (0..d).rev() { + f[i] = 0; + } + } + SPhashit(MC_SHA2, sha, &mut chash, p); + //hashit(sha, p, -1, &mut chash); + + let x = f[0]; + + for i in seedlen..olen { + dbmask[i - seedlen] = f[i + 1]; + } + + mgf1(sha, &dbmask[0..olen - seedlen], seedlen, &mut seed); + for i in 0..seedlen { + seed[i] ^= f[i + 1] + } + mgf1(sha, &seed[0..seedlen], olen - seedlen, f); + for i in 0..olen - seedlen { + dbmask[i] ^= f[i] + } + + let mut comp = 0; + for i in 0..hlen { + comp |= (chash[i] ^ dbmask[i]) as usize; + } + + for i in 0..olen - seedlen - hlen { + dbmask[i] = dbmask[i + hlen] + } + + for i in 0..hlen { + seed[i] = 0; + chash[i] = 0 + } + + // find first non-zero t in array + let mut k = 0; + let mut t = 0; + let m = olen - seedlen - hlen; + for i in 0..m { + if t == 0 && dbmask[i] != 0 { + k = i; + t = dbmask[i]; + } + } + + if comp != 0 || x != 0 || t != 0x01 { + for i in 0..olen - seedlen { + dbmask[i] = 0 + } + return 0; + } + + for i in 0..m - k - 1 { + f[i] = dbmask[i + k + 1]; + } + + for i in 0..olen - seedlen { + dbmask[i] = 0 + } + + m - k - 1 +} + +/* + +use core::sha3; +use core::hmac; + + + + let mut okm: [u8;100]=[0;100]; + let msg: &[u8] = b"abc"; + let dst: &[u8] = b"P256_XMD:SHA-256_SSWU_RO_TESTGEN"; + hmac::xof_expand(sha3::SHAKE128,&mut okm,48,&dst,&msg); + print!("okm= "); printbinary(&okm[0..48]); + hmac::xmd_expand(hmac::MC_SHA2,32,&mut okm,48,&dst,&msg); + print!("okm= "); printbinary(&okm[0..48]); + + + let mut ikm: [u8;22]=[0;22]; + let mut salt: [u8;13]=[0;13]; + let mut info: [u8;10]=[0;10]; + let mut prk: [u8;32]=[0;32]; + let mut okm: [u8;42]=[0;42]; + + for i in 0..22 {ikm[i]=0x0b;} + for i in 0..13 {salt[i]=i as u8;} + for i in 0..10 {info[i]=(0xf0+i) as u8;} + + hmac::hkdf_extract(hmac::MC_SHA2,32,&mut prk,Some(&salt),&ikm); + + print!("PRK= "); + for i in 0..32 { + print!("{:02X}",prk[i]); + } + + hmac::hkdf_expand(hmac::MC_SHA2,32,&mut okm,42,&prk,&info); + print!("OKM= "); + for i in 0..42 { + print!("{:02X}",okm[i]); + } + + +*/ diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/kyber.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/kyber.rs new file mode 100644 index 000000000000..e7a8ef762170 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/kyber.rs @@ -0,0 +1,728 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Kyber API high-level functions. Constant time where it matters. Slow (spends nearly all of its time running SHA3) but small. + + M.Scott 06/07/2022 +*/ + +use crate::sha3; +use crate::sha3::SHA3; + +const LGN: usize = 8; +const DEGREE: usize = 1 << LGN; +const PRIME: i16 = 0xD01; + +const ONE: i16 = 0x549; // r mod q +const QINV: i32 = -3327; // -1/q mod 2^16 + //const TWO26: i32 = 1<<26; // 2^26 +const TWO25: i32 = 1 << 25; // 2^25 +const BARC: i32 = 20159; // ((TWO26 + PRIME/2)/PRIME) + +pub const SECRET_CPA_SIZE_512: usize = 2 * (DEGREE * 3) / 2; +pub const PUBLIC_SIZE_512: usize = 32 + 2 * (DEGREE * 3) / 2; +pub const CIPHERTEXT_SIZE_512: usize = (10 * 2 + 4) * DEGREE / 8; +pub const SECRET_CCA_SIZE_512: usize = SECRET_CPA_SIZE_512 + PUBLIC_SIZE_512 + 64; +pub const SHARED_SECRET_512: usize = 32; + +pub const SECRET_CPA_SIZE_768: usize = 3 * (DEGREE * 3) / 2; +pub const PUBLIC_SIZE_768: usize = 32 + 3 * (DEGREE * 3) / 2; +pub const CIPHERTEXT_SIZE_768: usize = (10 * 3 + 4) * DEGREE / 8; +pub const SECRET_CCA_SIZE_768: usize = SECRET_CPA_SIZE_768 + PUBLIC_SIZE_768 + 64; +pub const SHARED_SECRET_768: usize = 32; + +pub const SECRET_CPA_SIZE_1024: usize = 4 * (DEGREE * 3) / 2; +pub const PUBLIC_SIZE_1024: usize = 32 + 4 * (DEGREE * 3) / 2; +pub const CIPHERTEXT_SIZE_1024: usize = (11 * 4 + 5) * DEGREE / 8; +pub const SECRET_CCA_SIZE_1024: usize = SECRET_CPA_SIZE_1024 + PUBLIC_SIZE_1024 + 64; +pub const SHARED_SECRET_1024: usize = 32; + +pub const MAXK: usize = 4; + +// parameters for each security level +// K,eta1,eta2,du,dv,shared secret +const PARAMS_512: [usize; 6] = [2, 3, 2, 10, 4, 32]; +const PARAMS_768: [usize; 6] = [3, 2, 2, 10, 4, 32]; +const PARAMS_1024: [usize; 6] = [4, 2, 2, 11, 5, 32]; + +/* Start of public domain reference implementation code - translated from https://github.com/pq-crystals/kyber */ + +const ZETAS: [i16; 128] = [ + -1044, -758, -359, -1517, 1493, 1422, 287, 202, -171, 622, 1577, 182, 962, -1202, -1474, 1468, + 573, -1325, 264, 383, -829, 1458, -1602, -130, -681, 1017, 732, 608, -1542, 411, -205, -1571, + 1223, 652, -552, 1015, -1293, 1491, -282, -1544, 516, -8, -320, -666, -1618, -1162, 126, 1469, + -853, -90, -271, 830, 107, -1421, -247, -951, -398, 961, -1508, -725, 448, -1065, 677, -1275, + -1103, 430, 555, 843, -1251, 871, 1550, 105, 422, 587, 177, -235, -291, -460, 1574, 1653, -246, + 778, 1159, -147, -777, 1483, -602, 1119, -1590, 644, -872, 349, 418, 329, -156, -75, 817, 1097, + 603, 610, 1322, -1285, -1465, 384, -1215, -136, 1218, -1335, -874, 220, -1187, -1659, -1185, + -1530, -1278, 794, -1510, -854, -870, 478, -108, -308, 996, 991, 958, -1460, 1522, 1628, +]; +/* +fn printbinary(array: &[u8]) { + for i in 0..array.len() { + print!("{:02X}", array[i]) + } + println!("") +} +*/ +/* Montgomery stuff */ + +fn montgomery_reduce(a: i32) -> i16 { + let dp = PRIME as i32; + let dt = (((a & 0xffff) * QINV) & 0xffff) as i16; + let t = ((a - ((dt as i32) * dp)) >> 16) as i16; + t +} + +fn barrett_reduce(a: i16) -> i16 { + let da = a as i32; + let mut t = ((BARC * da + TWO25) >> 26) as i16; + t *= PRIME; + a - t +} + +fn fqmul(a: i16, b: i16) -> i16 { + montgomery_reduce((a as i32) * (b as i32)) +} + +fn ntt(r: &mut [i16]) { + let mut k = 1; + let mut len = 128; + while len >= 2 { + let mut start = 0; + while start < 256 { + let zeta = ZETAS[k]; + k += 1; + let mut j = start; + while j < start + len { + let t = fqmul(zeta, r[j + len]); + r[j + len] = r[j] - t; + r[j] += t; + j += 1; + } + start = j + len + } + len >>= 1; + } +} + +fn invntt(r: &mut [i16]) { + let f = 1441 as i16; + let mut k = 127; + let mut len = 2; + while len <= 128 { + let mut start = 0; + while start < 256 { + let zeta = ZETAS[k]; + k -= 1; + let mut j = start; + while j < start + len { + let t = r[j]; + r[j] = barrett_reduce(t + r[j + len]); // problem here + r[j + len] -= t; + r[j + len] = fqmul(zeta, r[j + len]); + j += 1; + } + start = j + len; + } + len <<= 1; + } + for j in 0..256 { + r[j] = fqmul(r[j], f); + } +} + +fn basemul(r: &mut [i16], a: &[i16], b: &[i16], zeta: i16) { + r[0] = fqmul(a[1], b[1]); + r[0] = fqmul(r[0], zeta); + r[0] += fqmul(a[0], b[0]); + r[1] = fqmul(a[0], b[1]); + r[1] += fqmul(a[1], b[0]); +} + +fn poly_reduce(r: &mut [i16]) { + for i in 0..DEGREE { + r[i] = barrett_reduce(r[i]); + } +} + +fn poly_ntt(r: &mut [i16]) { + ntt(r); + poly_reduce(r); +} + +fn poly_invntt(r: &mut [i16]) { + invntt(r); +} + +// Note r must be distinct from a and b +fn poly_mul(r: &mut [i16], a: &[i16], b: &[i16]) { + for i in 0..DEGREE / 4 { + let x = 4 * i; + let y = x + 2; + let z = x + 4; + basemul(&mut r[x..y], &a[x..y], &b[x..y], ZETAS[64 + i]); + basemul(&mut r[y..z], &a[y..z], &b[y..z], -ZETAS[64 + i]); + } +} + +fn poly_tomont(r: &mut [i16]) { + for i in 0..DEGREE { + r[i] = montgomery_reduce((r[i] as i32) * (ONE as i32)); + } +} + +/* End of public domain reference code use */ + +fn poly_add(p1: &mut [i16], p2: &[i16], p3: &[i16]) { + for i in 0..DEGREE { + p1[i] = p2[i] + p3[i]; + } +} + +fn poly_acc(p1: &mut [i16], p3: &[i16]) { + for i in 0..DEGREE { + p1[i] += p3[i]; + } +} + +fn poly_dec(p1: &mut [i16], p3: &[i16]) { + for i in 0..DEGREE { + p1[i] -= p3[i]; + } +} + +// Generate a[i][j] from rho +fn expandaij(rho: &[u8], aij: &mut [i16], i: usize, j: usize) { + let mut buff: [u8; 3 * DEGREE] = [0; 3 * DEGREE]; + let mut sh = SHA3::new(sha3::SHAKE128); + for m in 0..32 { + sh.process(rho[m]) + } + sh.process(j as u8); + sh.process(i as u8); + sh.shake(&mut buff, 3 * DEGREE); + let mut m = 0; + let mut n = 0; + let dp = PRIME as u32; + while n < DEGREE { + let d1 = (buff[m] as u32) + 256 * ((buff[m + 1] & 0x0f) as u32); + let d2 = ((buff[m + 1] / 16) as u32) + 16 * (buff[m + 2] as u32); + if d1 < dp { + aij[n] = d1 as i16; + n += 1; + } + if d2 < dp && n < DEGREE { + aij[n] = d2 as i16; + n += 1; + } + m += 3; + } +} + +fn getbit(b: &[u8], n: usize) -> i16 { + let wd = n / 8; + let bt = n % 8; + ((b[wd] >> bt) & 1) as i16 +} + +fn cbd(bts: &[u8], eta: usize, f: &mut [i16]) { + for i in 0..DEGREE { + let mut a = 0 as i16; + let mut b = 0 as i16; + for j in 0..eta { + a += getbit(bts, 2 * i * eta + j); + b += getbit(bts, 2 * i * eta + eta + j); + } + f[i] = a - b; + } +} + +// extract ab bits into word from dense byte stream +fn nextword(ab: usize, t: &[u8], ptr: &mut usize, bts: &mut usize) -> i16 { + let mut r = (t[*ptr] >> (*bts)) as i16; + let mask = ((1 << ab) - 1) as i16; + let mut i = 0; + let mut gotbits = 8 - (*bts); // bits left in current byte + while gotbits < ab { + i += 1; + let w = t[(*ptr) + i] as i16; + r |= w << gotbits; + gotbits += 8; + } + *bts += ab; + while *bts >= 8 { + *bts -= 8; + *ptr += 1; + } + r & mask +} + +fn nextbyte16(ab: usize, t: &[i16], ptr: &mut usize, bts: &mut usize) -> u8 { + let mut left = ab - (*bts); + let mut i = 0; + let mut w = t[*ptr]; + w += (w >> 15) & PRIME; + let mut r = w >> (*bts); + while left < 8 { + i += 1; + w = t[(*ptr) + i]; + w += (w >> 15) & PRIME; + r |= w << left; + left += ab; + } + *bts += 8; + while *bts >= ab { + *bts -= ab; + *ptr += 1; + } + (r & 0xff) as u8 +} + +fn encode(t: &[i16], len: usize, l: usize, pack: &mut [u8]) { + let mut ptr = 0; + let mut bts = 0; + for n in 0..len * (DEGREE * l) / 8 { + pack[n] = nextbyte16(l, t, &mut ptr, &mut bts); + } +} + +// return 0 if encoding is unchanged +fn chk_encode(t: &[i16], len: usize, l: usize, pack: &[u8]) -> u8 { + let mut ptr = 0; + let mut bts = 0; + let mut diff = 0 as u8; + for n in 0..len * (DEGREE * l) / 8 { + let m = nextbyte16(l, t, &mut ptr, &mut bts); + diff |= m ^ pack[n]; + } + diff +} + +fn decode(pack: &[u8], l: usize, t: &mut [i16], len: usize) { + let mut ptr = 0; + let mut bts = 0; + for i in 0..len * DEGREE { + t[i] = nextword(l, pack, &mut ptr, &mut bts); + } +} + +// Bernsteins safe division by 0xD01 +fn safediv(xx: i32) -> i32 { + let mut x = xx; + let mut q = 0 as i32; + + let mut qpart = (((x as i64) * 645083) >> 31) as i32; + x -= qpart * 0xD01; + q += qpart; + + qpart = ((((x as i64) * 645083) >> 31) as i32) + 1; + x -= qpart * 0xD01; + q += qpart + (x >> 31); + + q +} + +fn compress(t: &mut [i16], len: usize, d: usize) { + let twod = (1 << d) as i32; + let dp = PRIME as i32; + for i in 0..len * DEGREE { + t[i] += (t[i] >> 15) & PRIME; + t[i] = (safediv(twod * (t[i] as i32) + dp / 2) & (twod - 1)) as i16; + } +} +fn decompress(t: &mut [i16], len: usize, d: usize) { + let twod1 = (1 << (d - 1)) as i32; + let dp = PRIME as i32; + for i in 0..len * DEGREE { + t[i] = ((dp * (t[i] as i32) + twod1) >> d) as i16; + } +} + +fn cpa_keypair(params: &[usize], tau: &[u8], sk: &mut [u8], pk: &mut [u8]) { + let mut rho: [u8; 32] = [0; 32]; + let mut sigma: [u8; 33] = [0; 33]; + let mut buff: [u8; 256] = [0; 256]; + + let mut r: [i16; DEGREE] = [0; DEGREE]; + let mut w: [i16; DEGREE] = [0; DEGREE]; + let mut aij: [i16; DEGREE] = [0; DEGREE]; + let mut s: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let mut e: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let mut p: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + + let mut sh = SHA3::new(sha3::HASH512); + + let ck = params[0]; + let eta1 = params[1]; + let public_key_size = 32 + ck * (DEGREE * 3) / 2; + + for i in 0..32 { + sh.process(tau[i]); + } + sh.hash(&mut buff); + for i in 0..32 { + rho[i] = buff[i]; + sigma[i] = buff[i + 32]; + } + sigma[32] = 0; + + // create s + for i in 0..ck { + sh = SHA3::new(sha3::SHAKE256); + for j in 0..33 { + sh.process(sigma[j]); + } + sh.shake(&mut buff, 64 * eta1); + cbd(&buff, eta1, &mut s[i * DEGREE..]); + sigma[32] += 1; + } + + // create e + for i in 0..ck { + sh = SHA3::new(sha3::SHAKE256); + for j in 0..33 { + sh.process(sigma[j]); + } + sh.shake(&mut buff, 64 * eta1); + cbd(&buff, eta1, &mut e[i * DEGREE..]); + sigma[32] += 1; + } + + for k in 0..ck { + let row = k * DEGREE; + poly_ntt(&mut s[row..]); + poly_ntt(&mut e[row..]); + } + + for i in 0..ck { + let row = i * DEGREE; + expandaij(&rho, &mut aij, i, 0); + poly_mul(&mut r, &aij, &s); + for j in 1..ck { + expandaij(&rho, &mut aij, i, j); + poly_mul(&mut w, &s[j * DEGREE..], &aij); + poly_acc(&mut r, &w); + } + poly_reduce(&mut r); + poly_tomont(&mut r); + poly_add(&mut p[row..], &r, &e[row..]); + poly_reduce(&mut p[row..]); + } + + encode(&s, ck, 12, sk); + encode(&p, ck, 12, pk); + for i in 0..32 { + pk[public_key_size - 32 + i] = rho[i]; + } +} + +fn cpa_base_encrypt( + params: &[usize], + coins: &[u8], + pk: &[u8], + ss: &[u8], + u: &mut [i16], + v: &mut [i16], +) { + let mut rho: [u8; 32] = [0; 32]; + let mut sigma: [u8; 33] = [0; 33]; + let mut buff: [u8; 256] = [0; 256]; + + let mut r: [i16; DEGREE] = [0; DEGREE]; + let mut w: [i16; DEGREE] = [0; DEGREE]; + let mut aij: [i16; DEGREE] = [0; DEGREE]; + let mut q: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let mut p: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + + let ck = params[0]; + let eta1 = params[1]; + let eta2 = params[2]; + let du = params[3]; + let dv = params[4]; + let public_key_size = 32 + ck * (DEGREE * 3) / 2; + + for i in 0..32 { + sigma[i] = coins[i]; + } + sigma[32] = 0; + for i in 0..32 { + rho[i] = pk[i + public_key_size - 32]; + } + // create q + for i in 0..ck { + let mut sh = SHA3::new(sha3::SHAKE256); + for j in 0..33 { + sh.process(sigma[j]); + } + sh.shake(&mut buff, 64 * eta1); + cbd(&buff, eta1, &mut q[i * DEGREE..]); + sigma[32] += 1; + } + // create e1 + for i in 0..ck { + let mut sh = SHA3::new(sha3::SHAKE256); + for j in 0..33 { + sh.process(sigma[j]); + } + sh.shake(&mut buff, 64 * eta2); + cbd(&buff, eta1, &mut u[i * DEGREE..]); + sigma[32] += 1; + } + for i in 0..ck { + let row = DEGREE * i; + poly_ntt(&mut q[row..]); + } + + for i in 0..ck { + let row = i * DEGREE; + expandaij(&rho, &mut aij, 0, i); + poly_mul(&mut r, &aij, &q); + for j in 1..ck { + expandaij(&rho, &mut aij, j, i); + poly_mul(&mut w, &q[j * DEGREE..], &aij); + poly_acc(&mut r, &w); + } + poly_reduce(&mut r); + poly_invntt(&mut r); + poly_acc(&mut u[row..], &r); + poly_reduce(&mut u[row..]); + } + + decode(&pk, 12, &mut p, ck); + + poly_mul(v, &p, &q); + for i in 1..ck { + let row = DEGREE * i; + poly_mul(&mut r, &p[row..], &q[row..]); + poly_acc(v, &r); + } + poly_invntt(v); + + let mut sh = SHA3::new(sha3::SHAKE256); + for j in 0..33 { + sh.process(sigma[j]); + } + sh.shake(&mut buff, 64 * eta2); + cbd(&buff, eta1, &mut w); // e2 + + poly_acc(v, &w); + + decode(&ss, 1, &mut r, 1); + decompress(&mut r, 1, 1); + poly_acc(v, &r); + poly_reduce(v); + compress(u, ck, du); + compress(v, 1, dv); +} + +fn cpa_encrypt(params: &[usize], coins: &[u8], pk: &[u8], ss: &[u8], ct: &mut [u8]) { + let mut v: [i16; DEGREE] = [0; DEGREE]; + let mut u: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let ck = params[0]; + let du = params[3]; + let dv = params[4]; + let ciphertext_size = (du * ck + dv) * DEGREE / 8; + cpa_base_encrypt(params, coins, pk, ss, &mut u, &mut v); + encode(&u, ck, du, ct); + encode(&v, 1, dv, &mut ct[ciphertext_size - (dv * DEGREE / 8)..]); +} + +// Re-encrypt and check that ct is OK (if so return is zero) +fn cpa_check_encrypt(params: &[usize], coins: &[u8], pk: &[u8], ss: &[u8], ct: &[u8]) -> u8 { + let mut v: [i16; DEGREE] = [0; DEGREE]; + let mut u: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let ck = params[0]; + let du = params[3]; + let dv = params[4]; + let ciphertext_size = (du * ck + dv) * DEGREE / 8; + cpa_base_encrypt(params, coins, pk, ss, &mut u, &mut v); + let d1 = chk_encode(&u, ck, du, ct); + let d2 = chk_encode(&v, 1, dv, &ct[ciphertext_size - (dv * DEGREE / 8)..]); + if (d1 | d2) == 0 { + 0 + } else { + 0xff + } +} + +fn cpa_decrypt(params: &[usize], sk: &[u8], ct: &[u8], ss: &mut [u8]) { + let mut w: [i16; DEGREE] = [0; DEGREE]; + let mut v: [i16; DEGREE] = [0; DEGREE]; + let mut r: [i16; DEGREE] = [0; DEGREE]; + let mut u: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + let mut s: [i16; MAXK * DEGREE] = [0; MAXK * DEGREE]; + + let ck = params[0]; + let du = params[3]; + let dv = params[4]; + + decode(ct, du, &mut u, ck); + decode(&ct[(du * ck * DEGREE) / 8..], dv, &mut v, 1); + decompress(&mut u, ck, du); + decompress(&mut v, 1, dv); + decode(sk, 12, &mut s, ck); + + poly_ntt(&mut u); + poly_mul(&mut w, &u, &s); + for i in 1..ck { + let row = DEGREE * i; + poly_ntt(&mut u[row..]); + poly_mul(&mut r, &u[row..], &s[row..]); + poly_acc(&mut w, &r); + } + poly_reduce(&mut w); + poly_invntt(&mut w); + poly_dec(&mut v, &w); + compress(&mut v, 1, 1); + encode(&v, 1, 1, ss); +} + +fn cca_keypair(params: &[usize], randbytes64: &[u8], sk: &mut [u8], pk: &mut [u8]) { + let ck = params[0]; + let secret_cpa_key_size = ck * (DEGREE * 3) / 2; + let public_key_size = 32 + ck * (DEGREE * 3) / 2; + + cpa_keypair(params, randbytes64, sk, pk); + for i in 0..public_key_size { + sk[i + secret_cpa_key_size] = pk[i]; + } + let mut sh = SHA3::new(sha3::HASH256); + for i in 0..public_key_size { + sh.process(pk[i]); + } + sh.hash(&mut sk[secret_cpa_key_size + public_key_size..]); + for i in 0..32 { + sk[i + secret_cpa_key_size + public_key_size + 32] = randbytes64[i + 32]; + } +} + +fn cca_encrypt(params: &[usize], randbytes32: &[u8], pk: &[u8], ss: &mut [u8], ct: &mut [u8]) { + let mut hm: [u8; 32] = [0; 32]; + let mut h: [u8; 32] = [0; 32]; + let mut g: [u8; 64] = [0; 64]; + let ck = params[0]; + let du = params[3]; + let dv = params[4]; + let public_key_size = 32 + ck * (DEGREE * 3) / 2; + let ciphertext_size = (du * ck + dv) * DEGREE / 8; + let shared_secret_size = params[5]; + + let mut sh = SHA3::new(sha3::HASH256); + for i in 0..32 { + sh.process(randbytes32[i]); + } + sh.hash(&mut hm); + + sh = SHA3::new(sha3::HASH256); + for i in 0..public_key_size { + sh.process(pk[i]); + } + sh.hash(&mut h); + + sh = SHA3::new(sha3::HASH512); + sh.process_array(&hm); + sh.process_array(&h); + sh.hash(&mut g); + cpa_encrypt(params, &g[32..], &pk, &hm, ct); + + sh = SHA3::new(sha3::HASH256); + for i in 0..ciphertext_size { + sh.process(ct[i]); + } + sh.hash(&mut h); + sh = SHA3::new(sha3::SHAKE256); + sh.process_array(&g[0..32]); + sh.process_array(&h); + sh.shake(ss, shared_secret_size); +} + +fn cca_decrypt(params: &[usize], sk: &[u8], ct: &[u8], ss: &mut [u8]) { + let mut m: [u8; 32] = [0; 32]; + let mut g: [u8; 64] = [0; 64]; + let ck = params[0]; + let secret_cpa_key_size = ck * (DEGREE * 3) / 2; + let public_key_size = 32 + ck * (DEGREE * 3) / 2; + let shared_secret_size = params[5]; + + let pk = &sk[secret_cpa_key_size..secret_cpa_key_size + public_key_size]; + let h = &sk[secret_cpa_key_size + public_key_size..secret_cpa_key_size + public_key_size + 32]; + let z = + &sk[secret_cpa_key_size + public_key_size + 32..secret_cpa_key_size + public_key_size + 64]; + + cpa_decrypt(params, sk, ct, &mut m); + + let mut sh = SHA3::new(sha3::HASH512); + sh.process_array(&m); + sh.process_array(h); + sh.hash(&mut g); + + let mask = cpa_check_encrypt(params, &g[32..], pk, &m, ct); // FO check ct is correct + + for i in 0..32 { + g[i] ^= (g[i] ^ z[i]) & mask; + } + + sh = SHA3::new(sha3::HASH256); + sh.process_array(&ct); + sh.hash(&mut m); + + sh = SHA3::new(sha3::SHAKE256); + sh.process_array(&g[0..32]); + sh.process_array(&m); + sh.shake(ss, shared_secret_size); +} + +// ********************* Kyber API ****************************** + +pub fn keypair_512(randbytes64: &[u8], sk: &mut [u8], pk: &mut [u8]) { + cca_keypair(&PARAMS_512, randbytes64, sk, pk); +} + +pub fn keypair_768(randbytes64: &[u8], sk: &mut [u8], pk: &mut [u8]) { + cca_keypair(&PARAMS_768, randbytes64, sk, pk); +} + +pub fn keypair_1024(randbytes64: &[u8], sk: &mut [u8], pk: &mut [u8]) { + cca_keypair(&PARAMS_1024, randbytes64, sk, pk); +} + +pub fn encrypt_512(randbytes32: &[u8], pk: &[u8], ss: &mut [u8], ct: &mut [u8]) { + cca_encrypt(&PARAMS_512, randbytes32, pk, ss, ct); +} + +pub fn encrypt_768(randbytes32: &[u8], pk: &[u8], ss: &mut [u8], ct: &mut [u8]) { + cca_encrypt(&PARAMS_768, randbytes32, pk, ss, ct); +} + +pub fn encrypt_1024(randbytes32: &[u8], pk: &[u8], ss: &mut [u8], ct: &mut [u8]) { + cca_encrypt(&PARAMS_1024, randbytes32, pk, ss, ct); +} + +pub fn decrypt_512(sk: &[u8], ct: &[u8], ss: &mut [u8]) { + cca_decrypt(&PARAMS_512, sk, ct, ss); +} + +pub fn decrypt_768(sk: &[u8], ct: &[u8], ss: &mut [u8]) { + cca_decrypt(&PARAMS_768, sk, ct, ss); +} + +pub fn decrypt_1024(sk: &[u8], ct: &[u8], ss: &mut [u8]) { + cca_decrypt(&PARAMS_1024, sk, ct, ss); +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/lib.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/lib.rs new file mode 100644 index 000000000000..e1b2139f8ad8 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/lib.rs @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// comment out if debugging with print macros !!! +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::many_single_char_names)] +#![allow(clippy::needless_range_loop)] +#![allow(clippy::manual_memcpy)] +#![allow(clippy::new_without_default)] +pub mod aes; +pub mod arch; +pub mod dilithium; +pub mod gcm; +pub mod hash256; +pub mod hash384; +pub mod hash512; +pub mod hmac; +pub mod kyber; +pub mod nhs; +pub mod rand; +pub mod sha3; +pub mod share; +pub mod x509; +pub mod bn254; diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/main.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/main.rs new file mode 100644 index 000000000000..e7a11a969c03 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/nhs.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/nhs.rs new file mode 100644 index 000000000000..8fc70fb1c3d7 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/nhs.rs @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NewHope Simple API high-level functions */ + +use crate::rand::RAND; +use crate::sha3; +use crate::sha3::SHA3; + +const PRIME: i32 = 0x3001; // q in Hex +const LGN: usize = 10; // Degree n=2^LGN +const ND: u32 = 0xF7002FFF; // 1/(R-q) mod R +const ONE: i32 = 0x2AC8; // R mod q +const R2MODP: u64 = 0x1620; // R^2 mod q + +const DEGREE: usize = 1 << LGN; +const WL: usize = 32; + +const INV: i32 = 0xeab; +const INVPR: i32 = 0x2c2a; + +const ROOTS: [i32; 1024] = [ + 0x2ac8, 0x2baf, 0x299b, 0x685, 0x2f04, 0x158d, 0x2d49, 0x24b5, 0x1edc, 0xab3, 0x2a95, 0x24d, + 0x3cb, 0x6a8, 0x12f9, 0x15ba, 0x1861, 0x2a89, 0x1c5c, 0xbe6, 0xc1e, 0x2024, 0x207, 0x19ce, + 0x2710, 0x1744, 0x18bc, 0x2cd7, 0x396, 0x18d5, 0x1c45, 0xc4, 0x21a6, 0xe03, 0x2b3c, 0x2d91, + 0xc5d, 0x432, 0x1fbc, 0xcae, 0x2512, 0x2979, 0x3b2, 0x714, 0xb2e, 0x1a97, 0x1a03, 0x1bcd, + 0x2216, 0x2701, 0xa, 0x263c, 0x1179, 0x200c, 0x2d08, 0x1c34, 0x291, 0x2c99, 0x2a5a, 0x723, + 0xb1d, 0x1ccc, 0x1fb6, 0x2f58, 0x2bfe, 0x1cda, 0x2a0, 0x5f1, 0x2de, 0x1fc7, 0x1ea8, 0x1719, + 0x2fa7, 0x27ec, 0x20ff, 0x12c0, 0x1ac1, 0x2232, 0x2f9b, 0xd3e, 0x2aed, 0x15f0, 0x11e8, 0xed0, + 0x26a, 0x1de5, 0xa3f, 0xf43, 0xebf, 0x204e, 0xac7, 0x2d9c, 0x5ea, 0x25d1, 0xb6, 0x49c, 0x995, + 0x2555, 0x26e2, 0x100, 0x1878, 0x5aa, 0x2e10, 0x271c, 0xcb, 0x1b4c, 0x2fb8, 0x25b7, 0x1543, + 0x2c7b, 0x241a, 0x2223, 0x20ca, 0x24ed, 0x137, 0x1b65, 0x1dc2, 0x7c7, 0x2ec3, 0xd0c, 0x1169, + 0x1c7a, 0x1ea1, 0xf89, 0x2199, 0x291d, 0x1088, 0x2046, 0x256d, 0x2bc7, 0x2e9b, 0x41f, 0x1b55, + 0x2b38, 0xd0, 0x2e6a, 0x1755, 0x6bc, 0x2724, 0x3ba, 0x222e, 0x2c5c, 0x2da5, 0x213c, 0x10fe, + 0x169a, 0x1552, 0x5d3, 0x300, 0x1b5d, 0x1342, 0x2004, 0x256f, 0x2039, 0x667, 0x23b5, 0x1123, + 0xdb, 0x2da0, 0xe1e, 0x2f54, 0x2767, 0x154a, 0x40a, 0x11d3, 0x2821, 0xc09, 0x974, 0x694, 0xfbf, + 0x27ba, 0x132, 0x83f, 0x2d06, 0x10e, 0x183f, 0x29ae, 0x28c3, 0x2dc9, 0x1144, 0x2c70, 0x2a4a, + 0xf3c, 0x1e32, 0x1171, 0x1e43, 0xdd4, 0x2ddf, 0x28d2, 0xfac, 0x3c4, 0x2f19, 0x10a6, 0x2f7, + 0xe1d, 0x828, 0x138f, 0x1332, 0xfab, 0xcf6, 0x13f8, 0x24a0, 0x112d, 0x2717, 0x6e7, 0x1044, + 0x36e, 0xfe8, 0x6a, 0xba7, 0x1d69, 0x29ec, 0x23b2, 0xaee, 0x16df, 0x1068, 0x1a7e, 0x253f, + 0x24c, 0xb33, 0x2683, 0x15ce, 0x1ad3, 0x1a36, 0xc96, 0xaea, 0x260a, 0xce, 0x28b1, 0xe4f, + 0x2b11, 0x5f8, 0x1fc4, 0xe77, 0x2366, 0x11f9, 0x153c, 0x24eb, 0x20cd, 0x1398, 0x22, 0x2b97, + 0x249b, 0x8eb, 0x12b2, 0x2fe3, 0x29c1, 0x1b00, 0x2663, 0xeaa, 0x2e06, 0xe0, 0x1569, 0x10f5, + 0x284e, 0xa38, 0x201d, 0x1c53, 0x1681, 0x1f6f, 0x2f95, 0x2fe8, 0xacb, 0x1680, 0x17fd, 0x2c39, + 0x165a, 0x10bb, 0x29d8, 0x2622, 0x1196, 0x884, 0x2a79, 0x140e, 0x2d80, 0x6fa, 0x11b2, 0x26c4, + 0x355, 0x1054, 0x29e9, 0x23ed, 0xbe3, 0x24fa, 0x1fb3, 0x10ac, 0x2919, 0x2584, 0x10a4, 0xe85, + 0x650, 0x1893, 0x1dc1, 0xd8e, 0x12dc, 0x2d42, 0x284d, 0xfff, 0x250f, 0xacd, 0x13c3, 0x6cc, + 0x1a79, 0x1221, 0x2614, 0x270a, 0x1ea, 0x155, 0x2818, 0x222c, 0x2e5b, 0x25d8, 0x1dbf, 0x191c, + 0xb0f, 0xdac, 0x1082, 0x12ef, 0x11b6, 0xfa8, 0x2b72, 0x159d, 0x209e, 0x31b, 0x2c7c, 0x14f7, + 0xe09, 0x1bb2, 0x1ec7, 0x2404, 0x20ae, 0x6ad, 0xed6, 0x2b70, 0x1c7b, 0x18d1, 0x2732, 0x12da, + 0xd56, 0x5c1, 0x1648, 0x18b7, 0x1605, 0x1bc4, 0x280, 0x2ece, 0xc, 0x1aae, 0x1c4, 0x1cdb, + 0x22d6, 0x21d8, 0x257c, 0x51f, 0x211b, 0xff, 0x2ee0, 0x2585, 0xe1, 0x2c35, 0x26db, 0x2971, + 0x2208, 0x17e1, 0x21be, 0x135e, 0x28d6, 0x2891, 0x1689, 0x2138, 0xb86, 0x2e3a, 0x1204, 0x2d10, + 0x2324, 0xf3f, 0x2508, 0x33d, 0xcb2, 0x292a, 0xe27, 0x2e64, 0x29f8, 0x2d46, 0x9b7, 0x20eb, + 0x1b7c, 0x9eb, 0x2b2a, 0x58c, 0x27d0, 0x121b, 0x272e, 0x29f6, 0x2dbd, 0x2697, 0x2aac, 0xd6f, + 0x1c67, 0x2c5b, 0x108d, 0x363, 0x249d, 0x2d5e, 0x2fd, 0x2cb2, 0x1f8f, 0x20a4, 0xa19, 0x2ac9, + 0x19b1, 0x1581, 0x17a2, 0x29eb, 0x1b72, 0x13b0, 0xee4, 0xa8f, 0x2315, 0x5e6, 0x951, 0x2e29, + 0xdad, 0x1f2b, 0x224e, 0x37f, 0x1a72, 0xa91, 0x1407, 0x2df9, 0x3ad, 0x23f7, 0x1a24, 0x1d2a, + 0x234b, 0x1df3, 0x1143, 0x7ff, 0x1a6d, 0x2774, 0x2690, 0x2ab5, 0x586, 0x2781, 0x2009, 0x2fdd, + 0x2881, 0x399, 0x2fb6, 0x144, 0x137f, 0xfa0, 0x2e4c, 0x1c7f, 0x2fac, 0xb09, 0x1264, 0x127b, + 0x198c, 0x2b40, 0x230, 0x1cf4, 0x180b, 0xb58, 0x144a, 0x2aec, 0xfb, 0x2602, 0x14ee, 0x783, + 0x1098, 0x23d8, 0x203, 0xe9, 0x108a, 0x14b8, 0xeec, 0xc58, 0x1248, 0x243c, 0x28aa, 0x6bf, + 0x27c4, 0x276e, 0x19b8, 0x1d11, 0x2e16, 0x472, 0x1464, 0x24b9, 0x662, 0x1097, 0x2067, 0x20d6, + 0x171c, 0x4, 0x682, 0x17bb, 0x1186, 0x4f2, 0x3ff, 0x2a43, 0x1dc7, 0x1ae5, 0x8cc, 0x2e7c, + 0x2ef8, 0x2ae0, 0x2904, 0xed4, 0x6c5, 0x14ae, 0xb72, 0x11c3, 0x337, 0x2da3, 0x2916, 0x6d8, + 0x1cf9, 0x10ee, 0x1800, 0x1ae4, 0xa0d, 0x101b, 0x1a8d, 0x2e98, 0x24cd, 0x813, 0x1aa4, 0x9b9, + 0x680, 0x2349, 0x24d1, 0x20f8, 0xe31, 0x249f, 0x216b, 0x12d9, 0x1d21, 0x19db, 0x191a, 0x1dd0, + 0x5df, 0x55c, 0x2b86, 0x213, 0xe9e, 0x1ef1, 0x268a, 0x1d5e, 0x1e20, 0x28c1, 0x1379, 0x249, + 0x19de, 0x18b, 0x1e41, 0x2a1e, 0x2612, 0x297, 0x2e96, 0x2102, 0x46, 0x1b9f, 0x1a4d, 0x2050, + 0x1b32, 0x568, 0x11f7, 0x1829, 0x870, 0x1f4, 0x1dca, 0x990, 0x1df6, 0x2b62, 0x13ec, 0x9f2, + 0x1260, 0x2997, 0x1412, 0x1e6d, 0x1694, 0x11ac, 0x2d8b, 0x276f, 0x26f5, 0x233e, 0x2b44, 0x2f5a, + 0x2d37, 0x2cb1, 0xc75, 0x98d, 0x1d56, 0x7ae, 0x10e6, 0x113f, 0x17b8, 0xad3, 0x737, 0x221e, + 0x1b70, 0x1f3e, 0x2966, 0x18b2, 0x4fa, 0x2044, 0x1312, 0x154e, 0x2029, 0x700, 0x1b45, 0x27a6, + 0x226a, 0x21bf, 0x58d, 0x2f11, 0x2e02, 0x17fc, 0x4d2, 0x1757, 0xcb1, 0x2ef1, 0x2582, 0x1276, + 0x881, 0x2fc0, 0x104a, 0x670, 0x274f, 0x2b53, 0x19dd, 0x752, 0x1663, 0xcbd, 0x2b2b, 0x2fc6, + 0x13b6, 0x21e6, 0x15f6, 0x126b, 0x2637, 0x1cd9, 0x2f50, 0xe82, 0x5b0, 0x24e0, 0x1350, 0x2f24, + 0x21f7, 0x1a16, 0x2f3e, 0x167e, 0x1f7d, 0x28a0, 0x16f0, 0xe33, 0x53b, 0x28c5, 0x1500, 0x2f88, + 0x26cc, 0x2018, 0x1604, 0x218b, 0x2cd1, 0x9ee, 0x17f3, 0x5fd, 0x1f5a, 0x2d0, 0x2b46, 0x23cc, + 0x503, 0x1c46, 0x1cc3, 0x28e2, 0x243e, 0x122b, 0x2e0c, 0xe37, 0x2611, 0x85e, 0x9b8, 0x1b24, + 0x762, 0x19b6, 0x3bc, 0x2d50, 0x2079, 0x18da, 0x170a, 0x800, 0xaa2, 0x135a, 0x1a15, 0x13d1, + 0xca, 0x2113, 0x2db9, 0xdb2, 0x1a5c, 0x29a9, 0x1488, 0x14c1, 0x2c9, 0x917, 0x28e7, 0x265c, + 0xdab, 0x2ab9, 0x2bc6, 0x105b, 0x1839, 0x219c, 0x50, 0x11da, 0x1802, 0xf56, 0x2e6, 0x2190, + 0xddb, 0x56e, 0x9d9, 0x1c81, 0x1016, 0x12d6, 0x296f, 0x14b4, 0x1014, 0x1e64, 0x1d90, 0x89f, + 0x2bc2, 0x2777, 0x2819, 0x1c65, 0x1a41, 0x5a2, 0x2cd2, 0x427, 0xd71, 0x29c8, 0x1e58, 0x53f, + 0x7c5, 0x1dcd, 0x4a1, 0x1268, 0x2597, 0x2926, 0xee, 0x111b, 0x1038, 0xe6c, 0x22dc, 0x2f2f, + 0x441, 0x2cfd, 0x1cb0, 0x6a4, 0x2224, 0x620, 0x5dc, 0x16b1, 0x2a1d, 0x1787, 0x20c7, 0x641, + 0xd84, 0x1c05, 0x2d0d, 0x2f52, 0x1b8c, 0xd7d, 0x17e8, 0x1589, 0xc73, 0x151b, 0x4e2, 0x1ae9, + 0x1b18, 0xb9b, 0x949, 0x2c60, 0x1e7a, 0xd5, 0x1bdc, 0x1f57, 0x1753, 0x124a, 0x559, 0xb76, + 0x2334, 0x12d1, 0x1de1, 0x14b2, 0x2faa, 0x1697, 0x147a, 0x5a1, 0x2c30, 0x1c02, 0x1043, 0x2ee1, + 0x2402, 0x1cc8, 0x2a16, 0xff7, 0x1364, 0x1b9a, 0x2a53, 0x2f94, 0x294c, 0x1ee5, 0x1a87, 0x2141, + 0xd66, 0x953, 0x28a3, 0x2f30, 0x2477, 0x18e3, 0x1035, 0x1fc1, 0x1d68, 0x2fb3, 0x138c, 0x2487, + 0x1bf8, 0xd96, 0x1018, 0x748, 0x244e, 0x15bd, 0x175e, 0x2be, 0x23d, 0x1da, 0x176d, 0xc17, + 0x24be, 0x2ebb, 0x7d8, 0x100a, 0x759, 0x1db4, 0x2259, 0x23f4, 0x2d59, 0x2847, 0xbf5, 0x1cfe, + 0xa20, 0x258, 0x1180, 0x279c, 0x54, 0x2abf, 0xc5c, 0x9f9, 0x3d5, 0x2ce4, 0x165f, 0x23d9, + 0x27b9, 0x6f9, 0x281a, 0x169e, 0x627, 0x156d, 0x1ff8, 0x211, 0x2e34, 0x1724, 0x2c2e, 0x2790, + 0x2dd5, 0x2bf2, 0xdbc, 0x2884, 0x20a9, 0x2390, 0x1e1a, 0x1b6a, 0x5f7, 0xab7, 0x1333, 0x16ab, + 0x28dd, 0x20, 0x30f, 0x24b6, 0x5c2, 0x1ce4, 0x1400, 0x2669, 0x60, 0x156c, 0xe20, 0x26d4, + 0x26ab, 0x1ebb, 0x223d, 0x5b4, 0x2025, 0x1e1c, 0xaae, 0x2e08, 0x6cd, 0x1677, 0x13d9, 0x17b5, + 0x1046, 0x1d8c, 0x14eb, 0x18d8, 0x1ce5, 0x2478, 0x16ae, 0xb79, 0x23d4, 0x684, 0x156b, 0x567, + 0x1a, 0x29ce, 0x83a, 0x19e8, 0x58e, 0x294a, 0x1136, 0x2319, 0x2fba, 0x1a29, 0x1d, 0x1879, + 0x291b, 0x19f6, 0x2c2f, 0x21c9, 0x19bb, 0xbbc, 0x26f9, 0xc22, 0x708, 0x11a1, 0x18d3, 0x7f8, + 0x28f8, 0x2427, 0x1deb, 0xaed, 0x26aa, 0x2482, 0x203b, 0x2f05, 0x2b82, 0x192f, 0x2df4, 0x8dc, + 0x2877, 0xd5e, 0x240e, 0x775, 0x2dae, 0x1d3e, 0x20ba, 0x215b, 0x22d1, 0xeba, 0xf50, 0xaa8, + 0x184a, 0x1f67, 0x2e04, 0xc6e, 0x6dd, 0x1a09, 0x27f, 0x494, 0x1426, 0xae3, 0xe15, 0x65f, + 0x13c4, 0x105, 0x872, 0x2667, 0x1ff6, 0xd9f, 0x2ca1, 0x2f39, 0x2657, 0x23fd, 0x2405, 0xb73, + 0x2294, 0x1f1e, 0x2eba, 0x110a, 0x2cae, 0x141f, 0x22cd, 0x25d6, 0x11c1, 0x1c, 0x2d8e, 0x161a, + 0x1aa8, 0x229e, 0x1bf9, 0x7cf, 0x106d, 0x2c40, 0xd93, 0x255e, 0x28c2, 0xc1a, 0x2f17, 0x7ca, + 0x2f63, 0xbf, +]; +const IROOTS: [i32; 1024] = [ + 0x2ac8, 0x452, 0x297c, 0x666, 0xb4c, 0x2b8, 0x1a74, 0xfd, 0x1a47, 0x1d08, 0x2959, 0x2c36, + 0x2db4, 0x56c, 0x254e, 0x1125, 0x2f3d, 0x13bc, 0x172c, 0x2c6b, 0x32a, 0x1745, 0x18bd, 0x8f1, + 0x1633, 0x2dfa, 0xfdd, 0x23e3, 0x241b, 0x13a5, 0x578, 0x17a0, 0xa9, 0x104b, 0x1335, 0x24e4, + 0x28de, 0x5a7, 0x368, 0x2d70, 0x13cd, 0x2f9, 0xff5, 0x1e88, 0x9c5, 0x2ff7, 0x900, 0xdeb, + 0x1434, 0x15fe, 0x156a, 0x24d3, 0x28ed, 0x2c4f, 0x688, 0xaef, 0x2353, 0x1045, 0x2bcf, 0x23a4, + 0x270, 0x4c5, 0x21fe, 0xe5b, 0xfbb, 0x1f79, 0x6e4, 0xe68, 0x2078, 0x1160, 0x1387, 0x1e98, + 0x22f5, 0x13e, 0x283a, 0x123f, 0x149c, 0x2eca, 0xb14, 0xf37, 0xdde, 0xbe7, 0x386, 0x1abe, + 0xa4a, 0x49, 0x14b5, 0x2f36, 0x8e5, 0x1f1, 0x2a57, 0x1789, 0x2f01, 0x91f, 0xaac, 0x266c, + 0x2b65, 0x2f4b, 0xa30, 0x2a17, 0x265, 0x253a, 0xfb3, 0x2142, 0x20be, 0x25c2, 0x121c, 0x2d97, + 0x2131, 0x1e19, 0x1a11, 0x514, 0x22c3, 0x66, 0xdcf, 0x1540, 0x1d41, 0xf02, 0x815, 0x5a, 0x18e8, + 0x1159, 0x103a, 0x2d23, 0x2a10, 0x2d61, 0x1327, 0x403, 0x25c9, 0x7b3, 0x1f0c, 0x1a98, 0x2f21, + 0x1fb, 0x2157, 0x99e, 0x1501, 0x640, 0x1e, 0x1d4f, 0x2716, 0xb66, 0x46a, 0x2fdf, 0x1c69, 0xf34, + 0xb16, 0x1ac5, 0x1e08, 0xc9b, 0x218a, 0x103d, 0x2a09, 0x4f0, 0x21b2, 0x750, 0x2f33, 0x9f7, + 0x2517, 0x236b, 0x15cb, 0x152e, 0x1a33, 0x97e, 0x24ce, 0x2db5, 0xac2, 0x1583, 0x1f99, 0x1922, + 0x2513, 0xc4f, 0x615, 0x1298, 0x245a, 0x2f97, 0x2019, 0x2c93, 0x1fbd, 0x291a, 0x8ea, 0x1ed4, + 0xb61, 0x1c09, 0x230b, 0x2056, 0x1ccf, 0x1c72, 0x27d9, 0x21e4, 0x2d0a, 0x1f5b, 0xe8, 0x2c3d, + 0x2055, 0x72f, 0x222, 0x222d, 0x11be, 0x1e90, 0x11cf, 0x20c5, 0x5b7, 0x391, 0x1ebd, 0x238, + 0x73e, 0x653, 0x17c2, 0x2ef3, 0x2fb, 0x27c2, 0x2ecf, 0x847, 0x2042, 0x296d, 0x268d, 0x23f8, + 0x7e0, 0x1e2e, 0x2bf7, 0x1ab7, 0x89a, 0xad, 0x21e3, 0x261, 0x2f26, 0x1ede, 0xc4c, 0x299a, + 0xfc8, 0xa92, 0xffd, 0x1cbf, 0x14a4, 0x2d01, 0x2a2e, 0x1aaf, 0x1967, 0x1f03, 0xec5, 0x25c, + 0x3a5, 0xdd3, 0x2c47, 0x8dd, 0x2945, 0x18ac, 0x197, 0x2f31, 0x4c9, 0x14ac, 0x2be2, 0x166, + 0x43a, 0xa94, 0x1b53, 0x293c, 0x212d, 0x6fd, 0x521, 0x109, 0x185, 0x2735, 0x151c, 0x123a, + 0x5be, 0x2c02, 0x2b0f, 0x1e7b, 0x1846, 0x297f, 0x2ffd, 0x18e5, 0xf2b, 0xf9a, 0x1f6a, 0x299f, + 0xb48, 0x1b9d, 0x2b8f, 0x1eb, 0x12f0, 0x1649, 0x893, 0x83d, 0x2942, 0x757, 0xbc5, 0x1db9, + 0x23a9, 0x2115, 0x1b49, 0x1f77, 0x2f18, 0x2dfe, 0xc29, 0x1f69, 0x287e, 0x1b13, 0x9ff, 0x2f06, + 0x515, 0x1bb7, 0x24a9, 0x17f6, 0x130d, 0x2dd1, 0x4c1, 0x1675, 0x1d86, 0x1d9d, 0x24f8, 0x55, + 0x1382, 0x1b5, 0x2061, 0x1c82, 0x2ebd, 0x4b, 0x2c68, 0x780, 0x24, 0xff8, 0x880, 0x2a7b, 0x54c, + 0x971, 0x88d, 0x1594, 0x2802, 0x1ebe, 0x120e, 0xcb6, 0x12d7, 0x15dd, 0xc0a, 0x2c54, 0x208, + 0x1bfa, 0x2570, 0x158f, 0x2c82, 0xdb3, 0x10d6, 0x2254, 0x1d8, 0x26b0, 0x2a1b, 0xcec, 0x2572, + 0x211d, 0x1c51, 0x148f, 0x616, 0x185f, 0x1a80, 0x1650, 0x538, 0x25e8, 0xf5d, 0x1072, 0x34f, + 0x2d04, 0x2a3, 0xb64, 0x2c9e, 0x1f74, 0x3a6, 0x139a, 0x2292, 0x555, 0x96a, 0x244, 0x60b, 0x8d3, + 0x1de6, 0x831, 0x2a75, 0x4d7, 0x2616, 0x1485, 0xf16, 0x264a, 0x2bb, 0x609, 0x19d, 0x21da, + 0x6d7, 0x234f, 0x2cc4, 0xaf9, 0x20c2, 0xcdd, 0x2f1, 0x1dfd, 0x1c7, 0x247b, 0xec9, 0x1978, + 0x770, 0x72b, 0x1ca3, 0xe43, 0x1820, 0xdf9, 0x690, 0x926, 0x3cc, 0x2f20, 0xa7c, 0x121, 0x2f02, + 0xee6, 0x2ae2, 0xa85, 0xe29, 0xd2b, 0x1326, 0x2e3d, 0x1553, 0x2ff5, 0x133, 0x2d81, 0x143d, + 0x19fc, 0x174a, 0x19b9, 0x2a40, 0x22ab, 0x1d27, 0x8cf, 0x1730, 0x1386, 0x491, 0x212b, 0x2954, + 0xf53, 0xbfd, 0x113a, 0x144f, 0x21f8, 0x1b0a, 0x385, 0x2ce6, 0xf63, 0x1a64, 0x48f, 0x2059, + 0x1e4b, 0x1d12, 0x1f7f, 0x2255, 0x24f2, 0x16e5, 0x1242, 0xa29, 0x1a6, 0xdd5, 0x7e9, 0x2eac, + 0x2e17, 0x8f7, 0x9ed, 0x1de0, 0x1588, 0x2935, 0x1c3e, 0x2534, 0xaf2, 0x2002, 0x7b4, 0x2bf, + 0x1d25, 0x2273, 0x1240, 0x176e, 0x29b1, 0x217c, 0x1f5d, 0xa7d, 0x6e8, 0x1f55, 0x104e, 0xb07, + 0x241e, 0xc14, 0x618, 0x1fad, 0x2cac, 0x93d, 0x1e4f, 0x2907, 0x281, 0x1bf3, 0x588, 0x277d, + 0x1e6b, 0x9df, 0x629, 0x1f46, 0x19a7, 0x3c8, 0x1804, 0x1981, 0x2536, 0x19, 0x6c, 0x1092, + 0x1980, 0x13ae, 0xfe4, 0x2f42, 0x9e, 0x2837, 0xea, 0x23e7, 0x73f, 0xaa3, 0x226e, 0x3c1, 0x1f94, + 0x2832, 0x1408, 0xd63, 0x1559, 0x19e7, 0x273, 0x2fe5, 0x1e40, 0xa2b, 0xd34, 0x1be2, 0x353, + 0x1ef7, 0x147, 0x10e3, 0xd6d, 0x248e, 0xbfc, 0xc04, 0x9aa, 0xc8, 0x360, 0x2262, 0x100b, 0x99a, + 0x278f, 0x2efc, 0x1c3d, 0x29a2, 0x21ec, 0x251e, 0x1bdb, 0x2b6d, 0x2d82, 0x15f8, 0x2924, 0x2393, + 0x1fd, 0x109a, 0x17b7, 0x2559, 0x20b1, 0x2147, 0xd30, 0xea6, 0xf47, 0x12c3, 0x253, 0x288c, + 0xbf3, 0x22a3, 0x78a, 0x2725, 0x20d, 0x16d2, 0x47f, 0xfc, 0xfc6, 0xb7f, 0x957, 0x2514, 0x1216, + 0xbda, 0x709, 0x2809, 0x172e, 0x1e60, 0x28f9, 0x23df, 0x908, 0x2445, 0x1646, 0xe38, 0x3d2, + 0x160b, 0x6e6, 0x1788, 0x2fe4, 0x15d8, 0x47, 0xce8, 0x1ecb, 0x6b7, 0x2a73, 0x1619, 0x27c7, + 0x633, 0x2fe7, 0x2a9a, 0x1a96, 0x297d, 0xc2d, 0x2488, 0x1953, 0xb89, 0x131c, 0x1729, 0x1b16, + 0x1275, 0x1fbb, 0x184c, 0x1c28, 0x198a, 0x2934, 0x1f9, 0x2553, 0x11e5, 0xfdc, 0x2a4d, 0xdc4, + 0x1146, 0x956, 0x92d, 0x21e1, 0x1a95, 0x2fa1, 0x998, 0x1c01, 0x131d, 0x2a3f, 0xb4b, 0x2cf2, + 0x2fe1, 0x724, 0x1956, 0x1cce, 0x254a, 0x2a0a, 0x1497, 0x11e7, 0xc71, 0xf58, 0x77d, 0x2245, + 0x40f, 0x22c, 0x871, 0x3d3, 0x18dd, 0x1cd, 0x2df0, 0x1009, 0x1a94, 0x29da, 0x1963, 0x7e7, + 0x2908, 0x848, 0xc28, 0x19a2, 0x31d, 0x2c2c, 0x2608, 0x23a5, 0x542, 0x2fad, 0x865, 0x1e81, + 0x2da9, 0x25e1, 0x1303, 0x240c, 0x7ba, 0x2a8, 0xc0d, 0xda8, 0x124d, 0x28a8, 0x1ff7, 0x2829, + 0x146, 0xb43, 0x23ea, 0x1894, 0x2e27, 0x2dc4, 0x2d43, 0x18a3, 0x1a44, 0xbb3, 0x28b9, 0x1fe9, + 0x226b, 0x1409, 0xb7a, 0x1c75, 0x4e, 0x1299, 0x1040, 0x1fcc, 0x171e, 0xb8a, 0xd1, 0x75e, + 0x26ae, 0x229b, 0xec0, 0x157a, 0x111c, 0x6b5, 0x6d, 0x5ae, 0x1467, 0x1c9d, 0x200a, 0x5eb, + 0x1339, 0xbff, 0x120, 0x1fbe, 0x13ff, 0x3d1, 0x2a60, 0x1b87, 0x196a, 0x57, 0x1b4f, 0x1220, + 0x1d30, 0xccd, 0x248b, 0x2aa8, 0x1db7, 0x18ae, 0x10aa, 0x1425, 0x2f2c, 0x1187, 0x3a1, 0x26b8, + 0x2466, 0x14e9, 0x1518, 0x2b1f, 0x1ae6, 0x238e, 0x1a78, 0x1819, 0x2284, 0x1475, 0xaf, 0x2f4, + 0x13fc, 0x227d, 0x29c0, 0xf3a, 0x187a, 0x5e4, 0x1950, 0x2a25, 0x29e1, 0xddd, 0x295d, 0x1351, + 0x304, 0x2bc0, 0xd2, 0xd25, 0x2195, 0x1fc9, 0x1ee6, 0x2f13, 0x6db, 0xa6a, 0x1d99, 0x2b60, + 0x1234, 0x283c, 0x2ac2, 0x11a9, 0x639, 0x2290, 0x2bda, 0x32f, 0x2a5f, 0x15c0, 0x139c, 0x7e8, + 0x88a, 0x43f, 0x2762, 0x1271, 0x119d, 0x1fed, 0x1b4d, 0x692, 0x1d2b, 0x1feb, 0x1380, 0x2628, + 0x2a93, 0x2226, 0xe71, 0x2d1b, 0x20ab, 0x17ff, 0x1e27, 0x2fb1, 0xe65, 0x17c8, 0x1fa6, 0x43b, + 0x548, 0x2256, 0x9a5, 0x71a, 0x26ea, 0x2d38, 0x1b40, 0x1b79, 0x658, 0x15a5, 0x224f, 0x248, + 0xeee, 0x2f37, 0x1c30, 0x15ec, 0x1ca7, 0x255f, 0x2801, 0x18f7, 0x1727, 0xf88, 0x2b1, 0x2c45, + 0x164b, 0x289f, 0x14dd, 0x2649, 0x27a3, 0x9f0, 0x21ca, 0x1f5, 0x1dd6, 0xbc3, 0x71f, 0x133e, + 0x13bb, 0x2afe, 0xc35, 0x4bb, 0x2d31, 0x10a7, 0x2a04, 0x180e, 0x2613, 0x330, 0xe76, 0x19fd, + 0xfe9, 0x935, 0x79, 0x1b01, 0x73c, 0x2ac6, 0x21ce, 0x1911, 0x761, 0x1084, 0x1983, 0xc3, 0x15eb, + 0xe0a, 0xdd, 0x1cb1, 0xb21, 0x2a51, 0x217f, 0xb1, 0x1328, 0x9ca, 0x1d96, 0x1a0b, 0xe1b, 0x1c4b, + 0x3b, 0x4d6, 0x2344, 0x199e, 0x28af, 0x1624, 0x4ae, 0x8b2, 0x2991, 0x1fb7, 0x41, 0x2780, + 0x1d8b, 0xa7f, 0x110, 0x2350, 0x18aa, 0x2b2f, 0x1805, 0x1ff, 0xf0, 0x2a74, 0xe42, 0xd97, 0x85b, + 0x14bc, 0x2901, 0xfd8, 0x1ab3, 0x1cef, 0xfbd, 0x2b07, 0x174f, 0x69b, 0x10c3, 0x1491, 0xde3, + 0x28ca, 0x252e, 0x1849, 0x1ec2, 0x1f1b, 0x2853, 0x12ab, 0x2674, 0x238c, 0x350, 0x2ca, 0xa7, + 0x4bd, 0xcc3, 0x90c, 0x892, 0x276, 0x1e55, 0x196d, 0x1194, 0x1bef, 0x66a, 0x1da1, 0x260f, + 0x1c15, 0x49f, 0x120b, 0x2671, 0x1237, 0x2e0d, 0x2791, 0x17d8, 0x1e0a, 0x2a99, 0x14cf, 0xfb1, + 0x15b4, 0x1462, 0x2fbb, 0xeff, 0x16b, 0x2d6a, 0x9ef, 0x5e3, 0x11c0, 0x2e76, 0x1623, 0x2db8, + 0x1c88, 0x740, 0x11e1, 0x12a3, 0x977, 0x1110, 0x2163, 0x2dee, 0x47b, 0x2aa5, 0x2a22, 0x1231, + 0x16e7, 0x1626, 0x12e0, 0x1d28, 0xe96, 0xb62, 0x21d0, 0xf09, 0xb30, 0xcb8, 0x2981, 0x2648, + 0x155d, 0x27ee, 0xb34, 0x169, 0x1574, 0x1fe6, 0x25f4, 0x151d, 0x1801, 0x1f13, 0x1308, 0x2929, + 0x6eb, 0x25e, 0x2cca, 0x1e3e, 0x248f, +]; + +fn round(a: i32, b: i32) -> i32 { + (a + b / 2) / b +} + +/* Constant time absolute value */ +fn nabs(x: i32) -> i32 { + let mask = x >> 31; + (x + mask) ^ mask +} + +/* Montgomery stuff */ + +fn redc(t: u64) -> i32 { + let m = (t as u32).wrapping_mul(ND); + (((m as u64) * (PRIME as u64) + t) >> WL) as i32 +} + +fn nres(x: i32) -> i32 { + redc((x as u64) * R2MODP) +} + +fn modmul(a: i32, b: i32) -> i32 { + redc((a as u64) * (b as u64)) +} + +/* Cooley-Tukey NTT */ +fn ntt(x: &mut [i32]) { + let mut t = DEGREE / 2; + let q = PRIME; + + /* Convert to Montgomery form */ + for j in 0..DEGREE { + x[j] = nres(x[j]) + } + let mut m = 1; + while m < DEGREE { + let mut k = 0; + for i in 0..m { + let s = ROOTS[m + i]; + for j in k..k + t { + let u = x[j]; + let v = modmul(x[j + t], s); + x[j] = u + v; + x[j + t] = u + 2 * q - v; + } + k += 2 * t; + } + t /= 2; + m *= 2; + } +} + +/* Gentleman-Sande INTT */ + +fn intt(x: &mut [i32]) { + let mut t = 1; + let q = PRIME; + let mut m = DEGREE / 2; + while m > 1 { + let mut k = 0; + for i in 0..m { + let s = IROOTS[m + i]; + for j in k..k + t { + let u = x[j]; + let v = x[j + t]; + x[j] = u + v; + let w = u + (DEGREE as i32) * q - v; + x[j + t] = modmul(w, s); + } + k += 2 * t; + } + t *= 2; + m /= 2; + } + + /* Last iteration merged with n^-1 */ + t = DEGREE / 2; + for j in 0..t { + let u = x[j]; + let v = x[j + t]; + let w = u + (DEGREE as i32) * q - v; + x[j + t] = modmul(w, INVPR); + x[j] = modmul(u + v, INV); + } + /* convert back from Montgomery to "normal" form */ + for j in 0..DEGREE { + x[j] = redc(x[j] as u64); + x[j] -= q; + x[j] += (x[j] >> (WL - 1)) & q; + } +} + +/* See https://eprint.iacr.org/2016/1157.pdf */ + +fn encode(key: &[u8], poly: &mut [i32]) { + let q2 = PRIME / 2; + let mut j = 0; + let mut i = 0; + while i < 256 { + let mut kj = key[j]; + j += 1; + for _ in 0..8 { + let b = (kj & 1) as i32; + poly[i] = b * q2; + poly[i + 256] = b * q2; + poly[i + 512] = b * q2; + poly[i + 768] = b * q2; + kj >>= 1; + i += 1; + } + } +} + +fn decode(poly: &[i32], key: &mut [u8]) { + let q2 = PRIME / 2; + for i in 0..32 { + key[i] = 0; + } + + let mut i = 0; + let mut j = 0; + while i < 256 { + for _ in 0..8 { + let t = nabs(poly[i] - q2) + + nabs(poly[i + 256] - q2) + + nabs(poly[i + 512] - q2) + + nabs(poly[i + 768] - q2); + let mut b = t - PRIME; + b = (b >> 31) & 1; + key[j] = (key[j] >> 1) + ((b << 7) as u8); + i += 1; + } + j += 1; + } +} + +/* convert 32-byte seed to random polynomial */ + +fn parse(seed: &[u8], poly: &mut [i32]) { + let mut hash: [u8; 4 * DEGREE] = [0; 4 * DEGREE]; + let mut sh = SHA3::new(sha3::SHAKE128); + for i in 0..32 { + sh.process(seed[i]) + } + sh.shake(&mut hash, 4 * DEGREE); + + let mut j = 0; + for i in 0..DEGREE { + let mut n = (hash[j] & 0x7f) as i32; + n <<= 8; + n += (hash[j + 1]) as i32; + n <<= 8; + n += (hash[j + 2]) as i32; + n <<= 8; + n += (hash[j + 3]) as i32; + j += 4; + poly[i] = nres(n); + //poly[i]=modmul(n,ONE); // reduce 31-bit random number mod q + } +} + +/* Compress 14 bits polynomial coefficients into byte array */ +/* 7 bytes is 3x14 */ + +fn nhs_pack(poly: &[i32], array: &mut [u8]) { + let mut j = 0; + let mut i = 0; + while i < DEGREE { + let a = poly[i]; + let b = poly[i + 1]; + let c = poly[i + 2]; + let d = poly[i + 3]; + i += 4; + array[j] = (a & 0xff) as u8; + array[j + 1] = (((a >> 8) | (b << 6)) & 0xff) as u8; + array[j + 2] = ((b >> 2) & 0xff) as u8; + array[j + 3] = (((b >> 10) | (c << 4)) & 0xff) as u8; + array[j + 4] = ((c >> 4) & 0xff) as u8; + array[j + 5] = (((c >> 12) | (d << 2)) & 0xff) as u8; + array[j + 6] = (d >> 6) as u8; + j += 7; + } +} + +fn nhs_unpack(array: &[u8], poly: &mut [i32]) { + let mut j = 0; + let mut i = 0; + while i < DEGREE { + let a = array[j] as i32; + let b = array[j + 1] as i32; + let c = array[j + 2] as i32; + let d = array[j + 3] as i32; + let e = array[j + 4] as i32; + let f = array[j + 5] as i32; + let g = array[j + 6] as i32; + j += 7; + poly[i] = a | ((b & 0x3f) << 8); + poly[i + 1] = (b >> 6) | (c << 2) | ((d & 0xf) << 10); + poly[i + 2] = (d >> 4) | (e << 4) | ((f & 3) << 12); + poly[i + 3] = (f >> 2) | (g << 6); + i += 4; + } +} + +/* See https://eprint.iacr.org/2016/1157.pdf */ + +fn compress(poly: &[i32], array: &mut [u8]) { + let mut col = 0 as i32; + let mut j = 0; + let mut i = 0; + while i < DEGREE { + for _ in 0..8 { + let b = round(poly[i] * 8, PRIME) & 7; + col = (col << 3) + b; + i += 1; + } + array[j] = (col & 0xff) as u8; + array[j + 1] = ((col >> 8) & 0xff) as u8; + array[j + 2] = ((col >> 16) & 0xff) as u8; + j += 3; + col = 0; + } +} + +fn decompress(array: &[u8], poly: &mut [i32]) { + let mut j = 0; + let mut i = 0; + while i < DEGREE { + let mut col = (array[j + 2] as i32) & 0xff; + col = (col << 8) + ((array[j + 1] as i32) & 0xff); + col = (col << 8) + ((array[j] as i32) & 0xff); + j += 3; + for _ in 0..8 { + let b = (col & 0xe00000) >> 21; + col <<= 3; + poly[i] = round(b * PRIME, 8); + i += 1; + } + } +} + +/* generate centered binomial distribution */ + +fn error(rng: &mut RAND, poly: &mut [i32]) { + for i in 0..DEGREE { + let mut n1 = ((rng.getbyte() as i32) & 0xff) + (((rng.getbyte() as i32) & 0xff) << 8); + let mut n2 = ((rng.getbyte() as i32) & 0xff) + (((rng.getbyte() as i32) & 0xff) << 8); + let mut r = 0 as i32; + for _ in 0..16 { + r += (n1 & 1) - (n2 & 1); + n1 >>= 1; + n2 >>= 1; + } + poly[i] = r + PRIME; + } +} + +fn redc_it(p: &mut [i32]) { + for i in 0..DEGREE { + p[i] = redc(p[i] as u64); + } +} + +fn nres_it(p: &mut [i32]) { + for i in 0..DEGREE { + p[i] = nres(p[i]); + } +} + +fn poly_mul(p1: &mut [i32], p3: &[i32]) { + for i in 0..DEGREE { + p1[i] = modmul(p1[i], p3[i]); + } +} + +fn poly_add(p1: &mut [i32], p3: &[i32]) { + for i in 0..DEGREE { + p1[i] += p3[i]; + } +} + +fn poly_rsub(p1: &mut [i32], p2: &[i32]) { + for i in 0..DEGREE { + p1[i] = p2[i] + PRIME - p1[i]; + } +} + +/* reduces inputs < 2q */ +fn poly_soft_reduce(poly: &mut [i32]) { + for i in 0..DEGREE { + let e = poly[i] - PRIME; + poly[i] = e + ((e >> (WL - 1)) & PRIME); + } +} + +/* fully reduces modulo q */ +fn poly_hard_reduce(poly: &mut [i32]) { + for i in 0..DEGREE { + let mut e = modmul(poly[i], ONE); + e -= PRIME; + poly[i] = e + ((e >> (WL - 1)) & PRIME); + } +} + +/* API functions. See https://eprint.iacr.org/2016/1157.pdf Protocol 1 */ +// ss is secret key key, sb is seed|public key to be sent to client +pub fn server_1(rng: &mut RAND, sb: &mut [u8], ss: &mut [u8]) { + let mut seed: [u8; 32] = [0; 32]; + let mut array: [u8; 1792] = [0; 1792]; + let mut s: [i32; DEGREE] = [0; DEGREE]; + let mut e: [i32; DEGREE] = [0; DEGREE]; + let mut b: [i32; DEGREE] = [0; DEGREE]; + + for i in 0..32 { + seed[i] = rng.getbyte(); + } + + parse(&seed, &mut b); + + error(rng, &mut e); + error(rng, &mut s); + + ntt(&mut s); + ntt(&mut e); + poly_mul(&mut b, &s); + poly_add(&mut b, &e); + poly_hard_reduce(&mut b); + + redc_it(&mut b); + nhs_pack(&b, &mut array); + + for i in 0..32 { + sb[i] = seed[i]; + } + + for i in 0..1792 { + sb[i + 32] = array[i]; + } + + poly_hard_reduce(&mut s); + nhs_pack(&s, &mut array); + + for i in 0..1792 { + ss[i] = array[i]; + } +} + +// optimized to reduce memory +// uc is U|cbar to be returned to server +// okey is shared key +pub fn client(rng: &mut RAND, sb: &[u8], uc: &mut [u8], okey: &mut [u8]) { + let mut sh = SHA3::new(sha3::HASH256); + + let mut seed: [u8; 32] = [0; 32]; + let mut array: [u8; 1792] = [0; 1792]; + let mut key: [u8; 32] = [0; 32]; + let mut cc: [u8; 384] = [0; 384]; + + let mut sd: [i32; DEGREE] = [0; DEGREE]; + let mut ed: [i32; DEGREE] = [0; DEGREE]; + let mut u: [i32; DEGREE] = [0; DEGREE]; + let mut k: [i32; DEGREE] = [0; DEGREE]; + let mut c: [i32; DEGREE] = [0; DEGREE]; + + error(rng, &mut sd); + error(rng, &mut ed); + + ntt(&mut sd); + ntt(&mut ed); + + for i in 0..32 { + seed[i] = sb[i]; + } + + for i in 0..1792 { + array[i] = sb[i + 32]; + } + + parse(&seed, &mut u); + + poly_mul(&mut u, &sd); + poly_add(&mut u, &ed); + poly_hard_reduce(&mut u); + + for i in 0..32 { + key[i] = rng.getbyte(); + } + + for i in 0..32 { + sh.process(key[i]); + } + sh.hash(&mut key); + + encode(&key, &mut k); + + nhs_unpack(&array, &mut c); + nres_it(&mut c); + + poly_mul(&mut c, &sd); + intt(&mut c); + error(rng, &mut ed); + poly_add(&mut c, &ed); + poly_add(&mut c, &k); + + compress(&c, &mut cc); + + sh = SHA3::new(sha3::HASH256); + for i in 0..32 { + sh.process(key[i]); + } + sh.hash(&mut key); + + for i in 0..32 { + okey[i] = key[i]; + } + + redc_it(&mut u); + nhs_pack(&u, &mut array); + + for i in 0..1792 { + uc[i] = array[i]; + } + + for i in 0..384 { + uc[i + 1792] = cc[i]; + } +} + +// calculate shared okey from uc and secret key ss +pub fn server_2(ss: &[u8], uc: &[u8], okey: &mut [u8]) { + let mut sh = SHA3::new(sha3::HASH256); + + let mut s: [i32; DEGREE] = [0; DEGREE]; + let mut k: [i32; DEGREE] = [0; DEGREE]; + let mut c: [i32; DEGREE] = [0; DEGREE]; + + let mut array: [u8; 1792] = [0; 1792]; + let mut key: [u8; 32] = [0; 32]; + let mut cc: [u8; 384] = [0; 384]; + + for i in 0..1792 { + array[i] = uc[i]; + } + + nhs_unpack(&array, &mut k); + nres_it(&mut k); + + for i in 0..384 { + cc[i] = uc[i + 1792]; + } + + decompress(&cc, &mut c); + + for i in 0..1792 { + array[i] = ss[i]; + } + + nhs_unpack(&array, &mut s); + + poly_mul(&mut k, &s); + intt(&mut k); + poly_rsub(&mut k, &c); + poly_soft_reduce(&mut k); + + decode(&k, &mut key); + + for i in 0..32 { + sh.process(key[i]); + } + sh.hash(&mut key); + + for i in 0..32 { + okey[i] = key[i]; + } +} + +/* +fn main() { + let x=3; + let y=redc(x as u64); + let z=redc((y as u64)*(R2MODP)); + println!("{:02x}",z); + + let mut a:[i32;1024]=[0;1024]; + for i in 0..1024 {a[i]=i as i32} + + ntt(&mut a); + + for i in 0..1024 {a[i]=modmul(a[i],ONE)} + + intt(&mut a); + + println!("{:02x}",a[7]); + +} +*/ diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/rand.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/rand.rs new file mode 100644 index 000000000000..73ea088cfbc6 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/rand.rs @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//extern crate mcore; + +use crate::hash256::HASH256; + +pub const RAND_NK: usize = 21; +const RAND_NJ: usize = 6; +const RAND_NV: usize = 8; + +// Marsaglia-Zaman random generator (https://projecteuclid.org/euclid.aoap/1177005878) +// Analysis: https://ieeexplore.ieee.org/document/669305 +#[allow(non_camel_case_types)] +pub struct RAND { + pub ira: [u32; RAND_NK], /* random number... */ + pub rndptr: usize, + pub borrow: u32, + pub pool_ptr: usize, + pub pool: [u8; 32], +} + +impl RAND { + pub fn new() -> RAND { + RAND { + ira: [0; RAND_NK], + rndptr: 0, + borrow: 0, + pool_ptr: 0, + pool: [0; 32], + } + } + + #[allow(dead_code)] + pub fn clean(&mut self) { + self.pool_ptr = 0; + self.rndptr = 0; + for i in 0..32 { + self.pool[i] = 0 + } + for i in 0..RAND_NK { + self.ira[i] = 0 + } + self.borrow = 0; + } + + fn sbrand(&mut self) -> u32 { + /* Marsaglia & Zaman random number generator */ + self.rndptr += 1; + if self.rndptr < RAND_NK { + return self.ira[self.rndptr]; + } + self.rndptr = 0; + let mut k = RAND_NK - RAND_NJ; + for i in 0..RAND_NK { + /* calculate next NK values */ + if k == RAND_NK { + k = 0 + } + let t = self.ira[k]; + let pdiff = t.wrapping_sub(self.ira[i]).wrapping_sub(self.borrow); + if pdiff < t { + self.borrow = 0 + } + if pdiff > t { + self.borrow = 1 + } + self.ira[i] = pdiff; + k += 1; + } + self.ira[0] + } + + fn sirand(&mut self, seed: u32) { + let mut m: u32 = 1; + let mut sd = seed; + self.borrow = 0; + self.rndptr = 0; + self.ira[0] ^= sd; + for i in 1..RAND_NK { + /* fill initialisation vector */ + let inn = (RAND_NV * i) % RAND_NK; + self.ira[inn] ^= m; /* note XOR */ + let t = m; + m = sd.wrapping_sub(m); + sd = t; + } + for _ in 0..10000 { + self.sbrand(); + } /* "warm-up" & stir the generator */ + } + + fn fill_pool(&mut self) { + let mut sh = HASH256::new(); + for _ in 0..128 { + sh.process((self.sbrand() & 0xff) as u8) + } + let w = sh.hash(); + for i in 0..32 { + self.pool[i] = w[i] + } + self.pool_ptr = 0; + } + + fn pack(b: [u8; 4]) -> u32 { + /* pack 4 bytes into a 32-bit Word */ + (((b[3] as u32) & 0xff) << 24) + | (((b[2] as u32) & 0xff) << 16) + | (((b[1] as u32) & 0xff) << 8) + | ((b[0] as u32) & 0xff) + } + + pub fn seed(&mut self, rawlen: usize, raw: &[u8]) { + /* initialise from at least 128 byte string of raw random entropy */ + let mut b: [u8; 4] = [0; 4]; + let mut sh = HASH256::new(); + self.pool_ptr = 0; + + for i in 0..RAND_NK { + self.ira[i] = 0 + } + if rawlen > 0 { + for i in 0..rawlen { + sh.process(raw[i]); + } + let digest = sh.hash(); + + /* initialise PRNG from distilled randomness */ + + for i in 0..8 { + b[0] = digest[4 * i]; + b[1] = digest[4 * i + 1]; + b[2] = digest[4 * i + 2]; + b[3] = digest[4 * i + 3]; + self.sirand(RAND::pack(b)); + } + } + self.fill_pool(); + } + + pub fn getbyte(&mut self) -> u8 { + let r = self.pool[self.pool_ptr]; + self.pool_ptr += 1; + if self.pool_ptr >= 32 { + self.fill_pool() + } + r + } +} + +/* test main program + +fn main() { + let mut raw : [u8;100]=[0;100]; + let mut rng=RAND::new(); + + rng.clean(); + for i in 0..100 {raw[i]=i as u8} + + rng.seed(100,&raw); + + for _ in 0..1000 { + print!("{:03} ",rng.getbyte()); + } +} */ diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/sha3.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/sha3.rs new file mode 100644 index 000000000000..d3bd6d6cc98b --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/sha3.rs @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pub const HASH224: usize = 28; +pub const HASH256: usize = 32; +pub const HASH384: usize = 48; +pub const HASH512: usize = 64; +pub const SHAKE128: usize = 16; +pub const SHAKE256: usize = 32; + +const ROUNDS: usize = 24; + +const RC: [u64; 24] = [ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +]; + +pub struct SHA3 { + length: usize, + rate: usize, + len: usize, + //s: [[u64; 5]; 5], + s: [u64; 25], +} + +impl SHA3 { + fn rotl(x: u64, n: u64) -> u64 { + ((x) << n) | ((x) >> (64 - n)) + } + + fn transform(&mut self) { + for k in 0..ROUNDS { + let c0 = self.s[0] ^ self.s[5] ^ self.s[10] ^ self.s[15] ^ self.s[20]; + let c1 = self.s[1] ^ self.s[6] ^ self.s[11] ^ self.s[16] ^ self.s[21]; + let c2 = self.s[2] ^ self.s[7] ^ self.s[12] ^ self.s[17] ^ self.s[22]; + let c3 = self.s[3] ^ self.s[8] ^ self.s[13] ^ self.s[18] ^ self.s[23]; + let c4 = self.s[4] ^ self.s[9] ^ self.s[14] ^ self.s[19] ^ self.s[24]; + + let d0 = c4 ^ SHA3::rotl(c1, 1); + let d1 = c0 ^ SHA3::rotl(c2, 1); + let d2 = c1 ^ SHA3::rotl(c3, 1); + let d3 = c2 ^ SHA3::rotl(c4, 1); + let d4 = c3 ^ SHA3::rotl(c0, 1); + + let b00 = self.s[0] ^ d0; + let b02 = SHA3::rotl(self.s[1] ^ d1, 1); + let b04 = SHA3::rotl(self.s[2] ^ d2, 62); + let b01 = SHA3::rotl(self.s[3] ^ d3, 28); + let b03 = SHA3::rotl(self.s[4] ^ d4, 27); + + let b13 = SHA3::rotl(self.s[5] ^ d0, 36); + let b10 = SHA3::rotl(self.s[6] ^ d1, 44); + let b12 = SHA3::rotl(self.s[7] ^ d2, 6); + let b14 = SHA3::rotl(self.s[8] ^ d3, 55); + let b11 = SHA3::rotl(self.s[9] ^ d4, 20); + + let b21 = SHA3::rotl(self.s[10] ^ d0, 3); + let b23 = SHA3::rotl(self.s[11] ^ d1, 10); + let b20 = SHA3::rotl(self.s[12] ^ d2, 43); + let b22 = SHA3::rotl(self.s[13] ^ d3, 25); + let b24 = SHA3::rotl(self.s[14] ^ d4, 39); + + let b34 = SHA3::rotl(self.s[15] ^ d0, 41); + let b31 = SHA3::rotl(self.s[16] ^ d1, 45); + let b33 = SHA3::rotl(self.s[17] ^ d2, 15); + let b30 = SHA3::rotl(self.s[18] ^ d3, 21); + let b32 = SHA3::rotl(self.s[19] ^ d4, 8); + + let b42 = SHA3::rotl(self.s[20] ^ d0, 18); + let b44 = SHA3::rotl(self.s[21] ^ d1, 2); + let b41 = SHA3::rotl(self.s[22] ^ d2, 61); + let b43 = SHA3::rotl(self.s[23] ^ d3, 56); + let b40 = SHA3::rotl(self.s[24] ^ d4, 14); + + self.s[0] = b00 ^ (!b10 & b20); + self.s[1] = b10 ^ (!b20 & b30); + self.s[2] = b20 ^ (!b30 & b40); + self.s[3] = b30 ^ (!b40 & b00); + self.s[4] = b40 ^ (!b00 & b10); + + self.s[5] = b01 ^ (!b11 & b21); + self.s[6] = b11 ^ (!b21 & b31); + self.s[7] = b21 ^ (!b31 & b41); + self.s[8] = b31 ^ (!b41 & b01); + self.s[9] = b41 ^ (!b01 & b11); + + self.s[10] = b02 ^ (!b12 & b22); + self.s[11] = b12 ^ (!b22 & b32); + self.s[12] = b22 ^ (!b32 & b42); + self.s[13] = b32 ^ (!b42 & b02); + self.s[14] = b42 ^ (!b02 & b12); + + self.s[15] = b03 ^ (!b13 & b23); + self.s[16] = b13 ^ (!b23 & b33); + self.s[17] = b23 ^ (!b33 & b43); + self.s[18] = b33 ^ (!b43 & b03); + self.s[19] = b43 ^ (!b03 & b13); + + self.s[20] = b04 ^ (!b14 & b24); + self.s[21] = b14 ^ (!b24 & b34); + self.s[22] = b24 ^ (!b34 & b44); + self.s[23] = b34 ^ (!b44 & b04); + self.s[24] = b44 ^ (!b04 & b14); + + self.s[0] ^= RC[k]; + } + } + + /* Initialise Hash function */ + pub fn init(&mut self, olen: usize) { + /* initialise */ + for i in 0..25 { + self.s[i] = 0; + } + self.length = 0; + self.len = olen; + self.rate = 200 - 2 * olen; + } + + pub fn new(olen: usize) -> SHA3 { + let mut nh = SHA3 { + length: 0, + rate: 0, + len: 0, + s: [0; 25], + }; + nh.init(olen); + nh + } + + pub fn new_copy(hh: &SHA3) -> SHA3 { + let mut nh = SHA3 { + length: 0, + rate: 0, + len: 0, + s: [0; 25], + }; + nh.length = hh.length; + nh.len = hh.len; + nh.rate = hh.rate; + for i in 0..25 { + nh.s[i] = hh.s[i]; + } + nh + } + + /* process a single byte */ + pub fn process(&mut self, byt: u8) { + /* process the next message byte */ + let cnt = self.length as usize; + let b = cnt % 8; + let ind = cnt / 8; + self.s[ind] ^= (byt as u64) << (8 * b); + self.length += 1; + if self.length == self.rate { + self.length = 0; + self.transform(); + } + } + + /* process an array of bytes */ + + pub fn process_array(&mut self, b: &[u8]) { + for i in 0..b.len() { + self.process(b[i]) + } + } + + /* process a 32-bit integer */ + pub fn process_num(&mut self, n: i32) { + self.process(((n >> 24) & 0xff) as u8); + self.process(((n >> 16) & 0xff) as u8); + self.process(((n >> 8) & 0xff) as u8); + self.process((n & 0xff) as u8); + } + + pub fn squeeze(&mut self, buff: &mut [u8], olen: usize) { + let mut m = 0; + let nb = olen / self.rate; + + for _ in 0..nb { + for i in 0..self.rate / 8 { + let mut el = self.s[i]; + for _ in 0..8 { + buff[m] = (el & 0xff) as u8; + m += 1; + el >>= 8; + } + } + self.transform(); + } + + let mut i = 0; + while m < olen { + let mut el = self.s[i]; + i += 1; + for _ in 0..8 { + buff[m] = (el & 0xff) as u8; + m += 1; + if m >= olen { + break; + } + el >>= 8; + } + } + + /* + loop { + for i in 0..25 { + let mut el = self.s[i]; + for _ in 0..8 { + buff[m] = (el & 0xff) as u8; + m += 1; + if m >= olen || (m % self.rate) == 0 { + done = true; + break; + } + el >>= 8; + } + if done { + break; + } + } + if m >= olen { + break; + } + done = false; + self.transform(); + } */ + } + + /* Generate 32-byte Hash */ + pub fn hash(&mut self, digest: &mut [u8]) { + /* pad message and finish - supply digest */ + let q = self.rate - self.length; + if q == 1 { + self.process(0x86); + } else { + self.process(0x06); + while self.length != self.rate - 1 { + self.process(0x00) + } + self.process(0x80); + } + let hlen = self.len as usize; + self.squeeze(digest, hlen); + } + + pub fn continuing_hash(&mut self, digest: &mut [u8]) { + let mut sh = SHA3::new_copy(self); + sh.hash(digest) + } + + pub fn shake(&mut self, digest: &mut [u8], olen: usize) { + let q = self.rate - self.length; + if q == 1 { + self.process(0x9f); + } else { + self.process(0x1f); + while self.length != self.rate - 1 { + self.process(0x00) + } + self.process(0x80); + } + self.squeeze(digest, olen); + } + + pub fn continuing_shake(&mut self, digest: &mut [u8], olen: usize) { + let mut sh = SHA3::new_copy(self); + sh.shake(digest, olen); + } +} + +//916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18 +//afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185 +//98be04516c04cc73593fef3ed0352ea9f6443942d6950e29a372a681c3deaf4535423709b02843948684e029010badcc0acd8303fc85fdad3eabf4f78cae165635f57afd28810fc2 +/* +fn main() { + let s = String::from("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); + let mut digest: [u8;100]=[0;100]; + let test = s.into_bytes(); + + let mut sh=SHA3::new(HASH256); + for i in 0..test.len(){ + sh.process(test[i]); + } + sh.hash(&mut digest); + for i in 0..32 {print!("{:02x}",digest[i])} + println!(""); + + sh=SHA3::new(HASH512); + for i in 0..test.len(){ + sh.process(test[i]); + } + sh.hash(&mut digest); + for i in 0..64 {print!("{:02x}",digest[i])} + println!(""); + + sh=SHA3::new(SHAKE256); + for i in 0..test.len(){ + sh.process(test[i]); + } + sh.shake(&mut digest,72); + for i in 0..72 {print!("{:02x}",digest[i])} + println!(""); + +} */ diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/share.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/share.rs new file mode 100644 index 000000000000..a48f91b17316 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/share.rs @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Shamir threshold secret sharing module */ +/* Split any byte array into number of shares < 256 */ +/* Specify number of shares required for recovery - nsr */ + +/* See TestMPIN.rs for an example of use */ + +use crate::rand::RAND; + +const PTAB: [u8; 256] = [ + 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53, 95, 225, 56, 72, 216, 115, + 149, 164, 247, 2, 6, 10, 30, 34, 102, 170, 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, + 112, 144, 171, 230, 49, 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205, + 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136, 131, 158, 185, 208, + 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154, 181, 196, 87, 249, 16, 48, 80, 240, + 11, 29, 39, 105, 187, 214, 97, 163, 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, + 233, 32, 96, 160, 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65, 195, + 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, 159, 186, 213, 100, 172, + 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, 155, 182, 193, 88, 232, 35, 101, 175, + 234, 37, 111, 177, 200, 67, 197, 84, 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, + 203, 70, 202, 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, 18, 54, + 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23, 57, 75, 221, 124, 132, 151, + 162, 253, 28, 36, 108, 180, 199, 82, 246, 1, +]; + +const LTAB: [u8; 256] = [ + 0, 255, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3, 100, 4, 224, 14, 52, 141, + 129, 239, 76, 113, 8, 200, 248, 105, 28, 193, 125, 194, 29, 181, 249, 185, 39, 106, 77, 228, + 166, 114, 154, 201, 9, 120, 101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53, 147, 218, + 142, 150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241, 64, 70, 131, 56, 102, 221, 253, + 48, 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16, 126, 110, 72, 195, 163, 182, 30, 66, + 58, 107, 40, 84, 250, 133, 61, 186, 43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243, + 115, 167, 87, 175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232, 44, + 215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81, 160, 127, 12, 246, 111, 23, + 196, 73, 236, 216, 67, 31, 45, 164, 118, 123, 183, 204, 187, 62, 90, 251, 96, 177, 134, 59, 82, + 161, 108, 170, 85, 41, 157, 151, 178, 135, 144, 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, + 91, 209, 83, 57, 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171, 68, 17, 146, + 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165, 103, 74, 237, 222, 197, 49, 254, + 24, 13, 99, 140, 128, 192, 247, 112, 7, +]; + +pub struct SHARE<'a> { + id: u8, + nsr: u8, + b: &'a [u8], +} + +fn mul(x: u8, y: u8) -> u8 { + /* x.y= AntiLog(Log(x) + Log(y)) */ + let ix = (x as usize) & 0xff; + let iy = (y as usize) & 0xff; + let lx = (LTAB[ix] as usize) & 0xff; + let ly = (LTAB[iy] as usize) & 0xff; + + if x != 0 && y != 0 { + PTAB[(lx + ly) % 255] + } else { + 0 + } +} + +fn add(x: u8, y: u8) -> u8 { + x ^ y +} + +fn inv(x: u8) -> u8 { + let ix = (x as usize) & 0xff; + let lx = (LTAB[ix] as usize) & 0xff; + PTAB[255 - lx] +} + +/* Lagrange interpolation */ +fn interpolate(n: usize, x: &[u8], y: &[u8]) -> u8 { + let mut yp = 0 as u8; + for i in 0..n { + let mut p = 1 as u8; + for j in 0..n { + if i != j { + p = mul(p, mul(x[j], inv(add(x[i], x[j])))); + } + } + yp = add(yp, mul(p, y[i])); + } + yp +} + +impl<'a> SHARE<'a> { + /* Return a share of M */ + /* input id - Unique share ID */ + /* input nsr - Number of shares required for recovery */ + /* input Message M to be shared */ + /* input Random number generator rng to be used */ + /* return share structure */ + // must bind lifetime of the byte array stored by structure, to lifetime of s + pub fn new( + ident: usize, + numshare: usize, + s: &'a mut [u8], + m: &[u8], + rng: &mut RAND, + ) -> SHARE<'a> { + if ident < 1 || ident >= 256 || numshare < 2 || numshare >= 256 { + return SHARE { + id: 0, + nsr: 0, + b: s, + }; + } + let len = m.len(); + for j in 0..len { + let mut x = ident as u8; + s[j] = m[j]; + for _ in 1..numshare { + s[j] = add(s[j], mul(rng.getbyte(), x)); + x = mul(x, ident as u8); + } + } + SHARE { + id: ident as u8, + nsr: numshare as u8, + b: s, + } + } + /* recover M from shares */ + pub fn recover(m: &mut [u8], s: &[SHARE]) { + let len = s[0].b.len(); + let nsr = s[0].nsr as usize; + if nsr != s.len() { + return; + } + for i in 1..nsr { + if s[i].nsr as usize != nsr || s[i].b.len() != len { + return; + } + } + let mut x: [u8; 256] = [0; 256]; + let mut y: [u8; 256] = [0; 256]; + + for j in 0..len { + for i in 0..nsr { + x[i] = s[i].id; + y[i] = s[i].b[j]; + } + m[j] = interpolate(nsr, &x, &y); + } + } +} diff --git a/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/x509.rs b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/x509.rs new file mode 100644 index 000000000000..0602946c6ef7 --- /dev/null +++ b/third_party/rust/chromium_crates_io/vendor/brave-miracl-0.1.3/src/x509.rs @@ -0,0 +1,1311 @@ +/* + * Copyright (c) 2012-2020 MIRACL UK Ltd. + * + * This file is part of MIRACL Core + * (see https://github.com/miracl/core). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* CORE X.509 Functions */ + +pub struct PKTYPE { + pub kind: usize, + pub hash: usize, + pub curve: usize, + pub len: usize, +} + +pub struct FDTYPE { + pub index: usize, + pub length: usize, +} + +// Supported Encryption/Signature Methods + +pub const ECC: usize = 1; +pub const RSA: usize = 2; +pub const ECD: usize = 3; // for Ed25519 and Ed448 +pub const PQ: usize = 4; + +// Supported Hash functions + +pub const H256: usize = 2; +pub const H384: usize = 3; +pub const H512: usize = 4; +pub const SHAKE256: usize = 5; + +// Supported Curves + +pub const USE_NIST256: usize = 4; +/**< For the NIST 256-bit standard curve - WEIERSTRASS only */ +pub const USE_ED25519: usize = 1; +/**< Bernstein's Modulus 2^255-19 - EDWARDS only */ +pub const USE_ED448: usize = 5; +//const USE_BRAINPOOL:usize = 2; /**< For Brainpool 256-bit curve - WEIERSTRASS only */ +//const USE_ANSSI:usize = 3; /**< For French 256-bit standard curve - WEIERSTRASS only */ +pub const USE_NIST384: usize = 10; +/**< For the NIST 384-bit standard curve - WEIERSTRASS only */ +pub const USE_NIST521: usize = 12; +/**< For the NIST 521-bit standard curve - WEIERSTRASS only */ + +const ANY: u8 = 0x00; +const SEQ: u8 = 0x30; +const OID: u8 = 0x006; +const INT: u8 = 0x02; +const NUL: u8 = 0x05; +//const ZER: u8 = 0x00; +//const UTF: u8 = 0x0C; +const UTC: u8 = 0x17; +const GTM: u8 = 0x18; +//const LOG: u8 = 0x01; +const BIT: u8 = 0x03; +const OCT: u8 = 0x04; +//const STR: u8 = 0x13; +const SET: u8 = 0x31; +//const IA5: u8 = 0x16; +const EXT: u8 = 0xA3; +const DNS: u8 = 0x82; + +// Define some OIDs +// Elliptic Curve with SHA256 + +const ECCSHA256: [u8; 8] = [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02]; +const ECCSHA384: [u8; 8] = [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03]; +const ECCSHA512: [u8; 8] = [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04]; +const ECPK: [u8; 7] = [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01]; +const EDPK25519: [u8; 3] = [0x2b, 0x65, 0x70]; +const EDPK448: [u8; 3] = [0x2b, 0x65, 0x71]; +const PRIME25519: [u8; 9] = [0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]; +const PRIME256V1: [u8; 8] = [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]; +const SECP384R1: [u8; 5] = [0x2B, 0x81, 0x04, 0x00, 0x22]; +const SECP521R1: [u8; 5] = [0x2B, 0x81, 0x04, 0x00, 0x23]; +const RSAPK: [u8; 9] = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01]; +const RSASHA256: [u8; 9] = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b]; +const RSASHA384: [u8; 9] = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0]; +const RSASHA512: [u8; 9] = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d]; +const DILITHIUM3: [u8; 11] = [ + 0x2b, 0x06, 0x01, 0x04, 0x01, 0x02, 0x82, 0x0B, 0x07, 0x06, 0x05, +]; +// Cert details + +pub const CN: [u8; 3] = [0x55, 0x04, 0x06]; // countryName +pub const SN: [u8; 3] = [0x55, 0x04, 0x08]; // stateName +pub const LN: [u8; 3] = [0x55, 0x04, 0x07]; // localName +pub const ON: [u8; 3] = [0x55, 0x04, 0x0A]; // orgName +pub const UN: [u8; 3] = [0x55, 0x04, 0x0B]; // unitName +pub const MN: [u8; 3] = [0x55, 0x04, 0x03]; // myName +pub const EN: [u8; 9] = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01]; // emailName + +// Extensions +pub const AN: [u8; 3] = [0x55, 0x1D, 0x11]; // altName +pub const KU: [u8; 3] = [0x55, 0x1D, 0x0F]; // keyUsage +pub const BC: [u8; 3] = [0x55, 0x1D, 0x13]; // basicConstraints + +fn getalen(tag: u8, b: &[u8], j: usize) -> usize { + let mut k = j; + let mut len: usize; + if tag != 0 && b[k] != tag { + return 0; + } + k += 1; + if b[k] == 0x81 { + k += 1; + len = b[k] as usize; + } else if b[k] == 0x82 { + k += 1; + len = 256 * (b[k] as usize); + k += 1; + len += b[k] as usize; + } else { + len = b[k] as usize; + if len > 127 { + return 0; + } + } + len +} + +fn skip(len: usize) -> usize { + if len < 128 { + return 2; + } + if len < 256 { + return 3; + } + 4 +} + +fn bround(len: usize) -> usize { + if len % 8 == 0 { + return len; + } + len + (8 - len % 8) +} + +impl PKTYPE { + pub fn new() -> PKTYPE { + PKTYPE { + kind: 0, + hash: 0, + curve: 0, + len: 0, + } + } +} + +impl FDTYPE { + pub fn new() -> FDTYPE { + FDTYPE { + index: 0, + length: 0, + } + } +} + +// Input private key in PKCS#8 format +// e.g. openssl req -x509 -nodes -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 +// e.g. openssl req -x509 -nodes -days 3650 -newkey ec:<(openssl ecparam -name prime256v1) -keyout key.pem -out ecdsacert.pem +// extract private key from uncompressed key.pem into octet +// For RSA octet = p|q|dp|dq|c where pk->len is multiple of 5 +// For ECC octet = k +pub fn extract_private_key(c: &[u8], pk: &mut [u8]) -> PKTYPE { + let mut soid: [u8; 12] = [0; 12]; + let mut ret = PKTYPE::new(); + let mut j = 0 as usize; + let pklen = pk.len(); + + let mut len = getalen(SEQ, c, j); // Check for expected SEQ clause, and get length + if len == 0 { + // if not a SEQ clause, there is a problem, exit + return ret; + } + j += skip(len); // skip over length to clause contents. + if len + j != c.len() { + return ret; + } + len = getalen(INT, c, j); + if len == 0 { + // if not a SEQ clause, there is a problem, exit + return ret; + } + j += skip(len) + len; + len = getalen(SEQ, c, j); + if len == 0 { + // if not a SEQ clause, there is a problem, exit + return ret; + } + j += skip(len); + // extract OID + len = getalen(OID, c, j); + if len == 0 { + return ret; + } + j += skip(len); + + let mut fin = j + len; + if len > soid.len() { + return ret; + } + let mut slen = 0; + while j < fin { + soid[slen] = c[j]; + slen += 1; + j += 1; + } + j = fin; + if EDPK25519 == soid[0..slen] { + len = getalen(OCT, c, j); + if len == 0 { + return ret; + } + j += skip(len); + len = getalen(OCT, c, j); + if len == 0 { + return ret; + } + j += skip(len); + let rlen = 32; + if rlen > pklen { + return ret; + } + ret.len = rlen; + for i in 0..rlen - len { + pk[i] = 0; + } + for i in rlen - len..rlen { + pk[i] = c[j]; + j += 1; + } + ret.kind = ECD; + ret.curve = USE_ED25519; + } + if EDPK448 == soid[0..slen] { + len = getalen(OCT, c, j); + if len == 0 { + return ret; + } + j += skip(len); + len = getalen(OCT, c, j); + if len == 0 { + return ret; + } + j += skip(len); + let rlen = 57; + if rlen > pklen { + return ret; + } + ret.len = rlen; + for i in 0..rlen - len { + pk[i] = 0; + } + for i in rlen - len..rlen { + pk[i] = c[j]; + j += 1; + } + ret.kind = ECD; + ret.curve = USE_ED448; + } + if DILITHIUM3 == soid[0..slen] { + len = getalen(OCT, c, j); + if len == 0 { + return ret; + } + j += skip(len); + len = getalen(OCT, c, j); + if len == 0 { + return ret; + } + j += skip(len); + let mut tlen = len; + if tlen > pk.len() { + tlen = pk.len(); + } + + for i in 0..tlen { + pk[i] = c[j]; + j += 1; + } + ret.len = tlen; + ret.kind = PQ; + ret.curve = 8 * tlen; + } + if ECPK == soid[0..slen] { + len = getalen(OID, c, j); + if len == 0 { + return ret; + } + j += skip(len); + + fin = j + len; + if len > soid.len() { + return ret; + } + slen = 0; + while j < fin { + soid[slen] = c[j]; + slen += 1; + j += 1; + } + j = fin; + len = getalen(OCT, c, j); + if len == 0 { + return ret; + } + j += skip(len); + len = getalen(SEQ, c, j); + if len == 0 { + return ret; + } + j += skip(len); + len = getalen(INT, c, j); + if len == 0 { + return ret; + } + j += skip(len) + len; // jump over version + len = getalen(OCT, c, j); + if len == 0 { + return ret; + } + j += skip(len); + + ret.kind = ECC; + let mut rlen = 0; + if PRIME256V1 == soid[0..slen] { + ret.curve = USE_NIST256; + rlen = 32; + } + if SECP384R1 == soid[0..slen] { + ret.curve = USE_NIST384; + rlen = 48; + } + if SECP521R1 == soid[0..slen] { + ret.curve = USE_NIST521; + rlen = 66; + } + if rlen > pklen { + ret.curve = 0; + ret.len = 0; + return ret; + } + ret.len = rlen; + for i in 0..rlen - len { + pk[i] = 0; + } + for i in rlen - len..rlen { + pk[i] = c[j]; + j += 1; + } + } + if RSAPK == soid[0..slen] { + len = getalen(NUL, c, j); + if len != 0 { + return ret; + } + j += skip(len); + + len = getalen(OCT, c, j); + if len == 0 { + return ret; + } + j += skip(len); + + len = getalen(SEQ, c, j); + if len == 0 { + return ret; + } + j += skip(len); + + len = getalen(INT, c, j); + if len == 0 { + return ret; + } + j += skip(len) + len; // jump over version + + len = getalen(INT, c, j); + if len == 0 { + return ret; + } + j += skip(len) + len; // jump over n + + len = getalen(INT, c, j); + if len == 0 { + return ret; + } + j += skip(len) + len; // jump over e + + len = getalen(INT, c, j); + if len == 0 { + return ret; + } + j += skip(len) + len; // jump over d + + len = getalen(INT, c, j); + if len == 0 { + return ret; + } + j += skip(len); // get p + + if c[j] == 0 { + j += 1; + len -= 1; + } + let mut rlen = bround(len); + + if 5 * rlen > pklen { + return ret; + } + + for i in 0..rlen - len { + pk[i] = 0; + } + for i in rlen - len..rlen { + pk[i] = c[j]; + j += 1; + } + + let flen = rlen; // should be same length for all + for k in 1..5 { + len = getalen(INT, c, j); + if len == 0 { + return ret; + } + j += skip(len); // get q,dp,dq,c + if c[j] == 0 { + j += 1; + len -= 1; + } + rlen = bround(len); + if rlen != flen { + return ret; + } + for i in 0..rlen - len { + pk[i] = 0; + } + for i in rlen - len..rlen { + pk[k * flen + i] = c[j]; + j += 1; + } + } + ret.len = 5 * flen; + ret.kind = RSA; + ret.curve = 16 * flen; + } + ret +} + +// Input signed cert as octet, and extract signature +// Return 0 for failure, ECC for Elliptic Curve signature, RSA for RSA signature +// Note that signature type is not provided here - its the type of the public key that +// is used to verify it that matters, and which determines for example the curve to be used! +pub fn extract_cert_sig(sc: &[u8], sig: &mut [u8]) -> PKTYPE { + let mut soid: [u8; 12] = [0; 12]; + let mut ret = PKTYPE::new(); + let mut j = 0 as usize; + let mut len = getalen(SEQ, sc, j); // Check for expected SEQ clause, and get length + let siglen = sig.len(); + + if len == 0 { + // if not a SEQ clause, there is a problem, exit + return ret; + } + j += skip(len); // skip over length to clause contents. Add len to skip clause + if len + j != sc.len() { + return ret; + } + len = getalen(SEQ, sc, j); + if len == 0 { + return ret; + } + j += skip(len) + len; // jump over cert to signature OID + len = getalen(SEQ, sc, j); + if len == 0 { + return ret; + } + j += skip(len); + let sj = j + len; // Needed to jump over signature OID + + // dive in to extract OID + len = getalen(OID, sc, j); + if len == 0 { + return ret; + } + j += skip(len); + let mut fin = j + len; + if len > soid.len() { + return ret; + } + + let mut slen = 0; + while j < fin { + soid[slen] = sc[j]; + slen += 1; + j += 1; + } + if EDPK25519 == soid[0..slen] { + ret.kind = ECD; + ret.hash = H512; + } + if EDPK448 == soid[0..slen] { + ret.kind = ECD; + ret.hash = SHAKE256; + } + if ECCSHA256 == soid[0..slen] { + ret.kind = ECC; + ret.hash = H256; + } + if ECCSHA384 == soid[0..slen] { + ret.kind = ECC; + ret.hash = H384; + } + if ECCSHA512 == soid[0..slen] { + ret.kind = ECC; + ret.hash = H512; + } + if RSASHA256 == soid[0..slen] { + ret.kind = RSA; + ret.hash = H256; + } + if RSASHA384 == soid[0..slen] { + ret.kind = RSA; + ret.hash = H384; + } + if RSASHA512 == soid[0..slen] { + ret.kind = RSA; + ret.hash = H512; + } + if DILITHIUM3 == soid[0..slen] { + ret.kind = PQ; + ret.hash = 0; // hash type is implicit + } + if ret.kind == 0 { + return ret; // unsupported type + } + + j = sj; + len = getalen(BIT, sc, j); + if len == 0 { + ret.kind = 0; + return ret; + } + j += skip(len); + j += 1; + len -= 1; // skip bit shift (hopefully 0!) + + if ret.kind == ECD { + if len > siglen { + ret.kind = 0; + return ret; + } + ret.len = len; + slen = 0; + fin = j + len; + while j < fin { + sig[slen] = sc[j]; + j += 1; + slen += 1; + } + if ret.hash == H512 { + ret.curve = USE_ED25519; + } + if ret.hash == SHAKE256 { + ret.curve = USE_ED448; + } + } + + if ret.kind == ECC { + len = getalen(SEQ, sc, j); + if len == 0 { + ret.kind = 0; + return ret; + } + j += skip(len); + + // pick up r part of signature + len = getalen(INT, sc, j); + if len == 0 { + ret.kind = 0; + return ret; + } + j += skip(len); + if sc[j] == 0 { + // skip leading zero + j += 1; + len -= 1; + } + let mut rlen = bround(len); + let mut ex = rlen - len; + + if 2 * rlen > siglen { + ret.kind = 0; + return ret; + } + ret.len = 2 * rlen; + + slen = 0; + for _ in 0..ex { + sig[slen] = 0; + slen += 1; + } + fin = j + len; + while j < fin { + sig[slen] = sc[j]; + j += 1; + slen += 1; + } + // pick up s part of signature + len = getalen(INT, sc, j); + if len == 0 { + ret.kind = 0; + return ret; + } + j += skip(len); + if sc[j] == 0 { + // skip leading zero + j += 1; + len -= 1; + } + rlen = bround(len); + ex = rlen - len; + for _ in 0..ex { + sig[slen] = 0; + slen += 1; + } + fin = j + len; + while j < fin { + sig[slen] = sc[j]; + j += 1; + slen += 1; + } + if ret.hash == H256 { + ret.curve = USE_NIST256; + } + if ret.hash == H384 { + ret.curve = USE_NIST384; + } + if ret.hash == H512 { + ret.curve = USE_NIST521; + } + } + if ret.kind == RSA { + let rlen = bround(len); + let ex = rlen - len; + if rlen > siglen { + ret.kind = 0; + ret.curve = 0; + return ret; + } + ret.len = rlen; + slen = 0; + for _ in 0..ex { + sig[slen] = 0; + slen += 1; + } + fin = j + len; + while j < fin { + sig[slen] = sc[j]; + j += 1; + slen += 1; + } + ret.curve = 8 * rlen; + } + if ret.kind == PQ { + if len > siglen { + ret.kind = 0; + ret.curve = 0; + return ret; + } + ret.len = len; + slen = 0; + fin = j + len; + while j < fin { + sig[slen] = sc[j]; + j += 1; + slen += 1; + } + ret.curve = 8 * len; + } + ret +} + +// Extract pointer to cert inside signed cert, and return its length; +// let cert=&sc[ptr..ptr+len] +pub fn find_cert(sc: &[u8], ptr: &mut usize) -> usize { + let mut j: usize = 0; + + let mut len = getalen(SEQ, sc, j); + if len == 0 { + return 0; + } + j += skip(len); + + let k = j; + len = getalen(SEQ, sc, j); + if len == 0 { + return 0; + } + j += skip(len); + let fin = j + len; + *ptr = k; + fin - k +} + +// Extract certificate from signed cert +pub fn extract_cert(sc: &[u8], cert: &mut [u8]) -> usize { + let mut ptr = 0; + let n = find_cert(sc, &mut ptr); + let k = ptr; + let fin = n + k; + if fin - k > cert.len() { + return 0; + } + for i in k..fin { + cert[i - k] = sc[i]; + } + n +} + +// extract pointer to ASN.1 raw public Key inside certificate, and return its length; +// let public_key=&c[ptr..ptr+len] +pub fn find_public_key(c: &[u8], ptr: &mut usize) -> usize { + let mut j: usize = 0; + let mut len = getalen(SEQ, c, j); + if len == 0 { + return 0; + } + j += skip(len); + + if len + j != c.len() { + return 0; + } + + len = getalen(ANY, c, j); + if len == 0 { + return 0; + } + j += skip(len) + len; //jump over version clause + + len = getalen(INT, c, j); + if len > 0 { + j += skip(len) + len; // jump over serial number clause (if there is one) + } + + len = getalen(SEQ, c, j); + if len == 0 { + return 0; + } + j += skip(len) + len; // jump over signature algorithm + + len = getalen(SEQ, c, j); + if len == 0 { + return 0; + } + j += skip(len) + len; // skip issuer + + len = getalen(SEQ, c, j); + if len == 0 { + return 0; + } + j += skip(len) + len; // skip validity + + len = getalen(SEQ, c, j); + if len == 0 { + return 0; + } + j += skip(len) + len; // skip subject + + let k = j; + len = getalen(SEQ, c, j); + if len == 0 { + return 0; + } + j += skip(len); // + + let fin = j + len; + *ptr = k; + fin - k +} + +// get Public details from ASN.1 description +pub fn get_public_key(c: &[u8], key: &mut [u8]) -> PKTYPE { + let mut koid: [u8; 12] = [0; 12]; + let mut ret = PKTYPE::new(); + let mut j = 0; + let keylen = key.len(); + + let mut len = getalen(SEQ, c, j); + if len == 0 { + return ret; + } + j += skip(len); // + + len = getalen(SEQ, c, j); + if len == 0 { + return ret; + } + j += skip(len); // + + // ** Maybe dive in and check Public Key OIDs here? + // ecpublicKey & prime256v1, secp384r1 or secp521r1 for ECC + // rsapublicKey for RSA + + let sj = j + len; + + len = getalen(OID, c, j); + if len == 0 { + return ret; + } + j += skip(len); + + let mut fin = j + len; + if len > koid.len() { + return ret; + } + let mut slen = 0; + while j < fin { + koid[slen] = c[j]; + slen += 1; + j += 1; + } + ret.kind = 0; + if ECPK == koid[0..slen] { + ret.kind = ECC; + } + if EDPK25519 == koid[0..slen] { + ret.kind = ECD; + ret.curve = USE_ED25519 + } + if EDPK448 == koid[0..slen] { + ret.kind = ECD; + ret.curve = USE_ED448 + } + if RSAPK == koid[0..slen] { + ret.kind = RSA; + } + if DILITHIUM3 == koid[0..slen] { + ret.kind = PQ; + } + + if ret.kind == 0 { + return ret; + } + if ret.kind == ECC { + len = getalen(OID, c, j); + if len == 0 { + ret.kind = 0; + return ret; + } + j += skip(len); + + fin = j + len; + if len > koid.len() { + ret.kind = 0; + return ret; + } + slen = 0; + while j < fin { + koid[slen] = c[j]; + slen += 1; + j += 1; + } + if PRIME25519 == koid[0..slen] { + ret.curve = USE_ED25519; + } + if PRIME256V1 == koid[0..slen] { + ret.curve = USE_NIST256; + } + if SECP384R1 == koid[0..slen] { + ret.curve = USE_NIST384; + } + if SECP521R1 == koid[0..slen] { + ret.curve = USE_NIST521; + } + } + j = sj; + + len = getalen(BIT, c, j); + if len == 0 { + ret.kind = 0; + return ret; + } + j += skip(len); + j += 1; + len -= 1; // skip bit shift (hopefully 0!) + + if ret.kind == ECC || ret.kind == ECD || ret.kind == PQ { + if len > keylen { + ret.kind = 0; + return ret; + } + ret.len = len; + fin = j + len; + slen = 0; + while j < fin { + key[slen] = c[j]; + slen += 1; + j += 1; + } + } + if ret.kind == PQ { + ret.curve = 8 * len; + } + if ret.kind == RSA { + // Key is (modulus,exponent) - assume exponent is 65537 + len = getalen(SEQ, c, j); + if len == 0 { + ret.kind = 0; + return ret; + } + j += skip(len); + + len = getalen(INT, c, j); + if len == 0 { + ret.kind = 0; + return ret; + } + j += skip(len); + if c[j] == 0 { + j += 1; + len -= 1; + } + if len > keylen { + ret.kind = 0; + return ret; + } + ret.len = len; + fin = j + len; + slen = 0; + while j < fin { + key[slen] = c[j]; + slen += 1; + j += 1; + } + ret.curve = 8 * len; + } + ret +} + +// Extract Public Key from inside Certificate +pub fn extract_public_key(c: &[u8], key: &mut [u8]) -> PKTYPE { + let mut ptr = 0; + let pklen = find_public_key(c, &mut ptr); // ptr is pointer into certificate, at start of ASN.1 raw public key + let cc = &c[ptr..ptr + pklen]; + get_public_key(&cc, key) +} + +pub fn find_issuer(c: &[u8]) -> FDTYPE { + let mut j: usize = 0; + let mut ret = FDTYPE::new(); + let mut len = getalen(SEQ, c, j); + if len == 0 { + return ret; + } + j += skip(len); + + if len + j != c.len() { + return ret; + } + + len = getalen(ANY, c, j); + if len == 0 { + return ret; + } + j += skip(len) + len; // jump over version clause + + len = getalen(INT, c, j); + if len > 0 { + j += skip(len) + len; // jump over serial number clause (if there is one) + } + + len = getalen(SEQ, c, j); + if len == 0 { + return ret; + } + j += skip(len) + len; // jump over signature algorithm + + len = getalen(SEQ, c, j); + ret.index = j; + ret.length = len + skip(len); + + ret +} + +pub fn find_validity(c: &[u8]) -> usize { + let pos = find_issuer(c); + let j = pos.index + pos.length; // skip issuer + + //let mut j=find_issuer(c); + //let len=getalen(SEQ,c,j); + //if len==0 { + // return 0; + //} + //j+=skip(len)+len; // skip issuer + j +} + +pub fn find_subject(c: &[u8]) -> FDTYPE { + let mut j = find_validity(c); + let mut ret = FDTYPE::new(); + let mut len = getalen(SEQ, c, j); + if len == 0 { + return ret; + } + j += skip(len) + len; // skip validity + + len = getalen(SEQ, c, j); + ret.index = j; + ret.length = len + skip(len); + + ret +} + +pub fn self_signed(c: &[u8]) -> bool { + let ksub = find_subject(c); + let kiss = find_issuer(c); + + if ksub.length != kiss.length { + return false; + } + + // let sublen=getalen(SEQ,c,ksub); + // let isslen=getalen(SEQ,c,kiss); + // if sublen != isslen { + // return false; + // } + // ksub+=skip(sublen); + // kiss+=skip(isslen); + let mut m: u8 = 0; + for i in 0..ksub.length { + m |= c[i + ksub.index] - c[i + kiss.index]; + } + if m != 0 { + return false; + } + true +} + +// NOTE: When extracting cert information, we actually return just an index to the data inside the cert, and maybe its length +// So no memory is assigned to store cert info. It is the callers responsibility to allocate such memory if required, and copy +// cert information into it. + +// Find entity property indicated by SOID, given start of issuer or subject field. Return index in cert, flen=length of field + +pub fn find_entity_property(c: &[u8], soid: &[u8], start: usize) -> FDTYPE { + let mut ret = FDTYPE::new(); + let mut foid: [u8; 32] = [0; 32]; + let mut j = start; + let tlen = getalen(SEQ, c, j); + if tlen == 0 { + return ret; + } + j += skip(tlen); + let k = j; + while j < k + tlen { + let mut len = getalen(SET, c, j); + if len == 0 { + return ret; + } + j += skip(len); + len = getalen(SEQ, c, j); + if len == 0 { + return ret; + } + j += skip(len); + len = getalen(OID, c, j); + if len == 0 { + return ret; + } + j += skip(len); + let fin = j + len; + if len > foid.len() { + return ret; + } + let mut flen: usize = 0; + while j < fin { + foid[flen] = c[j]; + flen += 1; + j += 1; + } + len = getalen(ANY, c, j); // get text, could be any type + if len == 0 { + return ret; + } + j += skip(len); + if foid[0..flen] == *soid { + ret.index = j; // if its the right one.. + ret.length = len; + return ret; + } + j += len; // skip over it + } + ret +} + +pub fn find_start_date(c: &[u8], start: usize) -> usize { + let mut j = start; + let mut len = getalen(SEQ, c, j); + if len == 0 { + return 0; + } + j += skip(len); + + len = getalen(UTC, c, j); + if len == 0 { + // could be generalised time + len = getalen(GTM, c, j); + if len == 0 { + return 0; + } + j += skip(len); + j += 2; // skip century + } else { + j += skip(len); + } + + j +} + +pub fn find_expiry_date(c: &[u8], start: usize) -> usize { + let mut j = start; + let mut len = getalen(SEQ, c, j); + if len == 0 { + return 0; + } + j += skip(len); + + len = getalen(UTC, c, j); + if len == 0 { + len = getalen(GTM, c, j); + if len == 0 { + return 0; + } + } + j += skip(len) + len; + + len = getalen(UTC, c, j); + if len == 0 { + // could be generalised time + len = getalen(GTM, c, j); + if len == 0 { + return 0; + } + j += skip(len); + j += 2; // skip century + } else { + j += skip(len); + } + + j +} + +pub fn find_extensions(c: &[u8]) -> usize { + let pos = find_subject(c); + let mut j = pos.index + pos.length; + + // let mut len=getalen(SEQ,c,j); + // if len==0 { + // return 0; + // } + // j+=skip(len)+len; // skip subject + + let len = getalen(SEQ, c, j); + if len == 0 { + return 0; + } + j += skip(len) + len; // skip public key + + if j >= c.len() { + return 0; + } + + j +} + +pub fn find_extension(c: &[u8], soid: &[u8], start: usize) -> FDTYPE { + let mut ret = FDTYPE::new(); + let mut foid: [u8; 32] = [0; 32]; + + let mut j = start; + let tlen = getalen(EXT, c, j); + if tlen == 0 { + return ret; + } + j += skip(tlen); + + let tlen = getalen(SEQ, c, j); + if tlen == 0 { + return ret; + } + j += skip(tlen); + + let k = j; + while j < k + tlen { + let mut len = getalen(SEQ, c, j); + if len == 0 { + return ret; + } + j += skip(len); + let nj = j + len; + len = getalen(OID, c, j); + j += skip(len); + let fin = j + len; + if len > foid.len() { + return ret; + } + let mut flen: usize = 0; + while j < fin { + foid[flen] = c[j]; + flen += 1; + j += 1; + } + if foid[0..flen] == *soid { + ret.index = j; // if its the right one.. + ret.length = nj - j; + return ret; + } + j = nj; // skip over this extension + } + + ret +} + +// return 1 if name found, else 0, where name is URL +// input cert, and pointer to SAN extension +// Takes wild-card into consideration + +pub fn find_alt_name(c: &[u8], start: usize, name: &[u8]) -> bool { + if start == 0 { + return false; + } + let mut j = start; + let mut tlen = getalen(OCT, c, j); + if tlen == 0 { + return false; + } + j += skip(tlen); + + tlen = getalen(SEQ, c, j); + if tlen == 0 { + return false; + } + j += skip(tlen); + let k = j; + while j < k + tlen { + let tag = c[j]; + let mut len = getalen(ANY, c, j); + if len == 0 { + return false; + } + j += skip(len); // ?? If its not dns, skip over it j+=len + if tag != DNS { + // only interested in URLs + j += len; + continue; + } + let mut cmp = true; + let mut m = 0; + let nlen = name.len(); + if c[j] == b'*' { + j += 1; + len -= 1; // skip over * + while m < nlen { + // advance to first . + if name[m] == b'.' { + break; + } + m += 1; + } + } + for _ in 0..len { + if m == nlen { + // name has ended before comparison completed + cmp = false; + j += 1; + continue; + } + if c[j] != name[m] { + cmp = false; + } + m += 1; + j += 1; + } + if m != nlen { + cmp = false; + } + if cmp { + return true; + } + } + + false +} diff --git a/third_party/rust/document_extractor_cxx/v0_1/README.chromium b/third_party/rust/document_extractor_cxx/v0_1/README.chromium new file mode 100644 index 000000000000..12c09b35e965 --- /dev/null +++ b/third_party/rust/document_extractor_cxx/v0_1/README.chromium @@ -0,0 +1,8 @@ +Name: document-extractor-cxx +URL: https://crates.io/crates/document-extractor-cxx +Description: +Version: 0.1.0 +Security Critical: yes +Shipped: yes +License: Mozilla Public License 2.0 +License File: