* Signatures:

- Prep for new signature algorithms; new SigType enum;
     Signature, SigningPublicKey, SigningPrivateKey store type
   - New Hash384 and Hash512 classes
   - Remove length field in SimpleDataStructure
   - New SU3File generator/verifier/extractor
This commit is contained in:
zzz
2013-07-29 13:49:05 +00:00
parent 6dbd8a6d1a
commit 4340f70d72
11 changed files with 706 additions and 26 deletions

View File

@@ -0,0 +1,31 @@
package net.i2p.crypto;
/*
* free (adj.): unencumbered; not under the control of others
* No warranty of any kind, either expressed or implied.
*/
import net.i2p.data.SimpleDataStructure;
/**
* 48 byte hash
*
* @since 0.9.8
*/
public class Hash384 extends SimpleDataStructure {
public final static int HASH_LENGTH = 48;
public Hash384() {
super();
}
/** @throws IllegalArgumentException if data is not correct length (null is ok) */
public Hash384(byte data[]) {
super(data);
}
public int length() {
return HASH_LENGTH;
}
}

View File

@@ -0,0 +1,31 @@
package net.i2p.crypto;
/*
* free (adj.): unencumbered; not under the control of others
* No warranty of any kind, either expressed or implied.
*/
import net.i2p.data.SimpleDataStructure;
/**
* 64 byte hash
*
* @since 0.9.8
*/
public class Hash512 extends SimpleDataStructure {
public final static int HASH_LENGTH = 64;
public Hash512() {
super();
}
/** @throws IllegalArgumentException if data is not correct length (null is ok) */
public Hash512(byte data[]) {
super(data);
}
public int length() {
return HASH_LENGTH;
}
}

View File

