Data: Initial work on b32 format for blinded leasesets (proposal 149, WIP)

This commit is contained in:
zzz
2019-03-23 13:39:47 +00:00
parent f17776ec54
commit 00d4525325
8 changed files with 401 additions and 33 deletions

View File

@@ -4,11 +4,15 @@ import java.security.GeneralSecurityException;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.TimeZone;
import java.util.zip.Checksum;
import java.util.zip.CRC32;
import net.i2p.I2PAppContext;
import net.i2p.crypto.eddsa.EdDSABlinding;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.data.Base32;
import net.i2p.data.BlindData;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
@@ -176,13 +180,155 @@ public final class Blinding {
return new SigningPrivateKey(TYPER, b);
}
/**
* What's the default blinded type for a given unblinded type?
*
* @return non-null
* @since 0.9.40
*/
public static SigType getDefaultBlindedType(SigType unblindedType) {
if (unblindedType == TYPE)
return TYPER;
return unblindedType;
}
/**
* Decode a new-format b32 address.
* PRELIMINARY - Subject to change - see proposal 149
*
* @param address ending with ".b32.i2p"
* @throws IllegalArgumentException on bad inputs
* @throws UnsupportedOperationException unless supported SigTypes
* @since 0.9.40
*/
public static BlindData decode(I2PAppContext ctx, String address) throws RuntimeException {
address = address.toLowerCase(Locale.US);
if (!address.endsWith(".b32.i2p"))
throw new IllegalArgumentException("Not a .b32.i2p address");
byte[] b = Base32.decode(address.substring(0, address.length() - 8));
if (b == null)
throw new IllegalArgumentException("Bad base32 encoding");
if (b.length < 35)
throw new IllegalArgumentException("Not a new-format address");
return decode(ctx, b);
}
/**
* Decode a new-format b32 address.
* PRELIMINARY - Subject to change - see proposal 149
*
* @param b 35+ bytes
* @throws IllegalArgumentException on bad inputs
* @throws UnsupportedOperationException unless supported SigTypes
* @since 0.9.40
*/
public static BlindData decode(I2PAppContext ctx, byte[] b) throws RuntimeException {
Checksum crc = new CRC32();
crc.update(b, 3, b.length - 3);
long check = crc.getValue();
b[0] ^= (byte) check;
b[1] ^= (byte) (check >> 8);
b[2] ^= (byte) (check >> 16);
int flag = b[0] & 0xff;
if ((flag & 0xf8) != 0)
throw new IllegalArgumentException("Corrupt b32 or unsupported options");
if ((flag & 0x01) != 0)
throw new IllegalArgumentException("Two byte sig types unsupported");
if ((flag & 0x04) != 0)
throw new IllegalArgumentException("Per-client auth unsupported");
// TODO two-byte sigtypes
int st1 = b[1] & 0xff;
int st2 = b[2] & 0xff;
SigType sigt1 = SigType.getByCode(st1);
SigType sigt2 = SigType.getByCode(st2);
if (sigt1 == null)
throw new IllegalArgumentException("Unknown sig type " + st1);
if (!sigt1.isAvailable())
throw new IllegalArgumentException("Unavailable sig type " + sigt1);
if (sigt2 == null)
throw new IllegalArgumentException("Unknown blinded sig type " + st2);
if (!sigt2.isAvailable())
throw new IllegalArgumentException("Unavailable blinded sig type " + sigt2);
// todo secret/privkey
int spkLen = sigt1.getPubkeyLen();
if (3 + spkLen > b.length)
throw new IllegalArgumentException("b32 too short");
byte[] spkData = new byte[spkLen];
System.arraycopy(b, 3, spkData, 0, spkLen);
SigningPublicKey spk = new SigningPublicKey(sigt1, spkData);
String secret;
if ((flag & 0x02) != 0) {
if (4 + spkLen > b.length)
throw new IllegalArgumentException("No secret data");
int secLen = b[3 + spkLen] & 0xff;
if (4 + spkLen + secLen != b.length)
throw new IllegalArgumentException("Bad b32 length");
secret = DataHelper.getUTF8(b, 4 + spkLen, secLen);
} else if (3 + spkLen != b.length) {
throw new IllegalArgumentException("b32 too long");
} else {
secret = null;
}
BlindData rv = new BlindData(ctx, spk, sigt2, secret);
return rv;
}
/**
* Encode a public key as a new-format b32 address.
* PRELIMINARY - Subject to change - see proposal 149
*
* @param secret may be empty or null
* @return (56+ chars).b32.i2p
* @throws IllegalArgumentException on bad inputs
* @throws UnsupportedOperationException unless supported SigTypes
* @since 0.9.40
*/
public static String encode(I2PAppContext ctx, SigningPublicKey key, String secret) throws RuntimeException {
SigType type = key.getType();
if (type != TYPE && type != TYPER)
throw new UnsupportedOperationException();
byte sdata[] = (secret != null) ? DataHelper.getUTF8(secret) : null;
int slen = (secret != null) ? 1 + sdata.length : 0;
if (slen > 256)
throw new IllegalArgumentException("secret too long");
byte[] d = key.getData();
byte[] b = new byte[d.length + slen + 3];
System.arraycopy(d, 0, b, 3, d.length);
if (slen > 0) {
b[3 + d.length] = (byte) sdata.length;
System.arraycopy(sdata, 0, b, 4 + d.length, sdata.length);
}
Checksum crc = new CRC32();
crc.update(b, 3, b.length - 3);
long check = crc.getValue();
// TODO two-byte sigtypes
if (slen > 0)
b[0] = 0x02;
b[1] = (byte) (type.getCode() & 0xff);
b[2] = (byte) (TYPER.getCode() & 0xff);
b[0] ^= (byte) check;
b[1] ^= (byte) (check >> 8);
b[2] ^= (byte) (check >> 16);
// todo privkey
return Base32.encode(b) + ".b32.i2p";
}
/******
public static void main(String args[]) throws Exception {
net.i2p.data.SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(TYPE);
SigningPublicKey pub = (SigningPublicKey) keys[0];
SigningPrivateKey priv = (SigningPrivateKey) keys[1];
I2PAppContext ctx = I2PAppContext.getGlobalContext();
//String b32 = encode(ctx, pub, null);
String b32 = encode(ctx, pub, "foobarbaz");
System.out.println("pub b32 is " + b32);
BlindData bd = decode(ctx, b32);
if (bd.getBlindedPubKey().equals(pub))
System.out.println("B32 test failed");
else
System.out.println("B32 test passed");
byte[] b = new byte[64];
net.i2p.I2PAppContext.getGlobalContext().random().nextBytes(b);
ctx.random().nextBytes(b);
b = EdDSABlinding.reduce(b);
SigningPrivateKey alpha = new SigningPrivateKey(TYPER, b);
SigningPublicKey bpub = null;