propagate from branch 'i2p.i2p.zzz.test2' (head c182b371bc28158dd47262b89e5bd0cdda29e07b)

to branch 'i2p.i2p' (head 4fc776f7b76b028bb890affccfdcfbefbb932c58)
This commit is contained in:
zzz
2014-08-10 13:56:15 +00:00
30 changed files with 594 additions and 157 deletions

View File

@ -22,7 +22,6 @@ import net.i2p.crypto.HMACGenerator;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SHA256Generator;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.crypto.TransientSessionKeyManager;
import net.i2p.data.Base64;
import net.i2p.data.RoutingKeyGenerator;
import net.i2p.internal.InternalClientManager;
@ -76,7 +75,7 @@ public class I2PAppContext {
protected final I2PProperties _overrideProps;
private StatManager _statManager;
private SessionKeyManager _sessionKeyManager;
protected SessionKeyManager _sessionKeyManager;
private NamingService _namingService;
private ElGamalEngine _elGamalEngine;
private ElGamalAESEngine _elGamalAESEngine;
@ -96,7 +95,7 @@ public class I2PAppContext {
private SimpleTimer2 _simpleTimer2;
private final PortMapper _portMapper;
private volatile boolean _statManagerInitialized;
private volatile boolean _sessionKeyManagerInitialized;
protected volatile boolean _sessionKeyManagerInitialized;
private volatile boolean _namingServiceInitialized;
private volatile boolean _elGamalEngineInitialized;
private volatile boolean _elGamalAESEngineInitialized;
@ -599,6 +598,9 @@ public class I2PAppContext {
* For client crypto within the router,
* use RouterContext.clientManager.getClientSessionKeyManager(dest)
*
* As of 0.9.15, this returns a dummy SessionKeyManager in I2PAppContext.
* The dummy SKM does NOT handle session tags.
* Overridden in RouterContext to return the full TransientSessionKeyManager.
*/
public SessionKeyManager sessionKeyManager() {
if (!_sessionKeyManagerInitialized)
@ -606,11 +608,11 @@ public class I2PAppContext {
return _sessionKeyManager;
}
private void initializeSessionKeyManager() {
protected void initializeSessionKeyManager() {
synchronized (_lock3) {
if (_sessionKeyManager == null)
//_sessionKeyManager = new PersistentSessionKeyManager(this);
_sessionKeyManager = new TransientSessionKeyManager(this);
_sessionKeyManager = new SessionKeyManager(this);
_sessionKeyManagerInitialized = true;
}
}

View File

@ -15,6 +15,8 @@ import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
@ -49,29 +51,36 @@ public class SU3File {
private int _versionLength;
private String _signer;
private int _signerLength;
private int _fileType = -1;
private ContentType _contentType;
private long _contentLength;
private PublicKey _signerPubkey;
private boolean _headerVerified;
private SigType _sigType;
private boolean _verifySignature = true;
private File _certFile;
private static final byte[] MAGIC = DataHelper.getUTF8("I2Psu3");
public static final String MAGIC = "I2Psu3";
private static final byte[] MAGIC_BYTES = DataHelper.getASCII(MAGIC);
private static final int FILE_VERSION = 0;
private static final int MIN_VERSION_BYTES = 16;
private static final int VERSION_OFFSET = 40; // Signature.SIGNATURE_BYTES; avoid early ctx init
private static final int TYPE_ZIP = 0;
public static final int TYPE_ZIP = 0;
public static final int TYPE_XML = 1;
public static final int CONTENT_UNKNOWN = 0;
public static final int CONTENT_ROUTER = 1;
public static final int CONTENT_PLUGIN = 2;
public static final int CONTENT_RESEED = 3;
public static final int CONTENT_NEWS = 4;
private enum ContentType {
UNKNOWN(CONTENT_UNKNOWN, "unknown"),
ROUTER(CONTENT_ROUTER, "router"),
PLUGIN(CONTENT_PLUGIN, "plugin"),
RESEED(CONTENT_RESEED, "reseed")
RESEED(CONTENT_RESEED, "reseed"),
NEWS(CONTENT_NEWS, "news")
;
private final int code;
@ -125,17 +134,44 @@ public class SU3File {
_file = file;
}
/**
* Should the signature be verified? Default true
* @since 0.9.15
*/
public void setVerifySignature(boolean shouldVerify) {
_verifySignature = shouldVerify;
}
/**
* Use this X.509 cert file for verification instead of $I2P/certificates/content_type/foo_at_mail.i2p
* @since 0.9.15
*/
private void setPublicKeyCertificate(File certFile) {
_certFile = certFile;
}
/**
* This does not check the signature, but it will fail if the signer is unknown,
* unless setVerifySignature(false) has been called.
*/
public String getVersionString() throws IOException {
verifyHeader();
return _version;
}
/**
* This does not check the signature, but it will fail if the signer is unknown,
* unless setVerifySignature(false) has been called.
*/
public String getSignerString() throws IOException {
verifyHeader();
return _signer;
}
/**
* This does not check the signature, but it will fail if the signer is unknown,
* unless setVerifySignature(false) has been called.
*
* @return null if unknown
* @since 0.9.9
*/
@ -145,6 +181,9 @@ public class SU3File {
}
/**
* This does not check the signature, but it will fail if the signer is unknown,
* unless setVerifySignature(false) has been called.
*
* @return -1 if unknown
* @since 0.9.9
*/
@ -154,6 +193,21 @@ public class SU3File {
}
/**
* This does not check the signature, but it will fail if the signer is unknown,
* unless setVerifySignature(false) has been called.
*
* @return -1 if unknown
* @since 0.9.15
*/
public int getFileType() throws IOException {
verifyHeader();
return _fileType;
}
/**
* This does not check the signature, but it will fail if the signer is unknown,
* unless setVerifySignature(false) has been called.
*
* Throws IOE if verify vails.
*/
public void verifyHeader() throws IOException {
@ -176,9 +230,9 @@ public class SU3File {
* Throws if verify vails.
*/
private void verifyHeader(InputStream in) throws IOException, DataFormatException {
byte[] magic = new byte[MAGIC.length];
byte[] magic = new byte[MAGIC_BYTES.length];
DataHelper.read(in, magic);
if (!DataHelper.eq(magic, MAGIC))
if (!DataHelper.eq(magic, MAGIC_BYTES))
throw new IOException("Not an su3 file");
skip(in, 1);
int foo = in.read();
@ -204,9 +258,9 @@ public class SU3File {
if (_contentLength <= 0)
throw new IOException("bad content length");
skip(in, 1);
foo = in.read();
if (foo != TYPE_ZIP)
throw new IOException("bad type");
_fileType = in.read();
if (_fileType != TYPE_ZIP && _fileType != TYPE_XML)
throw new IOException("bad file type");
skip(in, 1);
int cType = in.read();
_contentType = BY_CODE.get(Integer.valueOf(cType));
@ -231,17 +285,22 @@ public class SU3File {
throw new EOFException();
_signer = DataHelper.getUTF8(data);
KeyRing ring = new DirKeyRing(new File(_context.getBaseDir(), "certificates"));
try {
_signerPubkey = ring.getKey(_signer, _contentType.getName(), _sigType);
} catch (GeneralSecurityException gse) {
IOException ioe = new IOException("keystore error");
ioe.initCause(gse);
throw ioe;
if (_verifySignature) {
if (_certFile != null) {
_signerPubkey = loadKey(_certFile);
} else {
KeyRing ring = new DirKeyRing(new File(_context.getBaseDir(), "certificates"));
try {
_signerPubkey = ring.getKey(_signer, _contentType.getName(), _sigType);
} catch (GeneralSecurityException gse) {
IOException ioe = new IOException("keystore error");
ioe.initCause(gse);
throw ioe;
}
if (_signerPubkey == null)
throw new IOException("unknown signer: " + _signer + " for content type: " + _contentType.getName());
}
}
if (_signerPubkey == null)
throw new IOException("unknown signer: " + _signer);
_headerVerified = true;
}
@ -287,9 +346,9 @@ public class SU3File {
// read 10 bytes to get the sig type
in.mark(10);
// following is a dup of that in verifyHeader()
byte[] magic = new byte[MAGIC.length];
byte[] magic = new byte[MAGIC_BYTES.length];
DataHelper.read(in, magic);
if (!DataHelper.eq(magic, MAGIC))
if (!DataHelper.eq(magic, MAGIC_BYTES))
throw new IOException("Not an su3 file");
skip(in, 1);
int foo = in.read();
@ -310,8 +369,10 @@ public class SU3File {
verifyHeader(in);
else
skip(in, getContentOffset());
if (_signerPubkey == null)
throw new IOException("unknown signer: " + _signer);
if (_verifySignature) {
if (_signerPubkey == null)
throw new IOException("unknown signer: " + _signer + " for content type: " + _contentType.getName());
}
if (migrateTo != null) // else verify only
out = new FileOutputStream(migrateTo);
byte[] buf = new byte[16*1024];
@ -324,15 +385,19 @@ public class SU3File {
out.write(buf, 0, read);
tot += read;
}
byte[] sha = md.digest();
din.on(false);
Signature signature = new Signature(_sigType);
signature.readBytes(in);
SimpleDataStructure hash = _sigType.getHashInstance();
hash.setData(sha);
//System.out.println("hash\n" + HexDump.dump(sha));
//System.out.println("sig\n" + HexDump.dump(signature.getData()));
rv = _context.dsa().verifySignature(signature, hash, _signerPubkey);
if (_verifySignature) {
byte[] sha = md.digest();
din.on(false);
Signature signature = new Signature(_sigType);
signature.readBytes(in);
SimpleDataStructure hash = _sigType.getHashInstance();
hash.setData(sha);
//System.out.println("hash\n" + HexDump.dump(sha));
//System.out.println("sig\n" + HexDump.dump(signature.getData()));
rv = _context.dsa().verifySignature(signature, hash, _signerPubkey);
} else {
rv = true;
}
} catch (DataFormatException dfe) {
IOException ioe = new IOException("foo");
ioe.initCause(dfe);
@ -352,11 +417,12 @@ public class SU3File {
* Throws on all errors.
*
* @param content the input file, probably in zip format
* @param contentType 0-255, 0 for zip
* @param fileType 0-255, 0 for zip
* @param contentType 0-255
* @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,
public void write(File content, int fileType, int contentType, String version,
String signer, PrivateKey privkey, SigType sigType) throws IOException {
InputStream in = null;
DigestOutputStream out = null;
@ -365,7 +431,7 @@ public class SU3File {
in = new BufferedInputStream(new FileInputStream(content));
MessageDigest md = sigType.getDigestInstance();
out = new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(_file)), md);
out.write(MAGIC);
out.write(MAGIC_BYTES);
out.write((byte) 0);
out.write((byte) FILE_VERSION);
DataHelper.writeLong(out, 2, sigType.getCode());
@ -386,7 +452,9 @@ public class SU3File {
throw new IllegalArgumentException("No content");
DataHelper.writeLong(out, 8, contentLength);
out.write((byte) 0);
out.write((byte) TYPE_ZIP);
if (fileType < 0 || fileType > 255)
throw new IllegalArgumentException("bad content type");
out.write((byte) fileType);
out.write((byte) 0);
if (contentType < 0 || contentType > 255)
throw new IllegalArgumentException("bad content type");
@ -443,8 +511,11 @@ public class SU3File {
// defaults
String stype = null;
String ctype = null;
String ftype = null;
String kfile = null;
boolean error = false;
Getopt g = new Getopt("SU3File", args, "t:c:");
boolean shouldVerify = true;
Getopt g = new Getopt("SU3File", args, "t:c:f:k:x");
int c;
while ((c = g.getopt()) != -1) {
switch (c) {
@ -456,6 +527,18 @@ public class SU3File {
ctype = g.getOptarg();
break;
case 'f':
ftype = g.getOptarg();
break;
case 'k':
kfile = g.getOptarg();
break;
case 'x':
shouldVerify = false;
break;
case '?':
case ':':
default:
@ -476,18 +559,18 @@ public class SU3File {
Properties props = new Properties();
props.setProperty("prng.bufferSize", "16384");
new I2PAppContext(props);
ok = signCLI(stype, ctype, a.get(0), a.get(1), a.get(2), a.get(3), a.get(4), "");
ok = signCLI(stype, ctype, ftype, a.get(0), a.get(1), a.get(2), a.get(3), a.get(4), "");
} else if ("bulksign".equals(cmd)) {
Properties props = new Properties();
props.setProperty("prng.bufferSize", "16384");
new I2PAppContext(props);
ok = bulkSignCLI(stype, ctype, a.get(0), a.get(1), a.get(2), a.get(3));
} else if ("verifysig".equals(cmd)) {
ok = verifySigCLI(a.get(0));
ok = verifySigCLI(a.get(0), kfile);
} else if ("keygen".equals(cmd)) {
ok = genKeysCLI(stype, a.get(0), a.get(1), a.get(2));
} else if ("extract".equals(cmd)) {
ok = extractCLI(a.get(0), a.get(1));
ok = extractCLI(a.get(0), a.get(1), shouldVerify);
} else {
showUsageCLI();
}
@ -502,11 +585,11 @@ public class SU3File {
private static final void showUsageCLI() {
System.err.println("Usage: SU3File keygen [-t type|code] publicKeyFile keystore.ks you@mail.i2p");
System.err.println(" SU3File sign [-c type|code] [-t type|code] inputFile.zip signedFile.su3 keystore.ks version you@mail.i2p");
System.err.println(" SU3File bulksign [-c type|code] [-t type|code] directory keystore.ks version you@mail.i2p");
System.err.println(" SU3File sign [-t type|code] [-c type|code] [-f type|code] inputFile.zip signedFile.su3 keystore.ks version you@mail.i2p");
System.err.println(" SU3File bulksign [-t type|code] [-c type|code] directory keystore.ks version you@mail.i2p");
System.err.println(" SU3File showversion signedFile.su3");
System.err.println(" SU3File verifysig signedFile.su3");
System.err.println(" SU3File extract signedFile.su3 outFile.zip");
System.err.println(" SU3File verifysig [-k file.crt] signedFile.su3 ## -k use this pubkey cert for verification");
System.err.println(" SU3File extract [-x] [-k file.crt] signedFile.su3 outFile ## -x don't check sig");
System.err.println(dumpTypes());
}
@ -529,6 +612,9 @@ public class SU3File {
buf.append(" DEFAULT");
buf.append('\n');
}
buf.append("Available file types (-f):\n");
buf.append(" ZIP\t(code: 0) DEFAULT\n");
buf.append(" XML\t(code: 1)\n");
return buf.toString();
}
@ -554,6 +640,7 @@ public class SU3File {
private static final boolean showVersionCLI(String signedFile) {
try {
SU3File file = new SU3File(signedFile);
file.setVerifySignature(false);
String versionString = file.getVersionString();
if (versionString.equals(""))
System.out.println("No version string found in file '" + signedFile + "'");
@ -574,6 +661,7 @@ public class SU3File {
}
/**
* Zip only
* @return success
* @since 0.9.9
*/
@ -593,7 +681,12 @@ public class SU3File {
try {
while (keypw.length() < 6) {
System.out.print("Enter password for key \"" + signerName + "\": ");
keypw = DataHelper.readLine(System.in).trim();
keypw = DataHelper.readLine(System.in);
if (keypw == null) {
System.out.println("\nEOF reading password");
return false;
}
keypw = keypw.trim();
if (keypw.length() > 0 && keypw.length() < 6)
System.out.println("Key password must be at least 6 characters");
}
@ -608,7 +701,7 @@ public class SU3File {
if (!inputFile.endsWith(".zip"))
continue;
String signedFile = inputFile.substring(0, inputFile.length() - 4) + ".su3";
boolean rv = signCLI(stype, ctype, inputFile, signedFile, privateKeyFile, version, signerName, keypw);
boolean rv = signCLI(stype, ctype, null, inputFile, signedFile, privateKeyFile, version, signerName, keypw);
if (!rv)
return false;
success++;
@ -622,7 +715,7 @@ public class SU3File {
* @return success
* @since 0.9.9
*/
private static final boolean signCLI(String stype, String ctype, String inputFile, String signedFile,
private static final boolean signCLI(String stype, String ctype, String ftype, String inputFile, String signedFile,
String privateKeyFile, String version, String signerName, String keypw) {
SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype);
if (type == null) {
@ -634,19 +727,42 @@ public class SU3File {
System.out.println("Content type " + ctype + " is not supported");
return false;
}
return signCLI(type, ct, inputFile, signedFile, privateKeyFile, version, signerName, keypw);
int ft = TYPE_ZIP;
if (ftype != null) {
if (ftype.equalsIgnoreCase("ZIP")) {
ft = TYPE_ZIP;
} else if (ftype.equalsIgnoreCase("XML")) {
ft = TYPE_XML;
} else {
try {
ft = Integer.parseInt(ftype);
} catch (NumberFormatException nfe) {
ft = -1;
}
if (ft != TYPE_ZIP && ft != TYPE_XML) {
System.out.println("File type " + ftype + " is not supported");
return false;
}
}
}
return signCLI(type, ct, ft, inputFile, signedFile, privateKeyFile, version, signerName, keypw);
}
/**
* @return success
* @since 0.9.9
*/
private static final boolean signCLI(SigType type, ContentType ctype, String inputFile, String signedFile,
private static final boolean signCLI(SigType type, ContentType ctype, int ftype, String inputFile, String signedFile,
String privateKeyFile, String version, String signerName, String keypw) {
try {
while (keypw.length() < 6) {
System.out.print("Enter password for key \"" + signerName + "\": ");
keypw = DataHelper.readLine(System.in).trim();
keypw = DataHelper.readLine(System.in);
if (keypw == null) {
System.out.println("\nEOF reading password");
return false;
}
keypw = keypw.trim();
if (keypw.length() > 0 && keypw.length() < 6)
System.out.println("Key password must be at least 6 characters");
}
@ -657,7 +773,7 @@ public class SU3File {
return false;
}
SU3File file = new SU3File(signedFile);
file.write(new File(inputFile), ctype.getCode(), version, signerName, pk, type);
file.write(new File(inputFile), ftype, ctype.getCode(), version, signerName, pk, type);
System.out.println("Input file '" + inputFile + "' signed and written to '" + signedFile + "'");
return true;
} catch (GeneralSecurityException gse) {
@ -672,10 +788,12 @@ public class SU3File {
}
/** @return valid */
private static final boolean verifySigCLI(String signedFile) {
private static final boolean verifySigCLI(String signedFile, String pkFile) {
InputStream in = null;
try {
SU3File file = new SU3File(signedFile);
if (pkFile != null)
file.setPublicKeyCertificate(new File(pkFile));
boolean isValidSignature = file.verify();
if (isValidSignature)
System.out.println("Signature VALID (signed by " + file.getSignerString() + ' ' + file._sigType + ')');
@ -693,10 +811,11 @@ public class SU3File {
* @return success
* @since 0.9.9
*/
private static final boolean extractCLI(String signedFile, String outFile) {
private static final boolean extractCLI(String signedFile, String outFile, boolean verifySig) {
InputStream in = null;
try {
SU3File file = new SU3File(signedFile);
file.setVerifySignature(verifySig);
File out = new File(outFile);
boolean ok = file.verifyAndMigrate(out);
if (ok)
@ -740,11 +859,21 @@ public class SU3File {
try {
while (alias.length() == 0) {
System.out.print("Enter key name (example@mail.i2p): ");
alias = DataHelper.readLine(System.in).trim();
alias = DataHelper.readLine(System.in);
if (alias == null) {
System.out.println("\nEOF reading key name");
return false;
}
alias = alias.trim();
}
while (keypw.length() < 6) {
System.out.print("Enter new key password: ");
keypw = DataHelper.readLine(System.in).trim();
keypw = DataHelper.readLine(System.in);
if (keypw == null) {
System.out.println("\nEOF reading password");
return false;
}
keypw = keypw.trim();
if (keypw.length() > 0 && keypw.length() < 6)
System.out.println("Key password must be at least 6 characters");
}
@ -772,4 +901,26 @@ public class SU3File {
}
return true;
}
/**
* For the -k CLI option
* @return non-null, throws IOE on all errors
* @since 0.9.15
*/
private static PublicKey loadKey(File kd) throws IOException {
InputStream fis = null;
try {
fis = new FileInputStream(kd);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
cert.checkValidity();
return cert.getPublicKey();
} catch (GeneralSecurityException gse) {
IOException ioe = new IOException("cert error");
ioe.initCause(gse);
throw ioe;
} finally {
try { if (fis != null) fis.close(); } catch (IOException foo) {}
}
}
}

View File

@ -178,6 +178,29 @@ public enum SigType {
return true;
}
/**
* @return true if supported in this JVM
* @since 0.9.15
*/
public static boolean isAvailable(int code) {
SigType type = getByCode(code);
if (type == null)
return false;
return type.isAvailable();
}
/**
* @param stype number or name
* @return true if supported in this JVM
* @since 0.9.15
*/
public static boolean isAvailable(String stype) {
SigType type = parseSigType(stype);
if (type == null)
return false;
return type.isAvailable();
}
private static final Map<Integer, SigType> BY_CODE = new HashMap<Integer, SigType>();
static {

File diff suppressed because it is too large Load Diff

View File

@ -56,6 +56,7 @@ public class Base32 {
};
private final static byte BAD_ENCODING = -9; // Indicates error in encoding
/** Defeats instantiation. */
private Base32() { // nop
}
@ -136,6 +137,9 @@ public class Base32 {
}
/**
* Returns lower case.
* Does not add trailing '='.
*
* @param source if null will return ""
*/
public static String encode(String source) {
@ -143,6 +147,9 @@ public class Base32 {
}
/**
* Returns lower case.
* Does not add trailing '='.
*
* @param source The data to convert non-null
*/
public static String encode(byte[] source) {
@ -182,6 +189,8 @@ public class Base32 {
/**
* Decodes data from Base32 notation and
* returns it as a string.
* Case-insensitive.
* Does not allow trailing '='.
*
* @param s the string to decode, if null returns null
* @return The data as a string or null on failure
@ -194,6 +203,9 @@ public class Base32 {
}
/**
* Case-insensitive.
* Does not allow trailing '='.
*
* @param s non-null
* @return decoded data, null on error
*/

View File

@ -130,17 +130,19 @@ public class KeysAndCert extends DataStructureImpl {
&& DataHelper.eq(_certificate, ident._certificate);
}
/** the public key has enough randomness in it to use it by itself for speed */
/** the signing key has enough randomness in it to use it by itself for speed */
@Override
public int hashCode() {
if (_publicKey == null)
// don't use public key, some app devs thinking of using
// an all-zeros or leading-zeros public key for destinations
if (_signingKey == null)
return 0;
return _publicKey.hashCode();
return _signingKey.hashCode();
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
StringBuilder buf = new StringBuilder(256);
buf.append('[').append(getClass().getSimpleName()).append(": ");
buf.append("\n\tHash: ").append(getHash().toBase64());
buf.append("\n\tCertificate: ").append(_certificate);