From 65bd953d6528a51786964bdbffd79ca403784c1c Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 4 Aug 2023 18:14:09 +0200 Subject: [PATCH] Kotlin conversion: SymmetricKeyAlgorithmNegotiator --- .../SymmetricKeyAlgorithmNegotiator.java | 105 ------------------ .../SymmetricKeyAlgorithmNegotiator.kt | 77 +++++++++++++ 2 files changed, 77 insertions(+), 105 deletions(-) delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.java create mode 100644 pgpainless-core/src/main/java/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.kt diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.java b/pgpainless-core/src/main/java/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.java deleted file mode 100644 index de8ced24..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.java +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.algorithm.negotiation; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.pgpainless.algorithm.SymmetricKeyAlgorithm; -import org.pgpainless.policy.Policy; - -/** - * Interface for symmetric key algorithm negotiation. - */ -public interface SymmetricKeyAlgorithmNegotiator { - - /** - * Negotiate a symmetric encryption algorithm. - * If the override is non-null, it will be returned instead of performing an actual negotiation. - * Otherwise, the list of ordered sets containing the preferences of different recipient keys will be - * used to determine a suitable symmetric encryption algorithm. - * - * @param policy algorithm policy - * @param override algorithm override (if not null, return this) - * @param keyPreferences list of preferences per key - * @return negotiated algorithm - */ - SymmetricKeyAlgorithm negotiate( - Policy.SymmetricKeyAlgorithmPolicy policy, - SymmetricKeyAlgorithm override, - List> keyPreferences); - - /** - * Return an instance that negotiates a {@link SymmetricKeyAlgorithm} by selecting the most popular acceptable - * algorithm from the list of preferences. - * - * This negotiator has the best chances to select an algorithm which is understood by all recipients. - * - * @return negotiator that selects by popularity - */ - static SymmetricKeyAlgorithmNegotiator byPopularity() { - return new SymmetricKeyAlgorithmNegotiator() { - @Override - public SymmetricKeyAlgorithm negotiate( - Policy.SymmetricKeyAlgorithmPolicy policy, - SymmetricKeyAlgorithm override, - List> preferences) { - if (override == SymmetricKeyAlgorithm.NULL) { - throw new IllegalArgumentException("Algorithm override cannot be NULL (plaintext)."); - } - - if (override != null) { - return override; - } - - // Count score (occurrences) of each algorithm - Map supportWeight = new LinkedHashMap<>(); - for (Set keyPreferences : preferences) { - for (SymmetricKeyAlgorithm preferred : keyPreferences) { - if (supportWeight.containsKey(preferred)) { - supportWeight.put(preferred, supportWeight.get(preferred) + 1); - } else { - supportWeight.put(preferred, 1); - } - } - } - - // Pivot the score map - Map> byScore = new HashMap<>(); - for (SymmetricKeyAlgorithm algorithm : supportWeight.keySet()) { - int score = supportWeight.get(algorithm); - List withSameScore = byScore.get(score); - if (withSameScore == null) { - withSameScore = new ArrayList<>(); - byScore.put(score, withSameScore); - } - withSameScore.add(algorithm); - } - - List scores = new ArrayList<>(byScore.keySet()); - - // Sort map and iterate from highest to lowest score - Collections.sort(scores); - for (int i = scores.size() - 1; i >= 0; i--) { - int score = scores.get(i); - List withSameScore = byScore.get(score); - // Select best algorithm - SymmetricKeyAlgorithm best = policy.selectBest(withSameScore); - if (best != null) { - return best; - } - } - - // If no algorithm is acceptable, choose fallback - return policy.getDefaultSymmetricKeyAlgorithm(); - } - }; - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.kt b/pgpainless-core/src/main/java/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.kt new file mode 100644 index 00000000..1a6dfe7f --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.kt @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.algorithm.negotiation + +import org.pgpainless.algorithm.SymmetricKeyAlgorithm +import org.pgpainless.policy.Policy +import java.lang.IllegalArgumentException + +interface SymmetricKeyAlgorithmNegotiator { + + /** + * Negotiate a symmetric encryption algorithm. + * If the override is non-null, it will be returned instead of performing an actual negotiation. + * Otherwise, the list of ordered sets containing the preferences of different recipient keys will be + * used to determine a suitable symmetric encryption algorithm. + * + * @param policy algorithm policy + * @param override algorithm override (if not null, return this) + * @param keyPreferences list of preferences per key + * @return negotiated algorithm + */ + fun negotiate(policy: Policy.SymmetricKeyAlgorithmPolicy, + override: SymmetricKeyAlgorithm?, + keyPreferences: List>): SymmetricKeyAlgorithm + + companion object { + @JvmStatic + fun byPopularity(): SymmetricKeyAlgorithmNegotiator { + return object: SymmetricKeyAlgorithmNegotiator { + override fun negotiate( + policy: Policy.SymmetricKeyAlgorithmPolicy, + override: SymmetricKeyAlgorithm?, + keyPreferences: List>): + SymmetricKeyAlgorithm { + if (override == SymmetricKeyAlgorithm.NULL) { + throw IllegalArgumentException("Algorithm override cannot be NULL (plaintext).") + } + + if (override != null) { + return override + } + + // algorithm to #occurrences + val supportWeight = buildMap { + keyPreferences.forEach { keyPreference -> + keyPreference.forEach { pref -> + put(pref, getOrDefault(pref, 0) as Int + 1) + } + } + } + + // Pivot map and sort by popularity ascending + // score to list(algo) + val byScore = supportWeight.toList() + .map { e -> e.second to e.first } + .groupBy { e -> e.first } + .map { e -> e.key to e.value.map { it.second }.toList() } + .associate { e -> e } + .toSortedMap() + + // iterate in reverse over algorithms + for (e in byScore.entries.reversed()) { + val best = policy.selectBest(e.value) + if (best != null) { + return best + } + } + + return policy.defaultSymmetricKeyAlgorithm + } + + } + } + } +} \ No newline at end of file