Data: Initial support for enc types in PrivateKeyFile and I2PSessionImpl

CreateRouterInfoJob updates and cleanups; KeyCert updates
This commit is contained in:
zzz
2019-07-23 15:56:03 +00:00
parent e2980603b7
commit 66ecdb2f7a
4 changed files with 181 additions and 33 deletions

View File

@ -38,6 +38,7 @@ import net.i2p.client.I2PClient;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.crypto.EncType;
import net.i2p.crypto.SigType;
import net.i2p.data.Base32;
import net.i2p.data.DataFormatException;
@ -88,9 +89,9 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
/** who we are */
private final Destination _myDestination;
/** private key for decryption */
private final PrivateKey _privateKey;
private PrivateKey _privateKey;
/** private key for signing */
private /* final */ SigningPrivateKey _signingPrivateKey;
private SigningPrivateKey _signingPrivateKey;
/** configuration options */
private final Properties _options;
/** this session's Id */
@ -587,6 +588,12 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
*/
private void readDestination(InputStream destKeyStream) throws DataFormatException, IOException {
_myDestination.readBytes(destKeyStream);
// we don't support EncTypes in Destinations,
// but i2pd does, and also, this code is used from PrivateKeyFile to
// read in router.keys.dat files and we will support EncTypes for RouterIdentities
EncType etype = _myDestination.getPublicKey().getType();
if (etype != EncType.ELGAMAL_2048)
_privateKey = new PrivateKey(etype);
_privateKey.readBytes(destKeyStream);
SigType dtype = _myDestination.getSigningPublicKey().getType();
_signingPrivateKey = new SigningPrivateKey(dtype);

View File

@ -90,6 +90,35 @@ public class KeyCertificate extends Certificate {
System.arraycopy(spk.getData(), 128, _payload, HEADER_LENGTH, extra);
}
/**
* A KeyCertificate with enc type from the given public key,
* and the signature type and extra data from the given public key.
* EncType lengths greater than 256 not supported.
*
* @param spk non-null data non-null
* @param pk non-null
* @throws IllegalArgumentException
* @since 0.9.42
*/
public KeyCertificate(SigningPublicKey spk, PublicKey pk) {
super(CERTIFICATE_TYPE_KEY, null);
if (spk == null || spk.getData() == null ||
pk == null || pk.getData() == null)
throw new IllegalArgumentException();
SigType type = spk.getType();
int len = type.getPubkeyLen();
int extra = Math.max(0, len - 128);
_payload = new byte[HEADER_LENGTH + extra];
int code = type.getCode();
_payload[0] = (byte) (code >> 8);
_payload[1] = (byte) (code & 0xff);
code = pk.getType().getCode();
_payload[2] = (byte) (code >> 8);
_payload[3] = (byte) (code & 0xff);
if (extra > 0)
System.arraycopy(spk.getData(), 128, _payload, HEADER_LENGTH, extra);
}
/**
* A KeyCertificate with crypto type 0 (ElGamal)
* and the signature type as specified.
@ -101,6 +130,23 @@ public class KeyCertificate extends Certificate {
* @throws IllegalArgumentException
*/
public KeyCertificate(SigType type) {
this(type, EncType.ELGAMAL_2048);
}
/**
* A KeyCertificate with crypto type
* and the signature type as specified.
* Payload is created.
* If type.getPubkeyLen() is greater than 128, caller MUST
* fill in the extra key data in the payload.
* EncType lengths greater than 256 not supported.
*
* @param type non-null
* @param etype non-null
* @throws IllegalArgumentException
* @since 0.9.42
*/
public KeyCertificate(SigType type, EncType etype) {
super(CERTIFICATE_TYPE_KEY, null);
int len = type.getPubkeyLen();
int extra = Math.max(0, len - 128);
@ -108,7 +154,9 @@ public class KeyCertificate extends Certificate {
int code = type.getCode();
_payload[0] = (byte) (code >> 8);
_payload[1] = (byte) (code & 0xff);
// 2 and 3 always 0, it is the only crypto code for now
code = etype.getCode();
_payload[2] = (byte) (code >> 8);
_payload[3] = (byte) (code & 0xff);
}
/**

View File

@ -29,7 +29,9 @@ import net.i2p.client.I2PSessionException;
import net.i2p.client.naming.HostTxtEntry;
import net.i2p.crypto.Blinding;
import net.i2p.crypto.DSAEngine;
import net.i2p.crypto.EncType;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.KeyPair;
import net.i2p.crypto.SigType;
import net.i2p.util.OrderedProperties;
import net.i2p.util.RandomSource;
@ -90,13 +92,14 @@ public class PrivateKeyFile {
public static void main(String args[]) {
int hashEffort = HASH_EFFORT;
String stype = null;
String etype = null;
String ttype = null;
String hostname = null;
String offline = null;
int days = 365;
int mode = 0;
boolean error = false;
Getopt g = new Getopt("pkf", args, "t:nuxhse:c:a:o:d:r:");
Getopt g = new Getopt("pkf", args, "t:nuxhse:c:a:o:d:r:p:");
int c;
while ((c = g.getopt()) != -1) {
switch (c) {
@ -104,6 +107,10 @@ public class PrivateKeyFile {
stype = g.getOptarg();
break;
case 'p':
etype = g.getOptarg();
break;
case 't':
stype = g.getOptarg();
// fall thru...
@ -171,7 +178,21 @@ public class PrivateKeyFile {
boolean exists = f.exists();
PrivateKeyFile pkf = new PrivateKeyFile(f, client);
Destination d;
if (stype != null) {
if (etype != null && !exists) {
// create router.keys.dat format, no support in I2PClient
SigType type;
if (stype == null) {
type = SigType.EdDSA_SHA512_Ed25519;
} else {
type = SigType.parseSigType(stype);
if (type == null)
throw new IllegalArgumentException("Signature type " + stype + " is not supported");
}
EncType ptype = EncType.parseEncType(etype);
if (ptype == null)
throw new IllegalArgumentException("Encryption type " + etype + " is not supported");
d = pkf.createIfAbsent(type, ptype);
} else if (stype != null) {
SigType type = SigType.parseSigType(stype);
if (type == null)
throw new IllegalArgumentException("Signature type " + stype + " is not supported");
@ -319,10 +340,11 @@ public class PrivateKeyFile {
" -x (changes to hidden cert)\n" +
" \nother options:\n" +
" -a example.i2p (generate addressbook authentication string)\n" +
" -d days (specify expiration in days of offline sig, default 365)\n" +
" -c sigtype (specify sig type of destination)\n" +
" -d days (specify expiration in days of offline sig, default 365)\n" +
" -e effort (specify HashCash effort instead of default " + HASH_EFFORT + ")\n" +
" -o offlinedestfile (generate the online key file using the offline key file specified)\n" +
" -p enctype (specify enc type of destination)\n" +
" -r sigtype (specify sig type of transient key, default Ed25519)\n" +
" -t sigtype (changes to KeyCertificate of the given sig type)\n" +
"");
@ -411,7 +433,7 @@ public class PrivateKeyFile {
public Destination createIfAbsent() throws I2PException, IOException, DataFormatException {
return createIfAbsent(I2PClient.DEFAULT_SIGTYPE);
}
/**
* Create with the specified signature type if nonexistent.
*
@ -424,11 +446,12 @@ public class PrivateKeyFile {
if(!this.file.exists()) {
OutputStream out = null;
try {
out = new SecureFileOutputStream(this.file);
if (this.client != null)
if (this.client != null) {
out = new SecureFileOutputStream(this.file);
this.client.createDestination(out, type);
else
} else {
write();
}
} finally {
if (out != null) {
try { out.close(); } catch (IOException ioe) {}
@ -437,7 +460,70 @@ public class PrivateKeyFile {
}
return getDestination();
}
/**
* Create with the specified signature and encryption types if nonexistent.
* Private for now, only for router.keys.dat testing.
*
* Also reads in the file to get the privKey and signingPrivKey,
* which aren't available from I2PClient.
*
* @since 0.9.42
*/
private Destination createIfAbsent(SigType type, EncType ptype) throws I2PException, IOException, DataFormatException {
if(!this.file.exists()) {
OutputStream out = null;
try {
if (this.client != null) {
out = new SecureFileOutputStream(this.file);
// no support for this in I2PClient,
// so we modify code from CreateRouterInfoJob.createRouterInfo()
I2PAppContext ctx = I2PAppContext.getGlobalContext();
KeyPair keypair = ctx.keyGenerator().generatePKIKeys(ptype);
PublicKey pub = keypair.getPublic();
PrivateKey priv = keypair.getPrivate();
SimpleDataStructure signingKeypair[] = ctx.keyGenerator().generateSigningKeys(type);
SigningPublicKey spub = (SigningPublicKey)signingKeypair[0];
SigningPrivateKey spriv = (SigningPrivateKey)signingKeypair[1];
Certificate cert;
if (type != SigType.DSA_SHA1 || ptype != EncType.ELGAMAL_2048) {
// TODO long sig types
if (type.getPubkeyLen() > 128)
throw new UnsupportedOperationException("TODO");
cert = new KeyCertificate(type, ptype);
} else {
cert = Certificate.NULL_CERT;
}
byte[] padding;
int padLen = (SigningPublicKey.KEYSIZE_BYTES - spub.length()) +
(PublicKey.KEYSIZE_BYTES - pub.length());
if (padLen > 0) {
padding = new byte[padLen];
ctx.random().nextBytes(padding);
} else {
padding = null;
}
pub.writeBytes(out);
if (padding != null)
out.write(padding);
spub.writeBytes(out);
cert.writeBytes(out);
priv.writeBytes(out);
spriv.writeBytes(out);
} else {
write();
}
} catch (GeneralSecurityException gse) {
throw new RuntimeException("keygen fail", gse);
} finally {
if (out != null) {
try { out.close(); } catch (IOException ioe) {}
}
}
}
return getDestination();
}
/**
* If the destination is not set, read it in from the file.
* Also sets the local privKey and signingPrivKey.