@@ -0,0 +1,425 @@
package net.i2p.crypto;
import java.io.EOFException;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
/**
* Succesor to the ".sud" format used in TrustedUpdate.
* Format specified in http://www.i2p2.de/updates
*
* @since 0.9.8
*/
public class SU3File {
private final I2PAppContext _context;
private final Map<SigningPublicKey, String> _trustedKeys;
private final File _file;
private String _version;
private int _versionLength;
private String _signer;
private int _signerLength;
private int _contentType;
private long _contentLength;
private SigningPublicKey _signerPubkey;
private boolean _headerVerified;
private static final byte[] MAGIC = DataHelper.getUTF8("I2Psu3");
private static final int FILE_VERSION = 0;
private static final int MIN_VERSION_BYTES = 16;
private static final int VERSION_OFFSET = Signature.SIGNATURE_BYTES;
private static final int TYPE_ZIP = 0;
private static final int CONTENT_ROUTER = 0;
private static final int CONTENT_ROUTER_P200 = 1;
private static final int CONTENT_PLUGIN = 2;
private static final int CONTENT_RESEED = 3;
private static final int SIG_DSA_160 = SigType.DSA_SHA1.getCode();
/**
* Uses TrustedUpdate's default keys for verification.
*/
public SU3File(String file) {
this(new File(file));
}
/**
* Uses TrustedUpdate's default keys for verification.
*/
public SU3File(File file) {
this(file, (new TrustedUpdate()).getKeys());
}
/**
* @param trustedKeys map of pubkey to signer name, null ok if not verifying
*/
public SU3File(File file, Map<SigningPublicKey, String> trustedKeys) {
this(I2PAppContext.getGlobalContext(), file, trustedKeys);
}
/**
* @param trustedKeys map of pubkey to signer name, null ok if not verifying
*/
public SU3File(I2PAppContext context, File file, Map<SigningPublicKey, String> trustedKeys) {
_context = context;
_file = file;
_trustedKeys = trustedKeys;
}
public String getVersionString() throws IOException {
verifyHeader();
return _version;
}
public String getSignerString() throws IOException {
verifyHeader();
return _signer;
}
/**
* Throws IOE if verify vails.
*/
public void verifyHeader() throws IOException {
if (_headerVerified)
return;
InputStream in = null;
try {
in = new FileInputStream(_file);
verifyHeader(in);
} catch (DataFormatException dfe) {
IOException ioe = new IOException("foo");
ioe.initCause(dfe);
throw ioe;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
}
/**
* Throws if verify vails.
*/
private void verifyHeader(InputStream in) throws IOException, DataFormatException {
byte[] magic = new byte[MAGIC.length];
DataHelper.read(in, magic);
if (!DataHelper.eq(magic, MAGIC))
throw new IOException("Not an su3 file");
skip(in, 1);
int foo = in.read();
if (foo != FILE_VERSION)
throw new IOException("bad file version");
skip(in, 1);
int sigType = in.read();
// TODO, for other known algos we must start over with a new MessageDigest
// (rewind 10 bytes)
if (sigType != SIG_DSA_160)
throw new IOException("bad sig type");
_signerLength = (int) DataHelper.readLong(in, 2);
if (_signerLength != Signature.SIGNATURE_BYTES)
throw new IOException("bad sig length");
skip(in, 1);
int _versionLength = in.read();
if (_versionLength < MIN_VERSION_BYTES)
throw new IOException("bad version length");
skip(in, 1);
int signerLen = in.read();
if (signerLen <= 0)
throw new IOException("bad signer length");
_contentLength = DataHelper.readLong(in, 8);
if (_contentLength <= 0)
throw new IOException("bad content length");
skip(in, 1);
foo = in.read();
if (foo != TYPE_ZIP)
throw new IOException("bad type");
skip(in, 1);
_contentType = in.read();
if (_contentType < CONTENT_ROUTER || _contentType > CONTENT_RESEED)
throw new IOException("bad content type");
skip(in, 12);
byte[] data = new byte[_versionLength];
int bytesRead = DataHelper.read(in, data);
if (bytesRead != _versionLength)
throw new EOFException();
int zbyte;
for (zbyte = 0; zbyte < _versionLength; zbyte++) {
if (data[zbyte] == 0x00)
break;
}
_version = new String(data, 0, zbyte, "UTF-8");
data = new byte[signerLen];
bytesRead = DataHelper.read(in, data);
if (bytesRead != signerLen)
throw new EOFException();
_signer = DataHelper.getUTF8(data);
if (_trustedKeys != null) {
for (Map.Entry<SigningPublicKey, String> e : _trustedKeys.entrySet()) {
if (e.getValue().equals(_signer)) {
_signerPubkey = e.getKey();
break;
}
}
if (_signerPubkey == null)
throw new IOException("unknown signer: " + _signer);
}
_headerVerified = true;
}
/** skip but update digest */
private static void skip(InputStream in, int cnt) throws IOException {
for (int i = 0; i < cnt; i++) {
if (in.read() < 0)
throw new EOFException();
}
}
private int getContentOffset() throws IOException {
verifyHeader();
return VERSION_OFFSET + _versionLength + _signerLength;
}
/**
* One-pass verify and extract the content.
* Recommend extracting to a temp location as the sig is not checked until
* after extraction. This will delete the file if the sig does not verify.
* Throws IOE on all format errors.
*
* @param migrateTo the output file, probably in zip format
* @return true if signature is good
*/
public boolean verifyAndMigrate(File migrateTo) throws IOException {
DigestInputStream in = null;
OutputStream out = null;
boolean rv = false;
try {
MessageDigest md = SHA1.getInstance();
in = new DigestInputStream(new BufferedInputStream(new FileInputStream(_file)), md);
if (!_headerVerified)
verifyHeader(in);
else
skip(in, getContentOffset());
if (_signerPubkey == null)
throw new IOException("unknown signer: " + _signer);
out = new FileOutputStream(migrateTo);
byte[] buf = new byte[16*1024];
long tot = 0;
while (tot < _contentLength) {
int read = in.read(buf, 0, (int) Math.min(buf.length, _contentLength - tot));
if (read < 0)
throw new EOFException();
out.write(buf, 0, read);
tot += read;
}
byte[] sha = md.digest();
in.on(false);
Signature signature = new Signature();
signature.readBytes(in);
SHA1Hash hash = new SHA1Hash(sha);
rv = _context.dsa().verifySignature(signature, hash, _signerPubkey);
} catch (DataFormatException dfe) {
IOException ioe = new IOException("foo");
ioe.initCause(dfe);
throw ioe;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
if (out != null) try { out.close(); } catch (IOException ioe) {}
if (!rv)
migrateTo.delete();
}
return rv;
}
/**
* One-pass wrap and sign the content.
* Writes to the file specified in the constructor.
* Throws on all errors.
*
* @param content the input file, probably in zip format
* @param contentType 0-255, 0 for zip
* @param version 1-255 bytes when converted to UTF-8
* @param signer ID of the public key, 1-255 bytes when converted to UTF-8
*/
public void write(File content, int contentType, String version,
String signer, SigningPrivateKey privkey) throws IOException {
InputStream in = null;
DigestOutputStream out = null;
boolean ok = false;
try {
in = new BufferedInputStream(new FileInputStream(content));
MessageDigest md = SHA1.getInstance();
out = new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(_file)), md);
out.write(MAGIC);
out.write((byte) 0);
out.write((byte) FILE_VERSION);
out.write((byte) 0);
out.write((byte) SIG_DSA_160);
DataHelper.writeLong(out, 2, Signature.SIGNATURE_BYTES);
out.write((byte) 0);
byte[] verBytes = DataHelper.getUTF8(version);
if (verBytes.length == 0 || verBytes.length > 255)
throw new IllegalArgumentException("bad version length");
int verLen = Math.max(verBytes.length, MIN_VERSION_BYTES);
out.write((byte) verLen);
out.write((byte) 0);
byte[] signerBytes = DataHelper.getUTF8(signer);
if (signerBytes.length == 0 || signerBytes.length > 255)
throw new IllegalArgumentException("bad signer length");
out.write((byte) signerBytes.length);
long contentLength = content.length();
if (contentLength <= 0)
throw new IllegalArgumentException("No content");
DataHelper.writeLong(out, 8, contentLength);
out.write((byte) 0);
out.write((byte) TYPE_ZIP);
out.write((byte) 0);
if (contentType < 0 || contentType > 255)
throw new IllegalArgumentException("bad content type");
out.write((byte) contentType);
out.write(new byte[12]);
out.write(verBytes);
if (verBytes.length < MIN_VERSION_BYTES)
out.write(new byte[MIN_VERSION_BYTES - verBytes.length]);
out.write(signerBytes);
byte[] buf = new byte[16*1024];
long tot = 0;
while (tot < contentLength) {
int read = in.read(buf, 0, (int) Math.min(buf.length, contentLength - tot));
if (read < 0)
throw new EOFException();
out.write(buf, 0, read);
tot += read;
}
byte[] sha = md.digest();
out.on(false);
SHA1Hash hash = new SHA1Hash(sha);
Signature signature = _context.dsa().sign(hash, privkey);
signature.writeBytes(out);
ok = true;
} catch (DataFormatException dfe) {
IOException ioe = new IOException("foo");
ioe.initCause(dfe);
throw ioe;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
if (out != null) try { out.close(); } catch (IOException ioe) {}
if (!ok)
_file.delete();
}
}
/**
* Parses command line arguments when this class is used from the command
* line.
* Exits 1 on failure so this can be used in scripts.
*
* @param args Command line parameters.
*/
public static void main(String[] args) {
boolean ok = false;
try {
if ("showversion".equals(args[0])) {
ok = showVersionCLI(args[1]);
} else if ("sign".equals(args[0])) {
ok = signCLI(args[1], args[2], args[3], args[4], args[5]);
} else if ("verifysig".equals(args[0])) {
ok = verifySigCLI(args[1]);
} else {
showUsageCLI();
}
} catch (ArrayIndexOutOfBoundsException aioobe) {
showUsageCLI();
}
if (!ok)
System.exit(1);
}
private static final void showUsageCLI() {
System.err.println("Usage: SU3File showversion signedFile");
System.err.println(" SU3File sign inputFile signedFile privateKeyFile version signerName");
System.err.println(" SU3File verifysig signedFile");
}
/** @return success */
private static final boolean showVersionCLI(String signedFile) {
try {
SU3File file = new SU3File(new File(signedFile), null);
String versionString = file.getVersionString();
if (versionString.equals(""))
System.out.println("No version string found in file '" + signedFile + "'");
else
System.out.println("Version: " + versionString);
return !versionString.equals("");
} catch (IOException ioe) {
ioe.printStackTrace();
return false;
}
}
/** @return success */
private static final boolean signCLI(String inputFile, String signedFile, String privateKeyFile,
String version, String signerName) {
InputStream in = null;
try {
in = new FileInputStream(privateKeyFile);
SigningPrivateKey spk = new SigningPrivateKey();
spk.readBytes(in);
in.close();
SU3File file = new SU3File(signedFile);
file.write(new File(inputFile), CONTENT_ROUTER, version, signerName, spk);
System.out.println("Input file '" + inputFile + "' signed and written to '" + signedFile + "'");
return true;
} catch (DataFormatException dfe) {
System.out.println("Error signing input file '" + inputFile + "'");
dfe.printStackTrace();
return false;
} catch (IOException ioe) {
System.out.println("Error signing input file '" + inputFile + "'");
ioe.printStackTrace();
return false;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
}
/** @return valid */
private static final boolean verifySigCLI(String signedFile) {
InputStream in = null;
try {
SU3File file = new SU3File(signedFile);
boolean isValidSignature = file.verifyAndMigrate(new File("/dev/null"));
if (isValidSignature)
System.out.println("Signature VALID (signed by " + file.getSignerString() + ')');
else
System.out.println("Signature INVALID (signed by " + file.getSignerString() + ')');
return isValidSignature;
} catch (IOException ioe) {
System.out.println("Error verifying input file '" + signedFile + "'");
ioe.printStackTrace();
return false;
}
}
}

