package net.i2p.crypto;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.data.Hash;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SimpleDataStructure;
import net.i2p.util.SystemVersion;
/**
* Defines the properties for various signature types
* that I2P supports or may someday support.
*
* All Signatures, SigningPublicKeys, and SigningPrivateKeys have a type.
* Note that a SigType specifies both an algorithm and parameters, so that
* we may change primes or curves for a given algorithm.
*
* @since 0.9.8
*/
public enum SigType {
/**
* DSA_SHA1 is the default.
* Pubkey 128 bytes; privkey 20 bytes; hash 20 bytes; sig 40 bytes
* @since 0.9.8
*/
DSA_SHA1(0, 128, 20, 20, 40, SigAlgo.DSA, "SHA-1", "SHA1withDSA", CryptoConstants.DSA_SHA1_SPEC, "1.2.840.10040.4.3", "0"),
/** Pubkey 64 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes */
ECDSA_SHA256_P256(1, 64, 32, 32, 64, SigAlgo.EC, "SHA-256", "SHA256withECDSA", ECConstants.P256_SPEC, "1.2.840.10045.4.3.2", "0.9.12"),
/** Pubkey 96 bytes; privkey 48 bytes; hash 48 bytes; sig 96 bytes */
ECDSA_SHA384_P384(2, 96, 48, 48, 96, SigAlgo.EC, "SHA-384", "SHA384withECDSA", ECConstants.P384_SPEC, "1.2.840.10045.4.3.3", "0.9.12"),
/** Pubkey 132 bytes; privkey 66 bytes; hash 64 bytes; sig 132 bytes */
ECDSA_SHA512_P521(3, 132, 66, 64, 132, SigAlgo.EC, "SHA-512", "SHA512withECDSA", ECConstants.P521_SPEC, "1.2.840.10045.4.3.4", "0.9.12"),
/** Pubkey 256 bytes; privkey 512 bytes; hash 32 bytes; sig 256 bytes */
RSA_SHA256_2048(4, 256, 512, 32, 256, SigAlgo.RSA, "SHA-256", "SHA256withRSA", RSAConstants.F4_2048_SPEC, "1.2.840.113549.1.1.11", "0.9.12"),
/** Pubkey 384 bytes; privkey 768 bytes; hash 48 bytes; sig 384 bytes */
RSA_SHA384_3072(5, 384, 768, 48, 384, SigAlgo.RSA, "SHA-384", "SHA384withRSA", RSAConstants.F4_3072_SPEC, "1.2.840.113549.1.1.12", "0.9.12"),
/** Pubkey 512 bytes; privkey 1024 bytes; hash 64 bytes; sig 512 bytes */
RSA_SHA512_4096(6, 512, 1024, 64, 512, SigAlgo.RSA, "SHA-512", "SHA512withRSA", RSAConstants.F4_4096_SPEC, "1.2.840.113549.1.1.13", "0.9.12"),
/**
* Pubkey 32 bytes; privkey 32 bytes; hash 64 bytes; sig 64 bytes
*
* Due to bugs in previous versions, minimum version is 0.9.17.
*
* @since 0.9.15
*/
EdDSA_SHA512_Ed25519(7, 32, 32, 64, 64, SigAlgo.EdDSA, "SHA-512", "SHA512withEdDSA",
EdDSANamedCurveTable.getByName("ed25519-sha-512"), "1.3.101.101", "0.9.17"),
/**
* Prehash version (double hashing, for offline use such as su3, not for use on the network)
* Pubkey 32 bytes; privkey 32 bytes; hash 64 bytes; sig 64 bytes
* @since 0.9.25
*/
EdDSA_SHA512_Ed25519ph(8, 32, 32, 64, 64, SigAlgo.EdDSA, "SHA-512", "NonewithEdDSA",
EdDSANamedCurveTable.getByName("ed25519-sha-512"), "1.3.101.101", "0.9.25"),
;
// TESTING....................
// others..........
// EC mix and match
//ECDSA_SHA256_P192(5, 48, 24, 32, 48, SigAlgo.EC, "SHA-256", "SHA256withECDSA", ECConstants.P192_SPEC),
//ECDSA_SHA256_P384(6, 96, 48, 32, 96, SigAlgo.EC, "SHA-256", "SHA256withECDSA", ECConstants.P384_SPEC),
//ECDSA_SHA256_P521(7, 132, 66, 32, 132, SigAlgo.EC, "SHA-256", "SHA256withECDSA", ECConstants.P521_SPEC),
//ECDSA_SHA384_P256(8, 64, 32, 48, 64, SigAlgo.EC, "SHA-384", "SHA384withECDSA", ECConstants.P256_SPEC),
//ECDSA_SHA384_P521(9, 132, 66, 48, 132, SigAlgo.EC, "SHA-384", "SHA384withECDSA", ECConstants.P521_SPEC),
//ECDSA_SHA512_P256(10, 64, 32, 64, 64, SigAlgo.EC, "SHA-512", "SHA512withECDSA", ECConstants.P256_SPEC),
//ECDSA_SHA512_P384(11, 96, 48, 64, 96, SigAlgo.EC, "SHA-512", "SHA512withECDSA", ECConstants.P384_SPEC),
// Koblitz
//ECDSA_SHA256_K163(12, 42, 21, 32, 42, SigAlgo.EC, "SHA-256", "SHA256withECDSA", ECConstants.K163_SPEC),
//ECDSA_SHA256_K233(13, 60, 30, 32, 60, SigAlgo.EC, "SHA-256", "SHA256withECDSA", ECConstants.K233_SPEC),
//ECDSA_SHA256_K283(14, 72, 36, 32, 72, SigAlgo.EC, "SHA-256", "SHA256withECDSA", ECConstants.K283_SPEC),
//ECDSA_SHA256_K409(15, 104, 52, 32, 104, SigAlgo.EC, "SHA-256", "SHA256withECDSA", ECConstants.K409_SPEC),
//ECDSA_SHA256_K571(16, 144, 72, 32, 144, SigAlgo.EC, "SHA-256", "SHA256withECDSA", ECConstants.K571_SPEC),
// too short..............
/** Pubkey 48 bytes; privkey 24 bytes; hash 20 bytes; sig 48 bytes */
//ECDSA_SHA1_P192(1, 48, 24, 20, 48, SigAlgo.EC, "SHA-1", "SHA1withECDSA", ECConstants.P192_SPEC),
//RSA_SHA1(17, 128, 256, 20, 128, SigAlgo.RSA, "SHA-1", "SHA1withRSA", RSAConstants.F4_1024_SPEC),
//MD5
//RSA_SHA1
//ELGAMAL_SHA256
//DSA_2048_224(2, 256, 28, 32, 56, "SHA-256"),
// Nonstandard, used by Syndie.
// Pubkey 128 bytes; privkey 20 bytes; hash 32 bytes; sig 40 bytes
//DSA_1024_160_SHA256(1, 128, 20, 32, 40, "SHA-256", "?"),
// Pubkey 256 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes
//DSA_2048_256(2, 256, 32, 32, 64, "SHA-256", "?"),
// Pubkey 384 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes
//DSA_3072_256(3, 384, 32, 32, 64, "SHA-256", "?"),
private final int code, pubkeyLen, privkeyLen, hashLen, sigLen;
private final SigAlgo base;
private final String digestName, algoName, oid, since;
private final AlgorithmParameterSpec params;
private final boolean isAvail;
SigType(int cod, int pubLen, int privLen, int hLen, int sLen, SigAlgo baseAlgo,
String mdName, String aName, AlgorithmParameterSpec pSpec, String oid, String supportedSince) {
code = cod;
pubkeyLen = pubLen;
privkeyLen = privLen;
hashLen = hLen;
sigLen = sLen;
base = baseAlgo;
digestName = mdName;
algoName = aName;
params = pSpec;
this.oid = oid;
since = supportedSince;
isAvail = x_isAvailable();
}
/** the unique identifier for this type */
public int getCode() { return code; }
/** the length of the public key, in bytes */
public int getPubkeyLen() { return pubkeyLen; }
/** the length of the private key, in bytes */
public int getPrivkeyLen() { return privkeyLen; }
/** the length of the hash, in bytes */
public int getHashLen() { return hashLen; }
/** the length of the signature, in bytes */
public int getSigLen() { return sigLen; }
/** the standard base algorithm name used for the Java crypto factories */
public SigAlgo getBaseAlgorithm() { return base; }
/** the standard name used for the Java crypto factories */
public String getAlgorithmName() { return algoName; }
/**
* The elliptic curve ECParameterSpec for ECDSA; DSAParameterSpec for DSA
* @throws InvalidParameterSpecException if the algorithm is not available on this JVM.
*/
public AlgorithmParameterSpec getParams() throws InvalidParameterSpecException {
if (params == null)
throw new InvalidParameterSpecException(toString() + " is not available in this JVM");
return params;
}
/** @throws UnsupportedOperationException if not supported */
public MessageDigest getDigestInstance() {
if (digestName.equals("SHA-1"))
return SHA1.getInstance();
if (digestName.equals("SHA-256"))
return SHA256Generator.getDigestInstance();
try {
return MessageDigest.getInstance(digestName);
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException(e);
}
}
/**
* @since 0.9.9
* @throws UnsupportedOperationException if not supported
*/
public SimpleDataStructure getHashInstance() {
switch (getHashLen()) {
case 20:
return new SHA1Hash();
case 32:
return new Hash();
case 48:
return new Hash384();
case 64:
return new Hash512();
default:
throw new UnsupportedOperationException("Unsupported hash length: " + getHashLen());
}
}
/**
* The router version in which this type was first supported.
*
* @since 0.9.15
*/
public String getSupportedSince() {
return since;
}
/**
* The OID for the signature.
*
* @since 0.9.25
*/
public String getOID() {
return oid;
}
/**
* @since 0.9.12
* @return true if supported in this JVM
*/
public boolean isAvailable() {
return isAvail;
}
private boolean x_isAvailable() {
if (DSA_SHA1 == this)
return true;
try {
getParams();
if (getBaseAlgorithm() != SigAlgo.EdDSA) {
Signature jsig = Signature.getInstance(getAlgorithmName());
if (getBaseAlgorithm() == SigAlgo.EC && SystemVersion.isGentoo() ) {
// Do a full keygen/sign test on Gentoo, because it lies. Keygen works but sigs fail.
// https://bugs.gentoo.org/show_bug.cgi?id=528338
// http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=2497
// http://zzz.i2p/topics/1931
// Be sure nothing in the code paths below calls isAvailable()
// get an I2P keypair
SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(this);
SigningPrivateKey privKey = (SigningPrivateKey) keys[1];
// convert privkey back to Java key and sign
jsig.initSign(SigUtil.toJavaECKey(privKey));
// use the pubkey as random data
jsig.update(keys[0].getData());
jsig.sign();
}
}
getDigestInstance();
getHashInstance();
} catch (GeneralSecurityException e) {
return false;
} catch (RuntimeException e) {
return false;
}
return true;
}
/**
* @return true if supported in this JVM
* @since 0.9.15
*/
public static boolean isAvailable(int code) {
SigType type = getByCode(code);
if (type == null)
return false;
return type.isAvailable();
}
/**
* @param stype number or name
* @return true if supported in this JVM
* @since 0.9.15
*/
public static boolean isAvailable(String stype) {
SigType type = parseSigType(stype);
if (type == null)
return false;
return type.isAvailable();
}
private static final Map<Integer, SigType> BY_CODE = new HashMap<Integer, SigType>();
static {
for (SigType type : SigType.values()) {
if (BY_CODE.put(Integer.valueOf(type.getCode()), type) != null)
throw new IllegalStateException("Duplicate SigType code");
}
}
/** @return null if not supported */
public static SigType getByCode(int code) {
return BY_CODE.get(Integer.valueOf(code));
}
/**
* Convenience for user apps
*
* @param stype number or name
* @return null if not found
* @since 0.9.9 moved from SU3File in 0.9.12
*/
public static SigType parseSigType(String stype) {
try {
String uc = stype.toUpperCase(Locale.US);
// handle mixed-case enum
if (uc.equals("EDDSA_SHA512_ED25519"))
return EdDSA_SHA512_Ed25519;
if (uc.equals("EDDSA_SHA512_ED25519PH"))
return EdDSA_SHA512_Ed25519ph;
return valueOf(uc);
} catch (IllegalArgumentException iae) {
try {
int code = Integer.parseInt(stype);
return getByCode(code);
} catch (NumberFormatException nfe) {
return null;
}
}
}
}