2005-04-08 smeghead
* Security improvements to TrustedUpdate: signing and verification of the version string along with the data payload for signed update files (consequently the positions of the DSA signature and version string fields have been swapped in the spec for the update file's header); router will no longer perform a trusted update if the signed update's version is lower than or equal to the currently running router's version. * Added two new CLI commands to TrustedUpdate: showversion, verifyupdate. * Extended TrustedUpdate public API for use by third party applications.
This commit is contained in:
@ -7,6 +7,7 @@ import java.util.List;
|
|||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.crypto.TrustedUpdate;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.router.RouterVersion;
|
import net.i2p.router.RouterVersion;
|
||||||
@ -136,7 +137,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
String ver = buf.substring(index+VERSION_PREFIX.length(), end);
|
String ver = buf.substring(index+VERSION_PREFIX.length(), end);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Found version: [" + ver + "]");
|
_log.debug("Found version: [" + ver + "]");
|
||||||
if (needsUpdate(ver)) {
|
if (TrustedUpdate.needsUpdate(RouterVersion.VERSION, ver)) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Our version is out of date, update!");
|
_log.debug("Our version is out of date, update!");
|
||||||
break;
|
break;
|
||||||
@ -191,54 +192,6 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean needsUpdate(String version) {
|
|
||||||
StringTokenizer newTok = new StringTokenizer(sanitize(version), ".");
|
|
||||||
StringTokenizer ourTok = new StringTokenizer(sanitize(RouterVersion.VERSION), ".");
|
|
||||||
|
|
||||||
while (newTok.hasMoreTokens() && ourTok.hasMoreTokens()) {
|
|
||||||
String newVer = newTok.nextToken();
|
|
||||||
String oldVer = ourTok.nextToken();
|
|
||||||
switch (compare(newVer, oldVer)) {
|
|
||||||
case -1: // newVer is smaller
|
|
||||||
return false;
|
|
||||||
case 0: // eq
|
|
||||||
break;
|
|
||||||
case 1: // newVer is larger
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newTok.hasMoreTokens() && !ourTok.hasMoreTokens())
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String VALID = "0123456789.";
|
|
||||||
private static final String sanitize(String str) {
|
|
||||||
StringBuffer buf = new StringBuffer(str);
|
|
||||||
for (int i = 0; i < buf.length(); i++) {
|
|
||||||
if (VALID.indexOf(buf.charAt(i)) == -1) {
|
|
||||||
buf.deleteCharAt(i);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int compare(String lhs, String rhs) {
|
|
||||||
try {
|
|
||||||
int left = Integer.parseInt(lhs);
|
|
||||||
int right = Integer.parseInt(rhs);
|
|
||||||
if (left < right)
|
|
||||||
return -1;
|
|
||||||
else if (left == right)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
|
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,25 @@ package net.i2p.router.web;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
import net.i2p.crypto.TrustedUpdate;
|
import net.i2p.crypto.TrustedUpdate;
|
||||||
import net.i2p.router.Router;
|
import net.i2p.router.Router;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.util.I2PThread;
|
import net.i2p.router.RouterVersion;
|
||||||
import net.i2p.util.EepGet;
|
import net.i2p.util.EepGet;
|
||||||
|
import net.i2p.util.I2PThread;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the request to update the router by firing off an EepGet call and
|
* <p>Handles the request to update the router by firing off an
|
||||||
* displaying its status to anyone who asks. After the download completes,
|
* {@link net.i2p.util.EepGet} call to download the latest signed update file
|
||||||
* it is verified with the TrustedUpdate, and if it is authentic, the router
|
* and displaying the status to anyone who asks.
|
||||||
* is restarted.
|
* </p>
|
||||||
*
|
* <p>After the download completes the signed update file is verified with
|
||||||
|
* {@link net.i2p.crypto.TrustedUpdate}, and if it's authentic the payload
|
||||||
|
* of the signed update file is unpacked and the router is restarted to complete
|
||||||
|
* the update process.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class UpdateHandler {
|
public class UpdateHandler {
|
||||||
private static UpdateRunner _updateRunner;
|
private static UpdateRunner _updateRunner;
|
||||||
@ -140,7 +146,7 @@ public class UpdateHandler {
|
|||||||
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
|
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
|
||||||
_status = "<b>Update downloaded</b><br />";
|
_status = "<b>Update downloaded</b><br />";
|
||||||
TrustedUpdate up = new TrustedUpdate(_context);
|
TrustedUpdate up = new TrustedUpdate(_context);
|
||||||
boolean ok = up.migrateVerified(SIGNED_UPDATE_FILE, "i2pupdate.zip");
|
boolean ok = up.migrateVerified(RouterVersion.VERSION, SIGNED_UPDATE_FILE, "i2pupdate.zip");
|
||||||
File f = new File(SIGNED_UPDATE_FILE);
|
File f = new File(SIGNED_UPDATE_FILE);
|
||||||
f.delete();
|
f.delete();
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
package net.i2p.crypto;
|
package net.i2p.crypto;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.SequenceInputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import net.i2p.CoreVersion;
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
@ -18,15 +19,28 @@ import net.i2p.data.SigningPublicKey;
|
|||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles DSA signing and verification of I2P update archives.
|
* <p>Handles DSA signing and verification of update files.
|
||||||
|
* </p>
|
||||||
|
* <p>For convenience this class also makes certain operations available via the
|
||||||
|
* command line. These can be invoked as follows:
|
||||||
|
* </p>
|
||||||
|
* <pre>
|
||||||
|
* java net.i2p.crypto.TrustedUpdate keygen <i>publicKeyFile privateKeyFile</i>
|
||||||
|
* java net.i2p.crypto.TrustedUpdate showversion <i>signedFile</i>
|
||||||
|
* java net.i2p.crypto.TrustedUpdate sign <i>inputFile signedFile privateKeyFile version</i>
|
||||||
|
* java net.i2p.crypto.TrustedUpdate verifysig <i>signedFile</i>
|
||||||
|
* java net.i2p.crypto.TrustedUpdate verifyupdate <i>signedFile</i>
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* @author smeghead
|
* @author jrandom and smeghead
|
||||||
*/
|
*/
|
||||||
public class TrustedUpdate {
|
public class TrustedUpdate {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* default trusted key, generated by jrandom. This can be authenticated
|
* <p>Default trusted key generated by jrandom@i2p.net. This can be
|
||||||
* via gpg without modification (gpg --verify TrustedUpdate.java)
|
* authenticated via <code>gpg</code> without modification:</p>
|
||||||
*
|
* <p>
|
||||||
|
* <code>gpg --verify TrustedUpdate.java</code></p>
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
-----BEGIN PGP SIGNED MESSAGE-----
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
@ -47,334 +61,575 @@ CPah6TDXYJCWmR0n3oPtrvo=
|
|||||||
-----END PGP SIGNATURE-----
|
-----END PGP SIGNATURE-----
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private static final String VALID_VERSION_CHARS = "0123456789.";
|
||||||
|
private static final int VERSION_BYTES = 16;
|
||||||
|
private static final int HEADER_BYTES = Signature.SIGNATURE_BYTES + VERSION_BYTES;
|
||||||
|
private static final String PROP_TRUSTED_KEYS = "router.trustedUpdateKeys";
|
||||||
|
|
||||||
|
private static I2PAppContext _context;
|
||||||
|
|
||||||
|
private Log _log;
|
||||||
private ArrayList _trustedKeys;
|
private ArrayList _trustedKeys;
|
||||||
|
|
||||||
private I2PAppContext _context;
|
/**
|
||||||
private Log _log;
|
* Constructs a new <code>TrustedUpdate</code> with the default global
|
||||||
|
* context.
|
||||||
private static final int VERSION_BYTES = 16;
|
*/
|
||||||
private static final int HEADER_BYTES = VERSION_BYTES + Signature.SIGNATURE_BYTES;
|
|
||||||
|
|
||||||
public static final String PROP_TRUSTED_KEYS = "router.trustedUpdateKeys";
|
|
||||||
|
|
||||||
public TrustedUpdate() {
|
public TrustedUpdate() {
|
||||||
this(I2PAppContext.getGlobalContext());
|
this(I2PAppContext.getGlobalContext());
|
||||||
}
|
}
|
||||||
public TrustedUpdate(I2PAppContext ctx) {
|
|
||||||
_context = ctx;
|
/**
|
||||||
|
* Constructs a new <code>TrustedUpdate</code> with the given
|
||||||
|
* {@link net.i2p.I2PAppContext}.
|
||||||
|
*
|
||||||
|
* @param context An instance of <code>I2PAppContext</code>.
|
||||||
|
*/
|
||||||
|
public TrustedUpdate(I2PAppContext context) {
|
||||||
|
_context = context;
|
||||||
_log = _context.logManager().getLog(TrustedUpdate.class);
|
_log = _context.logManager().getLog(TrustedUpdate.class);
|
||||||
_trustedKeys = new ArrayList(1);
|
_trustedKeys = new ArrayList();
|
||||||
String keys = ctx.getProperty(PROP_TRUSTED_KEYS);
|
|
||||||
if ( (keys != null) && (keys.length() > 0) ) {
|
String propertyTrustedKeys = context.getProperty(PROP_TRUSTED_KEYS);
|
||||||
StringTokenizer tok = new StringTokenizer(keys, ", ");
|
|
||||||
while (tok.hasMoreTokens())
|
if ( (propertyTrustedKeys != null) && (propertyTrustedKeys.length() > 0) ) {
|
||||||
_trustedKeys.add(tok.nextToken());
|
StringTokenizer propertyTrustedKeysTokens = new StringTokenizer(propertyTrustedKeys, ",");
|
||||||
|
|
||||||
|
while (propertyTrustedKeysTokens.hasMoreTokens())
|
||||||
|
_trustedKeys.add(propertyTrustedKeysTokens.nextToken().trim());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
_trustedKeys.add(DEFAULT_TRUSTED_KEY);
|
_trustedKeys.add(DEFAULT_TRUSTED_KEY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList getTrustedKeys() { return _trustedKeys; }
|
/**
|
||||||
|
* Parses command line arguments when this class is used from the command
|
||||||
public static void main(String[] args) {
|
* line.
|
||||||
if (args.length <= 0) {
|
*
|
||||||
usage();
|
* @param args Command line parameters.
|
||||||
} else if ("keygen".equals(args[0])) {
|
*/
|
||||||
genKeysCLI(args[1], args[2]);
|
public static void main(String[] args) {
|
||||||
} else if ("sign".equals(args[0])) {
|
try {
|
||||||
signCLI(args[1], args[2], args[3], args[4]);
|
if ("keygen".equals(args[0])) {
|
||||||
} else if ("verify".equals(args[0])) {
|
genKeysCLI(args[1], args[2]);
|
||||||
verifyCLI(args[1]);
|
} else if ("showversion".equals(args[0])) {
|
||||||
} else {
|
showVersionCLI(args[1]);
|
||||||
usage();
|
} else if ("sign".equals(args[0])) {
|
||||||
|
signCLI(args[1], args[2], args[3], args[4]);
|
||||||
|
} else if ("verifysig".equals(args[0])) {
|
||||||
|
verifySigCLI(args[1]);
|
||||||
|
} else if ("verifyupdate".equals(args[0])) {
|
||||||
|
verifyUpdateCLI(args[1]);
|
||||||
|
} else {
|
||||||
|
showUsageCLI();
|
||||||
|
}
|
||||||
|
} catch (ArrayIndexOutOfBoundsException aioobe) {
|
||||||
|
showUsageCLI();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final void usage() {
|
/**
|
||||||
System.err.println("Usage: TrustedUpdate keygen publicKeyFile privateKeyFile");
|
* Checks if the given version is newer than the given current version.
|
||||||
System.err.println(" TrustedUpdate sign origFile signedFile privateKeyFile version");
|
*
|
||||||
System.err.println(" TrustedUpdate verify signedFile");
|
* @param currentVersion The current version.
|
||||||
|
* @param newVersion The version to test.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the given version is newer than the current
|
||||||
|
* version, otherwise <code>false</code>.
|
||||||
|
*/
|
||||||
|
public static final boolean needsUpdate(String currentVersion, String newVersion) {
|
||||||
|
StringTokenizer newVersionTokens = new StringTokenizer(sanitize(newVersion), ".");
|
||||||
|
StringTokenizer currentVersionTokens = new StringTokenizer(sanitize(currentVersion), ".");
|
||||||
|
|
||||||
|
while (newVersionTokens.hasMoreTokens() && currentVersionTokens.hasMoreTokens()) {
|
||||||
|
String newNumber = newVersionTokens.nextToken();
|
||||||
|
String currentNumber = currentVersionTokens.nextToken();
|
||||||
|
|
||||||
|
switch (compare(newNumber, currentNumber)) {
|
||||||
|
case -1: // newNumber is smaller
|
||||||
|
return false;
|
||||||
|
case 0: // eq
|
||||||
|
break;
|
||||||
|
case 1: // newNumber is larger
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newVersionTokens.hasMoreTokens() && !currentVersionTokens.hasMoreTokens())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int compare(String lop, String rop) {
|
||||||
|
try {
|
||||||
|
int left = Integer.parseInt(lop);
|
||||||
|
int right = Integer.parseInt(rop);
|
||||||
|
|
||||||
|
if (left < right)
|
||||||
|
return -1;
|
||||||
|
else if (left == right)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final void genKeysCLI(String publicKeyFile, String privateKeyFile) {
|
private static final void genKeysCLI(String publicKeyFile, String privateKeyFile) {
|
||||||
FileOutputStream out = null;
|
FileOutputStream fileOutputStream = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
Object signingKeypair[] = _context.keyGenerator().generateSigningKeypair();
|
||||||
Object keys[] = ctx.keyGenerator().generateSigningKeypair();
|
SigningPublicKey signingPublicKey = (SigningPublicKey) signingKeypair[0];
|
||||||
SigningPublicKey pub = (SigningPublicKey)keys[0];
|
SigningPrivateKey signingPrivateKey = (SigningPrivateKey) signingKeypair[1];
|
||||||
SigningPrivateKey priv = (SigningPrivateKey)keys[1];
|
|
||||||
|
|
||||||
out = new FileOutputStream(publicKeyFile);
|
fileOutputStream = new FileOutputStream(publicKeyFile);
|
||||||
pub.writeBytes(out);
|
signingPublicKey.writeBytes(fileOutputStream);
|
||||||
out.close();
|
fileOutputStream.close();
|
||||||
|
fileOutputStream = null;
|
||||||
|
|
||||||
out = new FileOutputStream(privateKeyFile);
|
fileOutputStream = new FileOutputStream(privateKeyFile);
|
||||||
priv.writeBytes(out);
|
signingPrivateKey.writeBytes(fileOutputStream);
|
||||||
out.close();
|
|
||||||
out = null;
|
System.out.println("\r\nPrivate key written to: " + privateKeyFile);
|
||||||
System.out.println("Private keys writen to " + privateKeyFile + " and public to " + publicKeyFile);
|
System.out.println("Public key written to: " + publicKeyFile);
|
||||||
System.out.println("Public: " + pub.toBase64());
|
System.out.println("\r\nPublic key: " + signingPublicKey.toBase64() + "\r\n");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error writing keys:");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
System.err.println("Error writing out the keys");
|
|
||||||
} finally {
|
} finally {
|
||||||
if (out != null) try { out.close(); } catch (IOException ioe) {}
|
if (fileOutputStream != null)
|
||||||
|
try {
|
||||||
|
fileOutputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final void signCLI(String origFile, String outFile, String privKeyFile, String version) {
|
private static final String sanitize(String versionString) {
|
||||||
TrustedUpdate up = new TrustedUpdate();
|
StringBuffer versionStringBuffer = new StringBuffer(versionString);
|
||||||
Signature sig = up.sign(origFile, outFile, privKeyFile, version);
|
|
||||||
if (sig != null)
|
for (int i = 0; i < versionStringBuffer.length(); i++) {
|
||||||
System.out.println("Signed and written to " + outFile);
|
if (VALID_VERSION_CHARS.indexOf(versionStringBuffer.charAt(i)) == -1) {
|
||||||
else
|
versionStringBuffer.deleteCharAt(i);
|
||||||
System.out.println("Error signing");
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionStringBuffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final void verifyCLI(String signedFile) {
|
private static final void showUsageCLI() {
|
||||||
TrustedUpdate up = new TrustedUpdate();
|
System.err.println("Usage: TrustedUpdate keygen publicKeyFile privateKeyFile");
|
||||||
boolean ok = up.verify(signedFile);
|
System.err.println(" TrustedUpdate showversion signedFile");
|
||||||
if (ok)
|
System.err.println(" TrustedUpdate sign inputFile signedFile privateKeyFile version");
|
||||||
|
System.err.println(" TrustedUpdate verifysig signedFile");
|
||||||
|
System.err.println(" TrustedUpdate verifyupdate signedFile");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final void showVersionCLI(String signedFile) {
|
||||||
|
String versionString = new TrustedUpdate().getVersionString(signedFile);
|
||||||
|
|
||||||
|
if (versionString == "")
|
||||||
|
System.out.println("No version string found in file '" + signedFile + "'");
|
||||||
|
else
|
||||||
|
System.out.println("Version: " + versionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final void signCLI(String inputFile, String signedFile, String privateKeyFile, String version) {
|
||||||
|
Signature signature = new TrustedUpdate().sign(inputFile, signedFile, privateKeyFile, version);
|
||||||
|
|
||||||
|
if (signature != null)
|
||||||
|
System.out.println("Input file '" + inputFile + "' signed and written to '" + signedFile + "'");
|
||||||
|
else
|
||||||
|
System.out.println("Error signing input file '" + inputFile + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final void verifySigCLI(String signedFile) {
|
||||||
|
boolean isValidSignature = new TrustedUpdate().verify(signedFile);
|
||||||
|
|
||||||
|
if (isValidSignature)
|
||||||
System.out.println("Signature VALID");
|
System.out.println("Signature VALID");
|
||||||
else
|
else
|
||||||
System.out.println("Signature INVALID");
|
System.out.println("Signature INVALID");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static final void verifyUpdateCLI(String signedFile) {
|
||||||
* Reads the version string from a signed I2P update file.
|
boolean isUpdate = new TrustedUpdate().isUpdatedVersion(CoreVersion.VERSION, signedFile);
|
||||||
*
|
|
||||||
* @param inputFile A signed I2P update file.
|
if (isUpdate)
|
||||||
*
|
System.out.println("File version is newer than current version.");
|
||||||
* @return The update version string read, or an empty string if no version
|
else
|
||||||
* string is present.
|
System.out.println("File version is older than or equal to current version.");
|
||||||
*/
|
}
|
||||||
public String getUpdateVersion(String inputFile) {
|
|
||||||
FileInputStream in = null;
|
/**
|
||||||
|
* Fetches the trusted keys for the current instance.
|
||||||
|
*
|
||||||
|
* @return An <code>ArrayList</code> containting the trusted keys.
|
||||||
|
*/
|
||||||
|
public ArrayList getTrustedKeys() {
|
||||||
|
return _trustedKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the version string from a signed update file.
|
||||||
|
*
|
||||||
|
* @param signedFile A signed update file.
|
||||||
|
*
|
||||||
|
* @return The version string read, or an empty string if no version string
|
||||||
|
* is present.
|
||||||
|
*/
|
||||||
|
public String getVersionString(String signedFile) {
|
||||||
|
FileInputStream fileInputStream = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
in = new FileInputStream(inputFile);
|
fileInputStream = new FileInputStream(signedFile);
|
||||||
byte data[] = new byte[VERSION_BYTES];
|
byte[] data = new byte[VERSION_BYTES];
|
||||||
int read = DataHelper.read(in, data);
|
int bytesRead = DataHelper.read(fileInputStream, data, Signature.SIGNATURE_BYTES, VERSION_BYTES);
|
||||||
if (read != VERSION_BYTES)
|
|
||||||
return null;
|
if (bytesRead != VERSION_BYTES)
|
||||||
|
return "";
|
||||||
|
|
||||||
for (int i = 0; i < VERSION_BYTES; i++)
|
for (int i = 0; i < VERSION_BYTES; i++)
|
||||||
if (data[i] == 0x00)
|
if (data[i] == 0x00)
|
||||||
return new String(data, 0, i, "UTF-8");
|
return new String(data, 0, i, "UTF-8");
|
||||||
|
|
||||||
return new String(data, "UTF-8");
|
return new String(data, "UTF-8");
|
||||||
} catch (UnsupportedEncodingException uee) {
|
} catch (UnsupportedEncodingException uee) {
|
||||||
// If this ever gets called, you need a new JVM.
|
|
||||||
throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + uee.getMessage());
|
throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + uee.getMessage());
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
return "";
|
return "";
|
||||||
} finally {
|
} finally {
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
if (fileInputStream != null)
|
||||||
|
try {
|
||||||
|
fileInputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the version of the given signed update file is newer than
|
||||||
|
* <code>currentVersion</code>.
|
||||||
|
*
|
||||||
|
* @param currentVersion The current version to check against.
|
||||||
|
* @param signedFile The signed update file.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the signed update file's version is newer
|
||||||
|
* than the current version, otherwise <code>false</code>.
|
||||||
|
*/
|
||||||
|
public boolean isUpdatedVersion(String currentVersion, String signedFile) {
|
||||||
|
if (needsUpdate(currentVersion, getVersionString(signedFile)))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the signature of a signed update file, and if it's valid and the
|
||||||
|
* file's version is newer than the given current version, migrates the data
|
||||||
|
* out of <code>signedFile</code> and into <code>outputFile</code>.
|
||||||
|
*
|
||||||
|
* @param currentVersion The current version to check against.
|
||||||
|
* @param signedFile A signed update file.
|
||||||
|
* @param outputFile The file to write the verified data to.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the signature and version were valid and the
|
||||||
|
* data was moved, <code>false</code> otherwise.
|
||||||
|
*/
|
||||||
|
public boolean migrateVerified(String currentVersion, String signedFile, String outputFile) {
|
||||||
|
if (!isUpdatedVersion(currentVersion, signedFile))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!verify(signedFile))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FileInputStream fileInputStream = null;
|
||||||
|
FileOutputStream fileOutputStream = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the given private key to sign the given input file with DSA. The
|
|
||||||
* output will be a binary file where the first 16 bytes are the I2P
|
|
||||||
* update's version string encoded in UTF-8 (padded with trailing
|
|
||||||
* <code>0h</code> characters if necessary), the next 40 bytes are the
|
|
||||||
* resulting DSA signature, and the remaining bytes are the input file.
|
|
||||||
*
|
|
||||||
* @param inputFile The file to be signed.
|
|
||||||
* @param outputFile The signed file to write.
|
|
||||||
* @param privateKeyFile The name of the file containing the private key to
|
|
||||||
* sign <code>inputFile</code> with.
|
|
||||||
* @param updateVersion The version number of the I2P update. If this
|
|
||||||
* string is longer than 16 characters it will be
|
|
||||||
* truncated.
|
|
||||||
*
|
|
||||||
* @return An instance of {@link net.i2p.data.Signature}, or null if there was an error
|
|
||||||
*/
|
|
||||||
public Signature sign(String inputFile, String outputFile, String privateKeyFile, String updateVersion) {
|
|
||||||
SigningPrivateKey key = new SigningPrivateKey();
|
|
||||||
FileInputStream in = null;
|
|
||||||
try {
|
try {
|
||||||
in = new FileInputStream(privateKeyFile);
|
fileInputStream = new FileInputStream(signedFile);
|
||||||
key.readBytes(in);
|
fileOutputStream = new FileOutputStream(outputFile);
|
||||||
|
long skipped = 0;
|
||||||
|
|
||||||
|
while (skipped < HEADER_BYTES)
|
||||||
|
skipped += fileInputStream.skip(HEADER_BYTES - skipped);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead = 0;
|
||||||
|
|
||||||
|
while ( (bytesRead = fileInputStream.read(buffer)) != -1)
|
||||||
|
fileOutputStream.write(buffer, 0, bytesRead);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (fileInputStream != null)
|
||||||
|
try {
|
||||||
|
fileInputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileOutputStream != null)
|
||||||
|
try {
|
||||||
|
fileOutputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the given private key to sign the given input file along with its
|
||||||
|
* version string using DSA. The output will be a signed update file where
|
||||||
|
* the first 40 bytes are the resulting DSA signature, the next 16 bytes are
|
||||||
|
* the input file's version string encoded in UTF-8 (padded with trailing
|
||||||
|
* <code>0h</code> characters if necessary), and the remaining bytes are the
|
||||||
|
* raw bytes of the input file.
|
||||||
|
*
|
||||||
|
* @param inputFile The file to be signed.
|
||||||
|
* @param signedFile The signed update file to write.
|
||||||
|
* @param privateKeyFile The name of the file containing the private key to
|
||||||
|
* sign <code>inputFile</code> with.
|
||||||
|
* @param version The version string of the input file. If this is
|
||||||
|
* longer than 16 characters it will be truncated.
|
||||||
|
*
|
||||||
|
* @return An instance of {@link net.i2p.data.Signature}, or
|
||||||
|
* <code>null</code> if there was an error.
|
||||||
|
*/
|
||||||
|
public Signature sign(String inputFile, String signedFile, String privateKeyFile, String version) {
|
||||||
|
FileInputStream fileInputStream = null;
|
||||||
|
SigningPrivateKey signingPrivateKey = new SigningPrivateKey();
|
||||||
|
|
||||||
|
try {
|
||||||
|
fileInputStream = new FileInputStream(privateKeyFile);
|
||||||
|
signingPrivateKey.readBytes(fileInputStream);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Unable to load the signing key", ioe);
|
_log.warn("Unable to load the signing key", ioe);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Unable to load the signing key", dfe);
|
_log.warn("Unable to load the signing key", dfe);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
if (fileInputStream != null)
|
||||||
|
try {
|
||||||
|
fileInputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sign(inputFile, outputFile, key, updateVersion);
|
return sign(inputFile, signedFile, signingPrivateKey, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Signature sign(String inputFile, String outputFile, SigningPrivateKey privKey, String updateVersion) {
|
/**
|
||||||
byte[] headerUpdateVersion = {
|
* Uses the given {@link net.i2p.data.SigningPrivateKey} to sign the given
|
||||||
0x00, 0x00, 0x00, 0x00,
|
* input file along with its version string using DSA. The output will be a
|
||||||
0x00, 0x00, 0x00, 0x00,
|
* signed update file where the first 40 bytes are the resulting DSA
|
||||||
0x00, 0x00, 0x00, 0x00,
|
* signature, the next 16 bytes are the input file's version string encoded
|
||||||
0x00, 0x00, 0x00, 0x00 };
|
* in UTF-8 (padded with trailing <code>0h</code> characters if necessary),
|
||||||
byte[] updateVersionBytes = null;
|
* and the remaining bytes are the raw bytes of the input file.
|
||||||
if (updateVersion.length() > VERSION_BYTES)
|
*
|
||||||
updateVersion = updateVersion.substring(0, VERSION_BYTES);
|
* @param inputFile The file to be signed.
|
||||||
try {
|
* @param signedFile The signed update file to write.
|
||||||
updateVersionBytes = updateVersion.getBytes("UTF-8");
|
* @param signingPrivateKey An instance of <code>SigningPrivateKey</code>
|
||||||
} catch (UnsupportedEncodingException e) {
|
* to sign <code>inputFile</code> with.
|
||||||
// If this ever gets called, you need a new JVM.
|
* @param version The version string of the input file. If this is
|
||||||
throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + e.getMessage());
|
* longer than 16 characters it will be truncated.
|
||||||
}
|
*
|
||||||
System.arraycopy(updateVersionBytes, 0, headerUpdateVersion, 0, updateVersionBytes.length);
|
* @return An instance of {@link net.i2p.data.Signature}, or
|
||||||
|
* <code>null</code> if there was an error.
|
||||||
|
*/
|
||||||
|
public Signature sign(String inputFile, String signedFile, SigningPrivateKey signingPrivateKey, String version) {
|
||||||
|
byte[] versionHeader = {
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00 };
|
||||||
|
byte[] versionRawBytes = null;
|
||||||
|
|
||||||
|
if (version.length() > VERSION_BYTES)
|
||||||
|
version = version.substring(0, VERSION_BYTES);
|
||||||
|
|
||||||
Signature signature = null;
|
|
||||||
FileInputStream in = null;
|
|
||||||
try {
|
try {
|
||||||
in = new FileInputStream(inputFile);
|
versionRawBytes = version.getBytes("UTF-8");
|
||||||
signature = _context.dsa().sign(in, privKey);
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.arraycopy(versionRawBytes, 0, versionHeader, 0, versionRawBytes.length);
|
||||||
|
|
||||||
|
FileInputStream fileInputStream = null;
|
||||||
|
Signature signature = null;
|
||||||
|
SequenceInputStream bytesToSignInputStream = null;
|
||||||
|
ByteArrayInputStream versionHeaderInputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
fileInputStream = new FileInputStream(inputFile);
|
||||||
|
versionHeaderInputStream = new ByteArrayInputStream(versionHeader);
|
||||||
|
bytesToSignInputStream = new SequenceInputStream(versionHeaderInputStream, fileInputStream);
|
||||||
|
signature = _context.dsa().sign(bytesToSignInputStream, signingPrivateKey);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.error("Error signing", e);
|
_log.error("Error signing", e);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
if (bytesToSignInputStream != null)
|
||||||
in = null;
|
try {
|
||||||
|
bytesToSignInputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInputStream = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileOutputStream fileOutputStream = null;
|
FileOutputStream fileOutputStream = null;
|
||||||
try {
|
|
||||||
fileOutputStream = new FileOutputStream(outputFile);
|
|
||||||
fileOutputStream.write(headerUpdateVersion);
|
|
||||||
fileOutputStream.write(signature.getData());
|
|
||||||
|
|
||||||
in = new FileInputStream(inputFile);
|
try {
|
||||||
byte buf[] = new byte[1024];
|
fileOutputStream = new FileOutputStream(signedFile);
|
||||||
int read = 0;
|
fileOutputStream.write(signature.getData());
|
||||||
while ( (read = in.read(buf)) != -1)
|
fileOutputStream.write(versionHeader);
|
||||||
fileOutputStream.write(buf, 0, read);
|
fileInputStream = new FileInputStream(inputFile);
|
||||||
fileOutputStream.close();
|
byte[] buffer = new byte[1024];
|
||||||
fileOutputStream = null;
|
int bytesRead = 0;
|
||||||
} catch (IOException ioe) {
|
while ( (bytesRead = fileInputStream.read(buffer)) != -1)
|
||||||
if (_log.shouldLog(Log.WARN))
|
fileOutputStream.write(buffer, 0, bytesRead);
|
||||||
_log.log(Log.WARN, "Error writing signed I2P update file " + outputFile, ioe);
|
fileOutputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.log(Log.WARN, "Error writing signed file " + signedFile, ioe);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
if (fileOutputStream != null) try { fileOutputStream.close(); } catch (IOException ioe) {}
|
if (fileInputStream != null)
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
try {
|
||||||
|
fileInputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileOutputStream != null)
|
||||||
|
try {
|
||||||
|
fileOutputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return signature;
|
return signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies the DSA signature of a signed I2P update.
|
* Verifies the DSA signature of a signed update file.
|
||||||
*
|
*
|
||||||
* @param inputFile The signed update file to check.
|
* @param signedFile The signed update file to check.
|
||||||
*
|
*
|
||||||
* @return <code>true</code> if the file has a valid signature.
|
* @return <code>true</code> if the file has a valid signature, otherwise
|
||||||
*/
|
* <code>false</code>.
|
||||||
public boolean verify(String inputFile) {
|
*/
|
||||||
|
public boolean verify(String signedFile) {
|
||||||
for (int i = 0; i < _trustedKeys.size(); i++) {
|
for (int i = 0; i < _trustedKeys.size(); i++) {
|
||||||
SigningPublicKey key = new SigningPublicKey();
|
SigningPublicKey signingPublicKey = new SigningPublicKey();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
key.fromBase64((String)_trustedKeys.get(i));
|
signingPublicKey.fromBase64((String)_trustedKeys.get(i));
|
||||||
boolean ok = verify(inputFile, key);
|
boolean isValidSignature = verify(signedFile, signingPublicKey);
|
||||||
if (ok) return true;
|
|
||||||
|
if (isValidSignature)
|
||||||
|
return true;
|
||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
_log.log(Log.CRIT, "Trusted key " + i + " is not valid");
|
_log.log(Log.CRIT, "Trusted key " + i + " is not valid");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("None of the keys match");
|
_log.warn("None of the keys match");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies the DSA signature of a signed I2P update.
|
* Verifies the DSA signature of a signed update file.
|
||||||
*
|
*
|
||||||
* @param inputFile The signed update file to check.
|
* @param signedFile The signed update file to check.
|
||||||
* @param key public key to verify against
|
* @param publicKeyFile A file containing the public key to use for
|
||||||
*
|
* verification.
|
||||||
* @return <code>true</code> if the file has a valid signature.
|
*
|
||||||
*/
|
* @return <code>true</code> if the file has a valid signature, otherwise
|
||||||
public boolean verify(String inputFile, SigningPublicKey key) {
|
* <code>false</code>.
|
||||||
FileInputStream in = null;
|
*/
|
||||||
try {
|
public boolean verify(String signedFile, String publicKeyFile) {
|
||||||
in = new FileInputStream(inputFile);
|
SigningPublicKey signingPublicKey = new SigningPublicKey();
|
||||||
byte version[] = new byte[VERSION_BYTES];
|
FileInputStream fileInputStream = null;
|
||||||
Signature sig = new Signature();
|
|
||||||
if (VERSION_BYTES != DataHelper.read(in, version))
|
|
||||||
throw new IOException("Not enough data for the version bytes");
|
|
||||||
sig.readBytes(in);
|
|
||||||
return _context.dsa().verifySignature(sig, in, key);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Error reading " + inputFile + " to verify", ioe);
|
|
||||||
return false;
|
|
||||||
} catch (DataFormatException dfe) {
|
|
||||||
if (_log.shouldLog(Log.ERROR))
|
|
||||||
_log.error("Error reading the signature", dfe);
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies the DSA signature of a signed I2P update.
|
|
||||||
*
|
|
||||||
* @param inputFile The signed update file to check.
|
|
||||||
* @param publicKeyFile The public key to use for verification.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if the file has a valid signature.
|
|
||||||
*/
|
|
||||||
public boolean verify(String inputFile, String publicKeyFile) {
|
|
||||||
SigningPublicKey pub = new SigningPublicKey();
|
|
||||||
FileInputStream in = null;
|
|
||||||
try {
|
try {
|
||||||
in = new FileInputStream(inputFile);
|
fileInputStream = new FileInputStream(signedFile);
|
||||||
pub.readBytes(in);
|
signingPublicKey.readBytes(fileInputStream);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Unable to load the signature", ioe);
|
_log.warn("Unable to load the signature", ioe);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Unable to load the signature", dfe);
|
_log.warn("Unable to load the signature", dfe);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
if (fileInputStream != null)
|
||||||
|
try {
|
||||||
|
fileInputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return verify(inputFile, pub);
|
return verify(signedFile, signingPublicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify the signature on the signed inputFile, and if it is valid, migrate
|
* Verifies the DSA signature of a signed update file.
|
||||||
* the raw data out of it and into the outputFile
|
|
||||||
*
|
*
|
||||||
* @return true if the signature was valid and the data moved, false otherwise.
|
* @param signedFile The signed update file to check.
|
||||||
|
* @param signingPublicKey An instance of
|
||||||
|
* {@link net.i2p.data.SigningPublicKey} to use for
|
||||||
|
* verification.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the file has a valid signature, otherwise
|
||||||
|
* <code>false</code>.
|
||||||
*/
|
*/
|
||||||
public boolean migrateVerified(String inputFile, String outputFile) {
|
public boolean verify(String signedFile, SigningPublicKey signingPublicKey) {
|
||||||
boolean ok = verify(inputFile);
|
FileInputStream fileInputStream = null;
|
||||||
if (!ok) return false;
|
|
||||||
FileOutputStream out = null;
|
|
||||||
FileInputStream in = null;
|
|
||||||
try {
|
|
||||||
out = new FileOutputStream(outputFile);
|
|
||||||
in = new FileInputStream(inputFile);
|
|
||||||
long skipped = 0;
|
|
||||||
while (skipped < HEADER_BYTES) {
|
|
||||||
skipped += in.skip(HEADER_BYTES - skipped);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte buf[] = new byte[1024];
|
try {
|
||||||
int read = 0;
|
fileInputStream = new FileInputStream(signedFile);
|
||||||
while ( (read = in.read(buf)) != -1)
|
Signature signature = new Signature();
|
||||||
out.write(buf, 0, read);
|
|
||||||
|
signature.readBytes(fileInputStream);
|
||||||
|
|
||||||
|
return _context.dsa().verifySignature(signature, fileInputStream, signingPublicKey);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Error reading " + signedFile + " to verify", ioe);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
if (_log.shouldLog(Log.ERROR))
|
||||||
|
_log.error("Error reading the signature", dfe);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
if (out != null) try { out.close(); } catch (IOException ioe) {}
|
if (fileInputStream != null)
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
try {
|
||||||
|
fileInputStream.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
history.txt
12
history.txt
@ -1,4 +1,14 @@
|
|||||||
$Id: history.txt,v 1.188 2005/04/05 17:24:32 jrandom Exp $
|
$Id: history.txt,v 1.189 2005/04/06 10:43:25 jrandom Exp $
|
||||||
|
|
||||||
|
2005-04-08 smeghead
|
||||||
|
* Security improvements to TrustedUpdate: signing and verification of the
|
||||||
|
version string along with the data payload for signed update files
|
||||||
|
(consequently the positions of the DSA signature and version string fields
|
||||||
|
have been swapped in the spec for the update file's header); router will
|
||||||
|
no longer perform a trusted update if the signed update's version is lower
|
||||||
|
than or equal to the currently running router's version.
|
||||||
|
* Added two new CLI commands to TrustedUpdate: showversion, verifyupdate.
|
||||||
|
* Extended TrustedUpdate public API for use by third party applications.
|
||||||
|
|
||||||
* 2005-04-06 0.5.0.6 released
|
* 2005-04-06 0.5.0.6 released
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user