Use the least possible memory for boot signing and verification
Close #971, close #966
This commit is contained in:
parent
23e5188422
commit
85042fbe25
@ -4,20 +4,18 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
public class ByteArrayStream extends ByteArrayOutputStream {
|
public class ByteArrayStream extends ByteArrayOutputStream {
|
||||||
public byte[] getBuf() {
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
public synchronized void readFrom(InputStream is) {
|
public synchronized void readFrom(InputStream is) {
|
||||||
readFrom(is, Integer.MAX_VALUE);
|
readFrom(is, Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void readFrom(InputStream is, int len) {
|
public synchronized void readFrom(InputStream is, int len) {
|
||||||
int read;
|
int read;
|
||||||
byte buffer[] = new byte[4096];
|
byte buffer[] = new byte[4096];
|
||||||
try {
|
try {
|
||||||
while ((read = is.read(buffer, 0, len < buffer.length ? len : buffer.length)) > 0) {
|
while ((read = is.read(buffer, 0, Math.min(len, buffer.length))) > 0) {
|
||||||
write(buffer, 0, read);
|
write(buffer, 0, read);
|
||||||
len -= read;
|
len -= read;
|
||||||
}
|
}
|
||||||
@ -25,9 +23,7 @@ public class ByteArrayStream extends ByteArrayOutputStream {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public synchronized void writeTo(OutputStream out, int off, int len) throws IOException {
|
|
||||||
out.write(buf, off, len);
|
|
||||||
}
|
|
||||||
public ByteArrayInputStream getInputStream() {
|
public ByteArrayInputStream getInputStream() {
|
||||||
return new ByteArrayInputStream(buf, 0, count);
|
return new ByteArrayInputStream(buf, 0, count);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
|||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.FilterInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@ -35,20 +36,62 @@ public class SignBoot {
|
|||||||
Security.addProvider(new BouncyCastleProvider());
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class PushBackRWStream extends FilterInputStream {
|
||||||
|
private OutputStream out;
|
||||||
|
private int pos = 0;
|
||||||
|
private byte[] backBuf;
|
||||||
|
|
||||||
|
PushBackRWStream(InputStream in, OutputStream o) {
|
||||||
|
super(in);
|
||||||
|
out = o;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
int b;
|
||||||
|
if (backBuf != null && backBuf.length > pos) {
|
||||||
|
b = backBuf[pos++];
|
||||||
|
} else {
|
||||||
|
b = super.read();
|
||||||
|
out.write(b);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] bytes, int off, int len) throws IOException {
|
||||||
|
int read = 0;
|
||||||
|
if (backBuf != null && backBuf.length > pos) {
|
||||||
|
read = Math.min(len, backBuf.length - pos);
|
||||||
|
System.arraycopy(backBuf, pos, bytes, off, read);
|
||||||
|
pos += read;
|
||||||
|
off += read;
|
||||||
|
len -= read;
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
|
int ar = super.read(bytes, off, len);
|
||||||
|
read += ar;
|
||||||
|
out.write(bytes, off, ar);
|
||||||
|
}
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unread(byte[] buf) {
|
||||||
|
backBuf = buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut,
|
public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut,
|
||||||
InputStream cert, InputStream key) {
|
InputStream cert, InputStream key) {
|
||||||
try {
|
try {
|
||||||
ByteArrayStream image = new ByteArrayStream();
|
PushBackRWStream in = new PushBackRWStream(imgIn, imgOut);
|
||||||
image.readFrom(imgIn);
|
byte[] hdr = new byte[1024];
|
||||||
int signableSize = getSignableImageSize(image.getBuf());
|
// First read the header
|
||||||
if (signableSize < image.size()) {
|
in.read(hdr);
|
||||||
System.err.println("NOTE: truncating input from " +
|
int signableSize = getSignableImageSize(hdr);
|
||||||
image.size() + " to " + signableSize + " bytes");
|
// Unread header
|
||||||
} else if (signableSize > image.size()) {
|
in.unread(hdr);
|
||||||
throw new IllegalArgumentException("Invalid image: too short, expected " +
|
BootSignature bootsig = new BootSignature(target, signableSize);
|
||||||
signableSize + " bytes");
|
|
||||||
}
|
|
||||||
BootSignature bootsig = new BootSignature(target, image.size());
|
|
||||||
if (cert == null) {
|
if (cert == null) {
|
||||||
cert = SignBoot.class.getResourceAsStream("/keys/testkey.x509.pem");
|
cert = SignBoot.class.getResourceAsStream("/keys/testkey.x509.pem");
|
||||||
}
|
}
|
||||||
@ -58,10 +101,9 @@ public class SignBoot {
|
|||||||
key = SignBoot.class.getResourceAsStream("/keys/testkey.pk8");
|
key = SignBoot.class.getResourceAsStream("/keys/testkey.pk8");
|
||||||
}
|
}
|
||||||
PrivateKey privateKey = CryptoUtils.readPrivateKey(key);
|
PrivateKey privateKey = CryptoUtils.readPrivateKey(key);
|
||||||
bootsig.setSignature(bootsig.sign(privateKey, image.getBuf(), signableSize),
|
byte[] sig = bootsig.sign(privateKey, in, signableSize);
|
||||||
CryptoUtils.getSignatureAlgorithmIdentifier(privateKey));
|
bootsig.setSignature(sig, CryptoUtils.getSignatureAlgorithmIdentifier(privateKey));
|
||||||
byte[] encoded_bootsig = bootsig.getEncoded();
|
byte[] encoded_bootsig = bootsig.getEncoded();
|
||||||
image.writeTo(imgOut);
|
|
||||||
imgOut.write(encoded_bootsig);
|
imgOut.write(encoded_bootsig);
|
||||||
imgOut.flush();
|
imgOut.flush();
|
||||||
return true;
|
return true;
|
||||||
@ -73,19 +115,29 @@ public class SignBoot {
|
|||||||
|
|
||||||
public static boolean verifySignature(InputStream imgIn, InputStream certIn) {
|
public static boolean verifySignature(InputStream imgIn, InputStream certIn) {
|
||||||
try {
|
try {
|
||||||
ByteArrayStream image = new ByteArrayStream();
|
// Read the header for size
|
||||||
image.readFrom(imgIn);
|
byte[] hdr = new byte[1024];
|
||||||
int signableSize = getSignableImageSize(image.getBuf());
|
if (imgIn.read(hdr) != hdr.length)
|
||||||
if (signableSize >= image.size()) {
|
return false;
|
||||||
|
int signableSize = getSignableImageSize(hdr);
|
||||||
|
|
||||||
|
// Read the rest of the image
|
||||||
|
byte[] rawImg = Arrays.copyOf(hdr, signableSize);
|
||||||
|
int remain = signableSize - hdr.length;
|
||||||
|
if (imgIn.read(rawImg, hdr.length, remain) != remain) {
|
||||||
System.err.println("Invalid image: not signed");
|
System.err.println("Invalid image: not signed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
byte[] signature = Arrays.copyOfRange(image.getBuf(), signableSize, image.size());
|
|
||||||
|
// Read footer, which contains the signature
|
||||||
|
byte[] signature = new byte[4096];
|
||||||
|
imgIn.read(signature);
|
||||||
|
|
||||||
BootSignature bootsig = new BootSignature(signature);
|
BootSignature bootsig = new BootSignature(signature);
|
||||||
if (certIn != null) {
|
if (certIn != null) {
|
||||||
bootsig.setCertificate(CryptoUtils.readCertificate(certIn));
|
bootsig.setCertificate(CryptoUtils.readCertificate(certIn));
|
||||||
}
|
}
|
||||||
if (bootsig.verify(image.getBuf(), signableSize)) {
|
if (bootsig.verify(rawImg, signableSize)) {
|
||||||
System.err.println("Signature is VALID");
|
System.err.println("Signature is VALID");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -148,8 +200,7 @@ public class SignBoot {
|
|||||||
* Initializes the object for verifying a signed image file
|
* Initializes the object for verifying a signed image file
|
||||||
* @param signature Signature footer
|
* @param signature Signature footer
|
||||||
*/
|
*/
|
||||||
public BootSignature(byte[] signature)
|
public BootSignature(byte[] signature) throws Exception {
|
||||||
throws Exception {
|
|
||||||
ASN1InputStream stream = new ASN1InputStream(signature);
|
ASN1InputStream stream = new ASN1InputStream(signature);
|
||||||
ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
|
ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
|
||||||
formatVersion = (ASN1Integer) sequence.getObjectAt(0);
|
formatVersion = (ASN1Integer) sequence.getObjectAt(0);
|
||||||
@ -193,10 +244,15 @@ public class SignBoot {
|
|||||||
publicKey = cert.getPublicKey();
|
publicKey = cert.getPublicKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] sign(PrivateKey key, byte[] image, int length) throws Exception {
|
public byte[] sign(PrivateKey key, InputStream is, int len) throws Exception {
|
||||||
Signature signer = Signature.getInstance(CryptoUtils.getSignatureAlgorithm(key));
|
Signature signer = Signature.getInstance(CryptoUtils.getSignatureAlgorithm(key));
|
||||||
signer.initSign(key);
|
signer.initSign(key);
|
||||||
signer.update(image, 0, length);
|
int read;
|
||||||
|
byte buffer[] = new byte[4096];
|
||||||
|
while ((read = is.read(buffer, 0, Math.min(len, buffer.length))) > 0) {
|
||||||
|
signer.update(buffer, 0, read);
|
||||||
|
len -= read;
|
||||||
|
}
|
||||||
signer.update(getEncodedAuthenticatedAttributes());
|
signer.update(getEncodedAuthenticatedAttributes());
|
||||||
return signer.sign();
|
return signer.sign();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user