forked from I2P_Developers/i2p.i2p
Data: Per-client auth for enc. LS2 (proposal 123)
This commit is contained in:
@ -5,15 +5,19 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.Blinding;
|
||||
import net.i2p.crypto.ChaCha20;
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
import net.i2p.crypto.EncType;
|
||||
import net.i2p.crypto.HKDF;
|
||||
import net.i2p.crypto.KeyPair;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.crypto.x25519.X25519DH;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -46,6 +50,12 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
private static final byte[] SUBCREDENTIAL = DataHelper.getASCII("subcredential");
|
||||
private static final String ELS2L1K = "ELS2_L1K";
|
||||
private static final String ELS2L2K = "ELS2_L2K";
|
||||
private static final String ELS2_DH = "ELS2_XCA";
|
||||
private static final String ELS2_PSK = "ELS2PSKA";
|
||||
private static final int IV_LEN = 12;
|
||||
private static final int ID_LEN = 8;
|
||||
private static final int COOKIE_LEN = 32;
|
||||
private static final int CLIENT_LEN = ID_LEN + COOKIE_LEN;
|
||||
|
||||
public EncryptedLeaseSet() {
|
||||
super();
|
||||
@ -351,6 +361,18 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
*/
|
||||
@Override
|
||||
public void encrypt(SessionKey skey) {
|
||||
encrypt(BlindData.AUTH_NONE, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws IllegalStateException if not initialized.
|
||||
* Ref: proposal 123
|
||||
*
|
||||
* @param authType 0, 1, or 3, see BlindData
|
||||
* @param clientKeys The client's X25519 public or private keys, null if unused
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void encrypt(int authType, List<? extends SimpleDataStructure> clientKeys) {
|
||||
if (_encryptedData != null)
|
||||
throw new IllegalStateException("already encrypted");
|
||||
if (_signature == null)
|
||||
@ -373,32 +395,148 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
_flags = saveFlags;
|
||||
}
|
||||
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
byte[] input = getHKDFInput(ctx);
|
||||
|
||||
// layer 2 (inner) encryption
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
byte[] salt = new byte[SALT_LEN];
|
||||
ctx.random().nextBytes(salt);
|
||||
HKDF hkdf = new HKDF(ctx);
|
||||
byte[] key = new byte[32];
|
||||
// use first 12 bytes only
|
||||
byte[] iv = new byte[32];
|
||||
hkdf.calculate(salt, input, ELS2L2K, key, iv, 0);
|
||||
int authLen;
|
||||
if (authType == BlindData.AUTH_NONE) {
|
||||
authLen = 1;
|
||||
} else if (authType == BlindData.AUTH_DH ||
|
||||
authType == BlindData.AUTH_PSK) {
|
||||
if (clientKeys == null || clientKeys.isEmpty())
|
||||
throw new IllegalArgumentException("No client keys provided");
|
||||
authLen = 1 + SALT_LEN + 2 + (clientKeys.size() * CLIENT_LEN);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad auth type " + authType);
|
||||
}
|
||||
byte[] authInput;
|
||||
byte[] authcookie = null;
|
||||
if (authType == BlindData.AUTH_NONE) {
|
||||
authInput = getHKDFInput(ctx);
|
||||
} else {
|
||||
authcookie = new byte[32];
|
||||
ctx.random().nextBytes(authcookie);
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Auth Cookie:\n" +
|
||||
net.i2p.util.HexDump.dump(authcookie));
|
||||
}
|
||||
authInput = getHKDFInput(ctx, authcookie);
|
||||
}
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Inner HKDF salt:\n" +
|
||||
net.i2p.util.HexDump.dump(salt) +
|
||||
"Inner HKDF input:\n" +
|
||||
net.i2p.util.HexDump.dump(authInput));
|
||||
}
|
||||
hkdf.calculate(salt, authInput, ELS2L2K, key, iv, 0);
|
||||
byte[] plaintext = baos.toByteArray();
|
||||
byte[] ciphertext = new byte[1 + SALT_LEN + plaintext.length];
|
||||
// Middle layer - flag
|
||||
ciphertext[0] = 0;
|
||||
System.arraycopy(salt, 0, ciphertext, 1, SALT_LEN);
|
||||
ChaCha20.encrypt(key, iv, plaintext, 0, ciphertext, 1 + SALT_LEN, plaintext.length);
|
||||
byte[] ciphertext = new byte[authLen + SALT_LEN + plaintext.length];
|
||||
|
||||
// Middle layer
|
||||
if (authType == BlindData.AUTH_NONE) {
|
||||
// Flag only
|
||||
ciphertext[0] = BlindData.AUTH_NONE;
|
||||
} else {
|
||||
// Flag
|
||||
ciphertext[0] = (byte) (authType & 0x0f);
|
||||
if (clientKeys.size() > 1)
|
||||
Collections.shuffle(clientKeys);
|
||||
if (authType == BlindData.AUTH_DH) {
|
||||
// DH
|
||||
KeyPair encKeys = ctx.keyGenerator().generatePKIKeys(EncType.ECIES_X25519);
|
||||
PrivateKey esk = encKeys.getPrivate();
|
||||
PublicKey epk = encKeys.getPublic();
|
||||
// HKDF input is 100 bytes
|
||||
byte[] clientAuthInput = new byte[32 + authInput.length];
|
||||
// we copy over end of authInput from above
|
||||
// subcredential and timestamp remain unchanged
|
||||
System.arraycopy(authInput, 32, clientAuthInput, 64, 36);
|
||||
// pubkey
|
||||
System.arraycopy(epk.getData(), 0, ciphertext, 1, 32);
|
||||
DataHelper.toLong(ciphertext, 33, 2, clientKeys.size());
|
||||
int off = 35;
|
||||
byte[] clientKey = new byte[32];
|
||||
byte[] clientIVandID = new byte[32];
|
||||
for (SimpleDataStructure sds : clientKeys) {
|
||||
if (!(sds instanceof PublicKey))
|
||||
throw new IllegalArgumentException("Bad DH client key type: " + sds);
|
||||
PublicKey cpk = (PublicKey) sds;
|
||||
if (cpk.getType() != EncType.ECIES_X25519)
|
||||
throw new IllegalArgumentException("Bad DH client key type: " + cpk);
|
||||
SessionKey dh = X25519DH.dh(esk, cpk);
|
||||
System.arraycopy(dh.getData(), 0, clientAuthInput, 0, 32);
|
||||
System.arraycopy(cpk.getData(), 0, clientAuthInput, 32, 32);
|
||||
hkdf.calculate(epk.getData(), clientAuthInput, ELS2_DH, clientKey, clientIVandID, 0);
|
||||
System.arraycopy(clientIVandID, IV_LEN, ciphertext, off, ID_LEN);
|
||||
off += ID_LEN;
|
||||
ChaCha20.encrypt(clientKey, clientIVandID, authcookie, 0, ciphertext, off, authcookie.length);
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Added client ID/enc.cookie:\n" +
|
||||
net.i2p.util.HexDump.dump(clientIVandID, IV_LEN, ID_LEN) +
|
||||
net.i2p.util.HexDump.dump(ciphertext, off, COOKIE_LEN) +
|
||||
"for client key:\n" +
|
||||
net.i2p.util.HexDump.dump(clientKey));
|
||||
}
|
||||
off += COOKIE_LEN;
|
||||
}
|
||||
} else {
|
||||
// PSK
|
||||
// salt
|
||||
byte[] authsalt = new byte[32];
|
||||
ctx.random().nextBytes(authsalt);
|
||||
System.arraycopy(authsalt, 0, ciphertext, 1, 32);
|
||||
DataHelper.toLong(ciphertext, 33, 2, clientKeys.size());
|
||||
int off = 35;
|
||||
byte[] clientKey = new byte[32];
|
||||
byte[] clientIVandID = new byte[32];
|
||||
for (SimpleDataStructure sds : clientKeys) {
|
||||
if (!(sds instanceof PrivateKey))
|
||||
throw new IllegalArgumentException("Bad DH client key type: " + sds);
|
||||
PrivateKey csk = (PrivateKey) sds;
|
||||
if (csk.getType() != EncType.ECIES_X25519)
|
||||
throw new IllegalArgumentException("Bad PSK client key type: " + csk);
|
||||
// HKDF input is 68 bytes `
|
||||
// we reuse authInput from above, just replace the first 32 bytes.
|
||||
// subcredential and timestamp remain unchanged
|
||||
System.arraycopy(csk.getData(), 0, authInput, 0, 32);
|
||||
hkdf.calculate(authsalt, authInput, ELS2_PSK, clientKey, clientIVandID, 0);
|
||||
System.arraycopy(clientIVandID, IV_LEN, ciphertext, off, ID_LEN);
|
||||
off += ID_LEN;
|
||||
ChaCha20.encrypt(clientKey, clientIVandID, authcookie, 0, ciphertext, off, authcookie.length);
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Added client ID/enc.cookie:\n" +
|
||||
net.i2p.util.HexDump.dump(clientIVandID, IV_LEN, ID_LEN) +
|
||||
net.i2p.util.HexDump.dump(ciphertext, off, COOKIE_LEN) +
|
||||
"for client key:\n" +
|
||||
net.i2p.util.HexDump.dump(clientKey));
|
||||
}
|
||||
off += COOKIE_LEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
System.arraycopy(salt, 0, ciphertext, authLen, SALT_LEN);
|
||||
ChaCha20.encrypt(key, iv, plaintext, 0, ciphertext, authLen + SALT_LEN, plaintext.length);
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Encrypt: inner plaintext:\n" + net.i2p.util.HexDump.dump(plaintext));
|
||||
_log.debug("Encrypt: inner ciphertext:\n" + net.i2p.util.HexDump.dump(ciphertext));
|
||||
}
|
||||
|
||||
// layer 1 (outer) encryption
|
||||
// reuse input (because there's no authcookie), generate new salt/key/iv
|
||||
ctx.random().nextBytes(salt);
|
||||
hkdf.calculate(salt, input, ELS2L1K, key, iv, 0);
|
||||
if (authType == BlindData.AUTH_NONE) {
|
||||
// reuse input (because there's no authcookie), generate new salt/key/iv
|
||||
hkdf.calculate(salt, authInput, ELS2L1K, key, iv, 0);
|
||||
} else {
|
||||
// get just the subcredential and date
|
||||
byte[] l1Input = new byte[36];
|
||||
System.arraycopy(authInput, 32, l1Input, 0, 36);
|
||||
hkdf.calculate(salt, l1Input, ELS2L1K, key, iv, 0);
|
||||
}
|
||||
plaintext = ciphertext;
|
||||
ciphertext = new byte[SALT_LEN + plaintext.length];
|
||||
System.arraycopy(salt, 0, ciphertext, 0, SALT_LEN);
|
||||
@ -415,16 +553,30 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
/**
|
||||
* Throws IllegalStateException if not initialized.
|
||||
*
|
||||
* @param skey ignored
|
||||
* @param clientKey PrivateKey for DH or PSK, or null if none
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
private void decrypt() throws DataFormatException, IOException {
|
||||
private void decrypt(PrivateKey csk) throws DataFormatException, IOException {
|
||||
try {
|
||||
x_decrypt(csk);
|
||||
} catch (IndexOutOfBoundsException ioobe) {
|
||||
throw new DataFormatException("ioobe", ioobe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws IllegalStateException if not initialized.
|
||||
*
|
||||
* @param clientKey PrivateKey for DH or PSK, or null if none
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
private void x_decrypt(PrivateKey csk) throws DataFormatException, IOException {
|
||||
if (_encryptedData == null)
|
||||
throw new IllegalStateException("not encrypted");
|
||||
if (_decryptedLS2 != null)
|
||||
return;
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
byte[] input = getHKDFInput(ctx);
|
||||
byte[] authInput = getHKDFInput(ctx);
|
||||
|
||||
// layer 1 (outer) decryption
|
||||
HKDF hkdf = new HKDF(ctx);
|
||||
@ -434,7 +586,7 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
byte[] ciphertext = _encryptedData;
|
||||
byte[] plaintext = new byte[ciphertext.length - SALT_LEN];
|
||||
// first 32 bytes of ciphertext are the salt
|
||||
hkdf.calculate(ciphertext, input, ELS2L1K, key, iv, 0);
|
||||
hkdf.calculate(ciphertext, authInput, ELS2L1K, key, iv, 0);
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Decrypt: chacha20 key:\n" + net.i2p.util.HexDump.dump(key));
|
||||
_log.debug("Decrypt: chacha20 IV:\n" + net.i2p.util.HexDump.dump(iv));
|
||||
@ -445,21 +597,104 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
_log.debug("Decrypt: outer plaintext:\n" + net.i2p.util.HexDump.dump(plaintext));
|
||||
}
|
||||
|
||||
boolean perClient = (plaintext[0] & 0x01) != 0;
|
||||
if (perClient) {
|
||||
int authScheme = (plaintext[0] & 0x0e) >> 1;
|
||||
// TODO
|
||||
throw new DataFormatException("Per client auth unsupported, scheme: " + authScheme);
|
||||
int authType = plaintext[0] & 0x0f;
|
||||
int authLen;
|
||||
if (authType == BlindData.AUTH_NONE) {
|
||||
authLen = 1;
|
||||
} else {
|
||||
if (csk == null)
|
||||
throw new DataFormatException("Per-client auth but no key");
|
||||
if (authType != BlindData.AUTH_DH && authType != BlindData.AUTH_PSK)
|
||||
throw new DataFormatException("Per-client auth unsupported type: " + authType);
|
||||
if (csk.getType() != EncType.ECIES_X25519)
|
||||
throw new DataFormatException("Bad PSK client key type: " + csk);
|
||||
byte[] seed = new byte[32];
|
||||
System.arraycopy(plaintext, 1, seed, 0, 32);
|
||||
int count = (int) DataHelper.fromLong(plaintext, 33, 2);
|
||||
if (count == 0)
|
||||
throw new DataFormatException("No client entries");
|
||||
authLen = 1 + SALT_LEN + 2 + (count * CLIENT_LEN);
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Found " + count + " client entries, seed is:\n" +
|
||||
net.i2p.util.HexDump.dump(seed));
|
||||
}
|
||||
byte[] clientKey = new byte[32];
|
||||
byte[] clientIVandID = new byte[32];
|
||||
if (authType == BlindData.AUTH_DH) {
|
||||
// seed is public key
|
||||
PublicKey epk = new PublicKey(EncType.ECIES_X25519, seed);
|
||||
SessionKey dh = X25519DH.dh(csk, epk);
|
||||
// HKDF input is 100 bytes
|
||||
byte[] clientAuthInput = new byte[64 + authInput.length];
|
||||
System.arraycopy(dh.getData(), 0, clientAuthInput, 0, 32);
|
||||
// TODO cache pubkey, either in PrivateKey or use KeyPair
|
||||
PublicKey cpk = csk.toPublic();
|
||||
System.arraycopy(cpk.getData(), 0, clientAuthInput, 32, 32);
|
||||
// we copy over end of authInput from above
|
||||
// subcredential and timestamp remain unchanged
|
||||
System.arraycopy(authInput, 0, clientAuthInput, 64, 36);
|
||||
hkdf.calculate(seed, clientAuthInput, ELS2_DH, clientKey, clientIVandID, 0);
|
||||
} else {
|
||||
// PSK
|
||||
// HKDF input is 68 bytes `
|
||||
// we reuse authInput from above, just replace the first 32 bytes.
|
||||
// subcredential and timestamp remain unchanged
|
||||
byte[] clientAuthInput = new byte[32 + authInput.length];
|
||||
System.arraycopy(csk.getData(), 0, clientAuthInput, 0, 32);
|
||||
// we copy over authInput from above
|
||||
// subcredential and timestamp remain unchanged
|
||||
System.arraycopy(authInput, 0, clientAuthInput, 32, 36);
|
||||
hkdf.calculate(seed, clientAuthInput, ELS2_PSK, clientKey, clientIVandID, 0);
|
||||
}
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Looking for client ID:\n" +
|
||||
net.i2p.util.HexDump.dump(clientIVandID, IV_LEN, ID_LEN) +
|
||||
"for client key:\n" +
|
||||
net.i2p.util.HexDump.dump(clientKey));
|
||||
}
|
||||
int off = 35;
|
||||
byte[] clientCookie = null;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (DataHelper.eq(clientIVandID, IV_LEN, plaintext, off, ID_LEN)) {
|
||||
clientCookie = new byte[32];
|
||||
System.arraycopy(plaintext, off + ID_LEN, clientCookie, 0, 32);
|
||||
break;
|
||||
}
|
||||
off += CLIENT_LEN;
|
||||
}
|
||||
if (clientCookie == null)
|
||||
throw new DataFormatException("Our client auth entry not found");
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Found client cookie:\n" +
|
||||
net.i2p.util.HexDump.dump(clientCookie));
|
||||
}
|
||||
byte[] clientAuthInput = new byte[32 + authInput.length];
|
||||
// we copy over end of authInput from above
|
||||
// subcredential and timestamp remain unchanged
|
||||
System.arraycopy(authInput, 0, clientAuthInput, 32, 36);
|
||||
// decrypt clientCookie to clientAuthInput
|
||||
ChaCha20.decrypt(clientKey, clientIVandID, clientCookie, 0, clientAuthInput, 0, 32);
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Decrypted client cookie:\n" +
|
||||
net.i2p.util.HexDump.dump(clientAuthInput, 0, 32));
|
||||
}
|
||||
authInput = clientAuthInput;
|
||||
}
|
||||
|
||||
// layer 2 (inner) decryption
|
||||
// reuse input (because there's no authcookie), get new salt/key/iv
|
||||
ciphertext = plaintext;
|
||||
plaintext = new byte[ciphertext.length - (1 + SALT_LEN)];
|
||||
plaintext = new byte[ciphertext.length - (authLen + SALT_LEN)];
|
||||
byte[] salt = new byte[SALT_LEN];
|
||||
System.arraycopy(ciphertext, 1, salt, 0, SALT_LEN);
|
||||
hkdf.calculate(salt, input, ELS2L2K, key, iv, 0);
|
||||
ChaCha20.decrypt(key, iv, ciphertext, 1 + SALT_LEN, plaintext, 0, plaintext.length);
|
||||
System.arraycopy(ciphertext, authLen, salt, 0, SALT_LEN);
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Inner HKDF salt:\n" +
|
||||
net.i2p.util.HexDump.dump(salt) +
|
||||
"Inner HKDF input:\n" +
|
||||
net.i2p.util.HexDump.dump(authInput));
|
||||
}
|
||||
hkdf.calculate(salt, authInput, ELS2L2K, key, iv, 0);
|
||||
ChaCha20.decrypt(key, iv, ciphertext, authLen + SALT_LEN, plaintext, 0, plaintext.length);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Decrypt: inner plaintext:\n" + net.i2p.util.HexDump.dump(plaintext));
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(plaintext);
|
||||
@ -470,13 +705,13 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
else if (type == KEY_TYPE_META_LS2)
|
||||
innerLS2 = new MetaLeaseSet();
|
||||
else
|
||||
throw new DataFormatException("Unsupported LS type: " + type);
|
||||
throw new DataFormatException("Bad decryption or unsupported LS type: " + type);
|
||||
innerLS2.readBytes(bais);
|
||||
_decryptedLS2 = innerLS2;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HKDF input
|
||||
* The HKDF input (no per-client auth)
|
||||
*
|
||||
* @return 36 bytes
|
||||
* @since 0.9.39
|
||||
@ -489,6 +724,22 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HKDF input (with per-client auth)
|
||||
*
|
||||
* @param authcookie 32 bytes
|
||||
* @return 68 bytes
|
||||
* @since 0.9.41
|
||||
*/
|
||||
private byte[] getHKDFInput(I2PAppContext ctx, byte[] authcookie) {
|
||||
byte[] subcredential = getSubcredential(ctx);
|
||||
byte[] rv = new byte[authcookie.length + subcredential.length + 4];
|
||||
System.arraycopy(authcookie, 0, rv, 0, authcookie.length);
|
||||
System.arraycopy(subcredential, 0, rv, authcookie.length, subcredential.length);
|
||||
DataHelper.toLong(rv, authcookie.length + subcredential.length, 4, _published / 1000);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* The subcredential
|
||||
*
|
||||
@ -537,6 +788,20 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
*/
|
||||
@Override
|
||||
public void sign(SigningPrivateKey key) throws DataFormatException {
|
||||
sign(key, BlindData.AUTH_NONE, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the structure using the supplied signing key.
|
||||
* Overridden because we sign the inner, then blind and encrypt
|
||||
* and sign the outer.
|
||||
*
|
||||
* @param authType 0, 1, or 3, see BlindData
|
||||
* @param clientKeys X25519 public keys for DH, private keys for PSK
|
||||
* @throws IllegalStateException if already signed
|
||||
* @since 0.9.41
|
||||
*/
|
||||
public void sign(SigningPrivateKey key, int authType, List<? extends SimpleDataStructure> clientKeys) throws DataFormatException {
|
||||
// now sign inner with the unblinded key
|
||||
// inner LS is always unpublished
|
||||
int saveFlags = _flags;
|
||||
@ -548,7 +813,7 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
_log.debug("Corresponding pubkey: " + key.toPublic());
|
||||
_log.debug("Inner sig: " + _signature.getType() + ' ' + _signature.toBase64());
|
||||
}
|
||||
encrypt(null);
|
||||
encrypt(authType, clientKeys);
|
||||
SigningPrivateKey bkey = Blinding.blind(key, _alpha);
|
||||
int len = size();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(1 + len);
|
||||
@ -580,6 +845,19 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
*/
|
||||
@Override
|
||||
public boolean verifySignature() {
|
||||
return verifySignature((PrivateKey) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt if possible, and verify inner sig also.
|
||||
*
|
||||
* Must call setDestination() prior to this if attempting decryption.
|
||||
*
|
||||
* @param clientKey PrivateKey for DH or PSK, or null if none
|
||||
* @return valid
|
||||
* @since 0.9.41
|
||||
*/
|
||||
public boolean verifySignature(PrivateKey clientKey) {
|
||||
// TODO use fields in super
|
||||
if (_decryptedLS2 != null)
|
||||
return _decryptedLS2.verifySignature();
|
||||
@ -598,7 +876,7 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
decrypt();
|
||||
decrypt(clientKey);
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.warn("ELS2 decrypt fail", dfe);
|
||||
return false;
|
||||
@ -684,13 +962,21 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
pkf.createIfAbsent(SigType.EdDSA_SHA512_Ed25519);
|
||||
System.out.println("Online test");
|
||||
java.io.File f2 = new java.io.File("online-encls2.dat");
|
||||
test(pkf, f2, false);
|
||||
test(pkf, f2, false, BlindData.AUTH_NONE, null);
|
||||
List<KeyPair> keys = new java.util.ArrayList<KeyPair>(4);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
keys.add(net.i2p.crypto.KeyGenerator.getInstance().generatePKIKeys(net.i2p.crypto.EncType.ECIES_X25519));
|
||||
}
|
||||
System.out.println("Online test with DH Keys");
|
||||
test(pkf, f2, false, BlindData.AUTH_DH, keys);
|
||||
System.out.println("Online test with PSK Keys");
|
||||
test(pkf, f2, false, BlindData.AUTH_PSK, keys);
|
||||
//System.out.println("Offline test");
|
||||
//f2 = new java.io.File("offline-encls2.dat");
|
||||
//test(pkf, f2, true);
|
||||
}
|
||||
|
||||
private static void test(PrivateKeyFile pkf, java.io.File outfile, boolean offline) throws Exception {
|
||||
private static void test(PrivateKeyFile pkf, java.io.File outfile, boolean offline, int authType, List<KeyPair> clientKeys) throws Exception {
|
||||
net.i2p.util.RandomSource rand = net.i2p.util.RandomSource.getInstance();
|
||||
long now = System.currentTimeMillis() + 5*60*1000;
|
||||
EncryptedLeaseSet ls2 = new EncryptedLeaseSet();
|
||||
@ -726,10 +1012,24 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
ls2.setOfflineSignature(now, transientPub, sig);
|
||||
ls2.sign(transientPriv);
|
||||
} else {
|
||||
ls2.sign(spk);
|
||||
List<SimpleDataStructure> signkeys = null;
|
||||
if (authType != BlindData.AUTH_NONE) {
|
||||
signkeys = new java.util.ArrayList<SimpleDataStructure>();
|
||||
for (KeyPair kp : clientKeys) {
|
||||
if (authType == BlindData.AUTH_DH)
|
||||
signkeys.add(kp.getPublic());
|
||||
else
|
||||
signkeys.add(kp.getPrivate());
|
||||
}
|
||||
}
|
||||
ls2.sign(spk, authType, signkeys);
|
||||
}
|
||||
System.out.println("\nCreated: " + ls2);
|
||||
if (!ls2.verifySignature()) {
|
||||
PrivateKey verifyKey = null;
|
||||
if (authType != BlindData.AUTH_NONE)
|
||||
verifyKey = clientKeys.get(0).getPrivate();
|
||||
if (!ls2.verifySignature(verifyKey)) {
|
||||
I2PAppContext.getGlobalContext().logManager().flush();
|
||||
System.out.println("Verify FAILED");
|
||||
return;
|
||||
}
|
||||
@ -746,8 +1046,9 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
System.out.println("\nRead back: " + ls3);
|
||||
// required to decrypt
|
||||
ls3.setDestination(pkf.getDestination());
|
||||
if (!ls3.verifySignature())
|
||||
if (!ls3.verifySignature(verifyKey))
|
||||
System.out.println("Verify FAILED");
|
||||
I2PAppContext.getGlobalContext().logManager().flush();
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
Reference in New Issue
Block a user