View File

@@ -0,0 +1,91 @@
package net.i2p.crypto;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
/**
* Defines the properties for various signature types
* that I2P supports or may someday support.
*
* @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, "SHA-1", "SHA1withDSA"),
/** Pubkey 40 bytes; privkey 20 bytes; hash 20 bytes; sig 40 bytes */
ECDSA_SHA1(1, 40, 20, 20, 40, "SHA-1", "SHA1withECDSA"),
/** Pubkey 64 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes */
ECDSA_SHA256(2, 64, 32, 32, 64, "SHA-256", "SHA256withECDSA"),
/** Pubkey 96 bytes; privkey 48 bytes; hash 48 bytes; sig 96 bytes */
ECDSA_SHA384(3, 96, 48, 48, 96, "SHA-384", "SHA384withECDSA"),
/** Pubkey 128 bytes; privkey 64 bytes; hash 64 bytes; sig 128 bytes */
ECDSA_SHA512(4, 128, 64, 64, 128, "SHA-512", "SHA512withECDSA")
//MD5
//ELGAMAL_SHA256
//RSA_SHA1
//RSA_SHA256
//RSA_SHA384
//RSA_SHA512
//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 String digestName, algoName;
SigType(int cod, int pubLen, int privLen, int hLen, int sLen, String mdName, String aName) {
code = cod;
pubkeyLen = pubLen;
privkeyLen = privLen;
hashLen = hLen;
sigLen = sLen;
digestName = mdName;
algoName = aName;
}
public int getCode() { return code; }
public int getPubkeyLen() { return pubkeyLen; }
public int getPrivkeyLen() { return privkeyLen; }
public int getHashLen() { return hashLen; }
public int getSigLen() { return sigLen; }
public String getAlgorithmName() { return algoName; }
/** @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);
}
}
private static final Map<Integer, SigType> BY_CODE = new HashMap<Integer, SigType>();
static {
for (SigType type : SigType.values()) {
BY_CODE.put(Integer.valueOf(type.getCode()), type);
}
}
/** @return null if not supported */
public static SigType getByCode(int code) {
return BY_CODE.get(Integer.valueOf(code));
}
}

