Router: Checks for new enc types

- Prevent encrypted lookups or stores
- Prevent participting in our tunnels
- Handle padding
- Checks in crypto classes
This commit is contained in:
zzz
2019-07-23 13:29:37 +00:00
parent 885e0468b2
commit e2980603b7
11 changed files with 204 additions and 17 deletions

View File

@ -119,6 +119,9 @@ public final class ElGamalEngine {
if ((data == null) || (data.length > ELG_CLEARTEXT_LENGTH))
throw new IllegalArgumentException("Data to encrypt must be <= 222 bytes");
if (publicKey == null) throw new IllegalArgumentException("Null public key specified");
EncType type = publicKey.getType();
if (type != EncType.ELGAMAL_2048)
throw new IllegalArgumentException("Bad public key type " + type);
long start = _context.clock().now();
@ -193,6 +196,9 @@ public final class ElGamalEngine {
* @return unencrypted data or null on failure
*/
public byte[] decrypt(byte encrypted[], PrivateKey privateKey) {
EncType type = privateKey.getType();
if (type != EncType.ELGAMAL_2048)
throw new IllegalArgumentException("Bad private key type " + type);
if ((encrypted == null) || (encrypted.length != ELG_ENCRYPTED_LENGTH))
throw new IllegalArgumentException("Data to decrypt must be exactly ELG_ENCRYPTED_LENGTH bytes");
long start = _context.clock().now();

View File

@ -11,7 +11,7 @@ import net.i2p.data.Hash;
import net.i2p.data.SimpleDataStructure;
/**
* PRELIMINARY - unused - subject to change
* PRELIMINARY - subject to change
*
* Defines the properties for various encryption types
* that I2P supports or may someday support.
@ -33,18 +33,21 @@ public enum EncType {
/**
* Used by i2pd. Not yet supported by Java I2P.
* Pubkey 64 bytes; privkey 32 bytes.
* See proposal 145.
*/
EC_P256(1, 64, 32, EncAlgo.EC, "EC/None/NoPadding", ECConstants.P256_SPEC, "0.9.38"),
/**
* Reserved, not used by anybody.
* Pubkey 96 bytes; privkey 48 bytes.
* See proposal 145.
*/
EC_P384(2, 96, 48, EncAlgo.EC, "EC/None/NoPadding", ECConstants.P384_SPEC, "0.9.38"),
/**
* Reserved, not used by anybody.
* Pubkey 132 bytes; privkey 66 bytes.
* See proposal 145.
*/
EC_P521(3, 132, 66, EncAlgo.EC, "EC/None/NoPadding", ECConstants.P521_SPEC, "0.9.38"),
@ -56,8 +59,6 @@ public enum EncType {
ECIES_X25519(4, 32, 32, EncAlgo.ECIES, "EC/None/NoPadding", X25519_SPEC, "0.9.38");
private final int code, pubkeyLen, privkeyLen;
private final EncAlgo base;
private final String algoName, since;
@ -71,6 +72,8 @@ public enum EncType {
*/
EncType(int cod, int pubLen, int privLen, EncAlgo baseAlgo,
String transformation, AlgorithmParameterSpec pSpec, String supportedSince) {
if (pubLen > 256)
throw new IllegalArgumentException("fixup PublicKey for longer keys");
code = cod;
pubkeyLen = pubLen;
privkeyLen = privLen;

View File

@ -60,8 +60,12 @@ public class Destination extends KeysAndCert {
byte[] padding;
if (c.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
// convert SPK to new SPK and padding
// EncTypes 1-3 allowed in Destinations, see proposal 145
KeyCertificate kcert = c.toKeyCertificate();
padding = sk.getPadding(kcert);
byte[] pad1 = pk.getPadding(kcert);
byte[] pad2 = sk.getPadding(kcert);
padding = combinePadding(pad1, pad2);
pk = pk.toTypedKey(kcert);
sk = sk.toTypedKey(kcert);
c = kcert;
} else {
@ -70,7 +74,8 @@ public class Destination extends KeysAndCert {
Destination rv;
synchronized(_cache) {
rv = _cache.get(sk);
if (rv != null && rv.getPublicKey().equals(pk) && rv.getCertificate().equals(c)) {
if (rv != null && rv.getPublicKey().equals(pk) && rv.getCertificate().equals(c) &&
DataHelper.eq(rv.getPadding(), padding)) {
//if (STATS)
// I2PAppContext.getGlobalContext().statManager().addRateData("DestCache", 1);
return rv;
@ -97,6 +102,11 @@ public class Destination extends KeysAndCert {
* @since 0.9.9
*/
private Destination(PublicKey pk, SigningPublicKey sk, Certificate c, byte[] padding) {
if (padding != null) {
int sz = pk.length() + sk.length() + padding.length;
if (sz != 384)
throw new IllegalArgumentException("bad total length " + sz);
}
_publicKey = pk;
_signingKey = sk;
_certificate = c;

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.crypto.EncType;
import net.i2p.crypto.SigType;
/**
@ -147,6 +148,14 @@ public class KeyCertificate extends Certificate {
return SigType.getByCode(getSigTypeCode());
}
/**
* @return null if unset or unknown
* @since 0.9.42
*/
public EncType getEncType() {
return EncType.getByCode(getCryptoTypeCode());
}
/**
* Signing Key extra data, if any, is first in the array.
* Crypto Key extra data, if any, is second in the array,

View File

@ -15,6 +15,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import net.i2p.crypto.EncType;
import net.i2p.crypto.SHA256Generator;
import net.i2p.crypto.SigType;
@ -68,6 +69,22 @@ public class KeysAndCert extends DataStructureImpl {
return SigType.DSA_SHA1;
}
/**
* @return null if not set or unknown
* @since 0.9.42
*/
public EncType getEncType() {
if (_certificate == null)
return null;
if (_certificate.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
try {
KeyCertificate kcert = _certificate.toKeyCertificate();
return kcert.getEncType();
} catch (DataFormatException dfe) {}
}
return EncType.ELGAMAL_2048;
}
public PublicKey getPublicKey() {
return _publicKey;
}
@ -117,28 +134,48 @@ public class KeysAndCert extends DataStructureImpl {
public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_publicKey != null || _signingKey != null || _certificate != null)
throw new IllegalStateException();
_publicKey = PublicKey.create(in);
PublicKey pk = PublicKey.create(in);
SigningPublicKey spk = SigningPublicKey.create(in);
Certificate cert = Certificate.create(in);
Certificate cert = Certificate.create(in);
if (cert.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
// convert SPK to new SPK and padding
// convert PK and SPK to new PK and SPK and padding
KeyCertificate kcert = cert.toKeyCertificate();
_publicKey = pk.toTypedKey(kcert);
_signingKey = spk.toTypedKey(kcert);
_padding = spk.getPadding(kcert);
byte[] pad1 = pk.getPadding(kcert);
byte[] pad2 = spk.getPadding(kcert);
_padding = combinePadding(pad1, pad2);
_certificate = kcert;
} else {
_publicKey = pk;
_signingKey = spk;
_certificate = cert;
}
}
/**
* @return null if both are null
* @since 0.9.42
*/
protected static byte[] combinePadding(byte[] pad1, byte[] pad2) {
if (pad1 == null)
return pad2;
if (pad2 == null)
return pad1;
byte[] rv = new byte[pad1.length + pad2.length];
System.arraycopy(pad1, 0, rv, 0, pad1.length);
System.arraycopy(pad2, 0, rv, pad1.length, pad2.length);
return rv;
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ((_certificate == null) || (_publicKey == null) || (_signingKey == null))
throw new DataFormatException("Not enough data to format the router identity");
_publicKey.writeBytes(out);
if (_padding != null)
out.write(_padding);
else if (_signingKey.length() < SigningPublicKey.KEYSIZE_BYTES)
else if (_signingKey.length() < SigningPublicKey.KEYSIZE_BYTES ||
_publicKey.length() < PublicKey.KEYSIZE_BYTES)
throw new DataFormatException("No padding set");
_signingKey.writeTruncatedBytes(out);
_certificate.writeBytes(out);

View File

@ -11,6 +11,7 @@ package net.i2p.data;
import java.io.InputStream;
import java.io.IOException;
import java.util.Arrays;
import net.i2p.crypto.EncType;
@ -20,6 +21,7 @@ import net.i2p.crypto.EncType;
* exponent, not the primes, which are constant and defined in the crypto spec.
*
* As of release 0.9.38, keys of arbitrary length and type are supported.
* Note: Support for keys longer than 256 bytes unimplemented.
* See EncType.
*
* @author jrandom
@ -134,6 +136,63 @@ public class PublicKey extends SimpleDataStructure {
return _unknownTypeCode;
}
/**
* Up-convert this from an untyped (type 0) PK to a typed PK based on the Key Cert given.
* The type of the returned key will be null if the kcert sigtype is null.
*
* @throws IllegalArgumentException if this is already typed to a different type
* @since 0.9.42
*/
public PublicKey toTypedKey(KeyCertificate kcert) {
if (_data == null)
throw new IllegalStateException();
EncType newType = kcert.getEncType();
if (_type == newType)
return this;
if (_type != EncType.ELGAMAL_2048)
throw new IllegalArgumentException("Cannot convert " + _type + " to " + newType);
// unknown type, keep the 256 bytes of data
if (newType == null)
return new PublicKey(null, _data);
int newLen = newType.getPubkeyLen();
if (newLen == KEYSIZE_BYTES)
return new PublicKey(newType, _data);
byte[] newData = new byte[newLen];
if (newLen < KEYSIZE_BYTES) {
// LEFT justified, padding at end
System.arraycopy(_data, 0, newData, 0, newLen);
} else {
// full 256 bytes + fragment in kcert
throw new IllegalArgumentException("TODO");
}
return new PublicKey(newType, newData);
}
/**
* Get the portion of this (type 0) PK that is really padding based on the Key Cert type given,
* if any
*
* @return trailing padding length &gt; 0 or null if no padding or type is unknown
* @throws IllegalArgumentException if this is already typed to a different type
* @since 0.9.42
*/
public byte[] getPadding(KeyCertificate kcert) {
if (_data == null)
throw new IllegalStateException();
EncType newType = kcert.getEncType();
if (_type == newType || newType == null)
return null;
if (_type != EncType.ELGAMAL_2048)
throw new IllegalStateException("Cannot convert " + _type + " to " + newType);
int newLen = newType.getPubkeyLen();
if (newLen >= KEYSIZE_BYTES)
return null;
int padLen = KEYSIZE_BYTES - newLen;
byte[] pad = new byte[padLen];
System.arraycopy(_data, _data.length - padLen, pad, 0, padLen);
return pad;
}
/**
* @since 0.9.17
*/
@ -160,4 +219,23 @@ public class PublicKey extends SimpleDataStructure {
buf.append(']');
return buf.toString();
}
/**
* @since 0.9.42
*/
@Override
public int hashCode() {
return DataHelper.hashCode(_type) ^ super.hashCode();
}
/**
* @since 0.9.42
*/
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || !(obj instanceof PublicKey)) return false;
PublicKey s = (PublicKey) obj;
return _type == s._type && Arrays.equals(_data, s._data);
}
}