org.apache.sshd.common.signature.SignatureFactory Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.sshd.common.signature;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.apache.sshd.common.BuiltinFactory;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.util.GenericUtils;
/**
* @author Apache MINA SSHD Project
*/
public interface SignatureFactory extends BuiltinFactory {
/**
* ECC signature types in ascending order of preference (i.e., most preferred 1st)
*/
List ECC_SIGNATURE_TYPE_PREFERENCES =
Collections.unmodifiableList(
Arrays.asList(
KeyPairProvider.ECDSA_SHA2_NISTP521,
KeyPairProvider.ECDSA_SHA2_NISTP384,
KeyPairProvider.ECDSA_SHA2_NISTP256));
/**
* RSA signature types in ascending order of preference (i.e., most preferred 1st)
*/
List RSA_SIGNATURE_TYPE_PREFERENCES =
Collections.unmodifiableList(
Arrays.asList(
KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS,
KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS,
KeyPairProvider.SSH_RSA));
/**
* @param provided The provided signature key types
* @param factories The available signature factories
* @return A {@link List} of the matching available factories names
* that are also listed as provided ones - in the same order
* of preference as they appear in the available listing. May be
* empty if no provided signature key types, or no available ones
* or no match found.
* @see #resolveSignatureFactoryNamesProposal(Iterable, Collection)
*/
static List resolveSignatureFactoriesProposal(
Iterable provided, Collection> factories) {
return resolveSignatureFactoryNamesProposal(provided, NamedResource.getNameList(factories));
}
/**
* @param provided The provided signature key types
* @param available The available signature factories names
* @return A {@link List} of the matching available factories names
* that are also listed as provided ones - in the same order
* of preference as they appear in the available listing. May be
* empty if no provided signature key types, or no available ones
* or no match found.
*/
static List resolveSignatureFactoryNamesProposal(
Iterable provided, Collection available) {
if ((provided == null) || GenericUtils.isEmpty(available)) {
return Collections.emptyList();
}
// We want to preserve the original available order as it indicates the preference
Set providedKeys = new HashSet<>();
for (String providedType : provided) {
Collection equivTypes =
KeyUtils.getAllEquivalentKeyTypes(providedType);
providedKeys.addAll(equivTypes);
}
if (GenericUtils.isEmpty(providedKeys)) {
return Collections.emptyList();
}
List supported = new ArrayList<>(available);
for (int index = 0; index < supported.size(); index++) {
String kt = supported.get(index);
if (!providedKeys.contains(kt)) {
supported.remove(index);
index--; // compensate for auto-increment
}
}
return supported;
}
// returns -1 or > size() if append to end
static int resolvePreferredSignaturePosition(
List> factories, NamedFactory factory) {
if (GenericUtils.isEmpty(factories)) {
return -1; // just add it to the end
}
String name = factory.getName();
if (KeyPairProvider.SSH_RSA.equalsIgnoreCase(name)) {
return -1;
}
int pos = RSA_SIGNATURE_TYPE_PREFERENCES.indexOf(name);
if (pos >= 0) {
Map posMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (int index = 0, count = factories.size(); index < count; index++) {
NamedFactory f = factories.get(index);
String keyType = f.getName();
String canonicalName = KeyUtils.getCanonicalKeyType(keyType);
if (!KeyPairProvider.SSH_RSA.equalsIgnoreCase(canonicalName)) {
continue; // debug breakpoint
}
posMap.put(keyType, index);
}
return resolvePreferredSignaturePosition(RSA_SIGNATURE_TYPE_PREFERENCES, pos, posMap);
}
pos = ECC_SIGNATURE_TYPE_PREFERENCES.indexOf(name);
if (pos >= 0) {
Map posMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (int index = 0, count = factories.size(); index < count; index++) {
NamedFactory f = factories.get(index);
String keyType = f.getName();
if (!ECC_SIGNATURE_TYPE_PREFERENCES.contains(keyType)) {
continue; // debug breakpoint
}
posMap.put(keyType, index);
}
return resolvePreferredSignaturePosition(ECC_SIGNATURE_TYPE_PREFERENCES, pos, posMap);
}
return -1; // no special preference - stick it as last
}
static int resolvePreferredSignaturePosition(
List preferredOrder, int prefValue, Map posMap) {
if (GenericUtils.isEmpty(preferredOrder) || (prefValue < 0) || GenericUtils.isEmpty(posMap)) {
return -1;
}
int posValue = -1;
for (Map.Entry pe : posMap.entrySet()) {
String name = pe.getKey();
int order = preferredOrder.indexOf(name);
if (order < 0) {
continue; // should not happen, but tolerate
}
Integer curIndex = pe.getValue();
int resIndex;
if (order < prefValue) {
resIndex = curIndex.intValue() + 1;
} else if (order > prefValue) {
resIndex = curIndex.intValue(); // by using same index we insert in front of it in effect
} else {
continue; // should not happen, but tolerate
}
// Preferred factories should be as close as possible to the beginning of the list
if ((posValue < 0) || (resIndex < posValue)) {
posValue = resIndex;
}
}
return posValue;
}
}