View File

@@ -8,6 +8,7 @@ import java.io.InputStream;
import java.io.IOException; import java.io.IOException;
import java.io.SequenceInputStream; import java.io.SequenceInputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@@ -209,6 +210,13 @@ riCe6OlAEiNpcc6mMyIYYWFICbrDFTrDR3wXqwc/Jkcx6L5VVWoagpSzbo3yGhc=
_log.debug("TrustedUpdate created, trusting " + _trustedKeys.size() + " keys."); _log.debug("TrustedUpdate created, trusting " + _trustedKeys.size() + " keys.");
} }
/**
* @since 0.9.8
*/
Map<SigningPublicKey, String> getKeys() {
return Collections.unmodifiableMap(_trustedKeys);
}
/** /**
* Duplicate keys or names rejected, * Duplicate keys or names rejected,
* except that duplicate empty names are allowed * except that duplicate empty names are allowed

View File

@@ -9,6 +9,8 @@ package net.i2p.data;
* *
*/ */
import net.i2p.crypto.SigType;
/** /**
* Defines the signature as defined by the I2P data structure spec. * Defines the signature as defined by the I2P data structure spec.
* A signature is a 40-byte array verifying the authenticity of some data * A signature is a 40-byte array verifying the authenticity of some data
@@ -20,19 +22,47 @@ package net.i2p.data;
* @author jrandom * @author jrandom
*/ */
public class Signature extends SimpleDataStructure { public class Signature extends SimpleDataStructure {
public final static int SIGNATURE_BYTES = 40; private static final SigType DEF_TYPE = SigType.DSA_SHA1;
/** 40 */
public final static int SIGNATURE_BYTES = DEF_TYPE.getSigLen();
/** all zeros */ /** all zeros */
public final static byte[] FAKE_SIGNATURE = new byte[SIGNATURE_BYTES]; public final static byte[] FAKE_SIGNATURE = new byte[SIGNATURE_BYTES];
private final SigType _type;
public Signature() { public Signature() {
this(DEF_TYPE);
}
/**
* @since 0.9.8
*/
public Signature(SigType type) {
super(); super();
_type = type;
} }
public Signature(byte data[]) { public Signature(byte data[]) {
super(data); this(DEF_TYPE, data);
}
/**
* @since 0.9.8
*/
public Signature(SigType type, byte data[]) {
super();
_type = type;
setData(data);
} }
public int length() { public int length() {
return SIGNATURE_BYTES; return _type.getSigLen();
}
/**
* @since 0.9.8
*/
public SigType getType() {
return _type;
} }
} }

