Kotlin conversion: SymmetricKeyAlgorithmNegotiator

This commit is contained in:
Paul Schaub 2023-08-04 18:14:09 +02:00
parent fb235d7810
commit cf7a7f3aca
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
2 changed files with 77 additions and 105 deletions

View File

@ -1,105 +0,0 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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<Set<SymmetricKeyAlgorithm>> 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<Set<SymmetricKeyAlgorithm>> 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<SymmetricKeyAlgorithm, Integer> supportWeight = new LinkedHashMap<>();
for (Set<SymmetricKeyAlgorithm> 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<Integer, List<SymmetricKeyAlgorithm>> byScore = new HashMap<>();
for (SymmetricKeyAlgorithm algorithm : supportWeight.keySet()) {
int score = supportWeight.get(algorithm);
List<SymmetricKeyAlgorithm> withSameScore = byScore.get(score);
if (withSameScore == null) {
withSameScore = new ArrayList<>();
byScore.put(score, withSameScore);
}
withSameScore.add(algorithm);
}
List<Integer> 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<SymmetricKeyAlgorithm> 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();
}
};
}
}

View File

@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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<Set<SymmetricKeyAlgorithm>>): SymmetricKeyAlgorithm
companion object {
@JvmStatic
fun byPopularity(): SymmetricKeyAlgorithmNegotiator {
return object: SymmetricKeyAlgorithmNegotiator {
override fun negotiate(
policy: Policy.SymmetricKeyAlgorithmPolicy,
override: SymmetricKeyAlgorithm?,
keyPreferences: List<Set<SymmetricKeyAlgorithm>>):
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
}
}
}
}
}