View File

@@ -10,6 +10,7 @@ package net.i2p.data;
*/ */
import net.i2p.crypto.KeyGenerator; import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SigType;
/** /**
* Defines the SigningPrivateKey as defined by the I2P data structure spec. * Defines the SigningPrivateKey as defined by the I2P data structure spec.
@@ -20,14 +21,34 @@ import net.i2p.crypto.KeyGenerator;
* @author jrandom * @author jrandom
*/ */
public class SigningPrivateKey extends SimpleDataStructure { public class SigningPrivateKey extends SimpleDataStructure {
public final static int KEYSIZE_BYTES = 20; private static final SigType DEF_TYPE = SigType.DSA_SHA1;
public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen();
private final SigType _type;
public SigningPrivateKey() { public SigningPrivateKey() {
this(DEF_TYPE);
}
/**
* @since 0.9.8
*/
public SigningPrivateKey(SigType type) {
super(); super();
_type = type;
} }
public SigningPrivateKey(byte data[]) { public SigningPrivateKey(byte data[]) {
super(data); this(DEF_TYPE, data);
}
/**
* @since 0.9.8
*/
public SigningPrivateKey(SigType type, byte data[]) {
super();
_type = type;
setData(data);
} }
/** constructs from base64 /** constructs from base64
@@ -35,12 +56,20 @@ public class SigningPrivateKey extends SimpleDataStructure {
* on a prior instance of SigningPrivateKey * on a prior instance of SigningPrivateKey
*/ */
public SigningPrivateKey(String base64Data) throws DataFormatException { public SigningPrivateKey(String base64Data) throws DataFormatException {
super(); this();
fromBase64(base64Data); fromBase64(base64Data);
} }
public int length() { public int length() {
return KEYSIZE_BYTES; return _type.getPrivkeyLen();
}
/**
* @since 0.9.8
*/
public SigType getType() {
return _type;
} }
/** converts this signing private key to its public equivalent /** converts this signing private key to its public equivalent

View File

@@ -12,6 +12,8 @@ package net.i2p.data;
import java.io.InputStream; import java.io.InputStream;
import java.io.IOException; import java.io.IOException;
import net.i2p.crypto.SigType;
/** /**
* Defines the SigningPublicKey as defined by the I2P data structure spec. * Defines the SigningPublicKey as defined by the I2P data structure spec.
* A signing public key is 128 byte Integer. The public key represents only the * A signing public key is 128 byte Integer. The public key represents only the
@@ -21,11 +23,14 @@ import java.io.IOException;
* @author jrandom * @author jrandom
*/ */
public class SigningPublicKey extends SimpleDataStructure { public class SigningPublicKey extends SimpleDataStructure {
public final static int KEYSIZE_BYTES = 128; private static final SigType DEF_TYPE = SigType.DSA_SHA1;
public final static int KEYSIZE_BYTES = DEF_TYPE.getPubkeyLen();
private static final int CACHE_SIZE = 1024; private static final int CACHE_SIZE = 1024;
private static final SDSCache<SigningPublicKey> _cache = new SDSCache(SigningPublicKey.class, KEYSIZE_BYTES, CACHE_SIZE); private static final SDSCache<SigningPublicKey> _cache = new SDSCache(SigningPublicKey.class, KEYSIZE_BYTES, CACHE_SIZE);
private final SigType _type;
/** /**
* Pull from cache or return new * Pull from cache or return new
* @throws AIOOBE if not enough bytes * @throws AIOOBE if not enough bytes
@@ -44,11 +49,28 @@ public class SigningPublicKey extends SimpleDataStructure {
} }
public SigningPublicKey() { public SigningPublicKey() {
this(DEF_TYPE);
}
/**
* @since 0.9.8
*/
public SigningPublicKey(SigType type) {
super(); super();
_type = type;
} }
public SigningPublicKey(byte data[]) { public SigningPublicKey(byte data[]) {
super(data); this(DEF_TYPE, data);
}
/**
* @since 0.9.8
*/
public SigningPublicKey(SigType type, byte data[]) {
super();
_type = type;
setData(data);
} }
/** constructs from base64 /** constructs from base64
@@ -56,11 +78,18 @@ public class SigningPublicKey extends SimpleDataStructure {
* on a prior instance of SigningPublicKey * on a prior instance of SigningPublicKey
*/ */
public SigningPublicKey(String base64Data) throws DataFormatException { public SigningPublicKey(String base64Data) throws DataFormatException {
super(); this();
fromBase64(base64Data); fromBase64(base64Data);
} }
public int length() { public int length() {
return KEYSIZE_BYTES; return _type.getPubkeyLen();
}
/**
* @since 0.9.8
*/
public SigType getType() {
return _type;
} }
} }

View File

@@ -31,17 +31,13 @@ import net.i2p.crypto.SHA256Generator;
*/ */
public abstract class SimpleDataStructure extends DataStructureImpl { public abstract class SimpleDataStructure extends DataStructureImpl {
protected byte[] _data; protected byte[] _data;
/** this is just to avoid lots of calls to length() */
protected final int _length;
/** A new instance with the data set to null. Call readBytes(), setData(), or fromByteArray() after this to set the data */ /** A new instance with the data set to null. Call readBytes(), setData(), or fromByteArray() after this to set the data */
public SimpleDataStructure() { public SimpleDataStructure() {
_length = length();
} }
/** @throws IllegalArgumentException if data is not the legal number of bytes (but null is ok) */ /** @throws IllegalArgumentException if data is not the legal number of bytes (but null is ok) */
public SimpleDataStructure(byte data[]) { public SimpleDataStructure(byte data[]) {
_length = length();
setData(data); setData(data);
} }
@@ -68,8 +64,8 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
public void setData(byte[] data) { public void setData(byte[] data) {
if (_data != null) if (_data != null)
throw new RuntimeException("Data already set"); throw new RuntimeException("Data already set");
if (data != null && data.length != _length) if (data != null && data.length != length())
throw new IllegalArgumentException("Bad data length: " + data.length + "; required: " + _length); throw new IllegalArgumentException("Bad data length: " + data.length + "; required: " + length());
_data = data; _data = data;
} }
@@ -81,9 +77,10 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_data != null) if (_data != null)
throw new RuntimeException("Data already set"); throw new RuntimeException("Data already set");
_data = new byte[_length]; int length = length();
_data = new byte[length];
int read = read(in, _data); int read = read(in, _data);
if (read != _length) throw new DataFormatException("Not enough bytes to read the data"); if (read != length) throw new DataFormatException("Not enough bytes to read the data");
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
@@ -109,8 +106,8 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
byte[] d = Base64.decode(data); byte[] d = Base64.decode(data);
if (d == null) if (d == null)
throw new DataFormatException("Bad Base64 encoded data"); throw new DataFormatException("Bad Base64 encoded data");
if (d.length != _length) if (d.length != length())
throw new DataFormatException("Bad decoded data length, expected " + _length + " got " + d.length); throw new DataFormatException("Bad decoded data length, expected " + length() + " got " + d.length);
// call setData() instead of _data = data in case overridden // call setData() instead of _data = data in case overridden
setData(d); setData(d);
} }
@@ -141,8 +138,8 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
@Override @Override
public void fromByteArray(byte data[]) throws DataFormatException { public void fromByteArray(byte data[]) throws DataFormatException {
if (data == null) throw new DataFormatException("Null data passed in"); if (data == null) throw new DataFormatException("Null data passed in");
if (data.length != _length) if (data.length != length())
throw new DataFormatException("Bad data length: " + data.length + "; required: " + _length); throw new DataFormatException("Bad data length: " + data.length + "; required: " + length());
// call setData() instead of _data = data in case overridden // call setData() instead of _data = data in case overridden
setData(data); setData(data);
} }
@@ -151,12 +148,13 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
public String toString() { public String toString() {
StringBuilder buf = new StringBuilder(64); StringBuilder buf = new StringBuilder(64);
buf.append('[').append(getClass().getSimpleName()).append(": "); buf.append('[').append(getClass().getSimpleName()).append(": ");
int length = length();
if (_data == null) { if (_data == null) {
buf.append("null"); buf.append("null");
} else if (_length <= 32) { } else if (length <= 32) {
buf.append(toBase64()); buf.append(toBase64());
} else { } else {
buf.append("size: ").append(Integer.toString(_length)); buf.append("size: ").append(Integer.toString(length));
} }
buf.append(']'); buf.append(']');
return buf.toString(); return buf.toString();

View File

@@ -1,3 +1,11 @@
2012-07-29 zzz
* Signatures:
- Prep for new signature algorithms; new SigType enum;
Signature, SigningPublicKey, SigningPrivateKey store type
- New Hash384 and Hash512 classes
- Remove length field in SimpleDataStructure
- New SU3File generator/verifier/extractor
2012-07-28 zzz 2012-07-28 zzz
* Addresses: Treat RFC 4193 addresses fc00::/7 as local * Addresses: Treat RFC 4193 addresses fc00::/7 as local
* NetDB: Disable RI verifies for now * NetDB: Disable RI verifies for now

View File

@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */ /** deprecated */
public final static String ID = "Monotone"; public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION; public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 13; public final static long BUILD = 14;
/** for example "-test" */ /** for example "-test" */
public final static String EXTRA = ""; public final static String EXTRA = "";