From 815092c2e189c3f571e3648142561ad365769278 Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Thu, 2 Mar 2023 10:32:29 +0100 Subject: [PATCH 01/17] add processor to sign RPM file --- .../RawRpmFileSignatureProcessor.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java new file mode 100644 index 0000000..2c2d322 --- /dev/null +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java @@ -0,0 +1,72 @@ +package org.eclipse.packager.rpm.signature; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; + +import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream; +import org.apache.commons.compress.utils.IOUtils; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.eclipse.packager.rpm.RpmLead; +import org.eclipse.packager.rpm.RpmSignatureTag; +import org.eclipse.packager.rpm.RpmTag; +import org.eclipse.packager.rpm.header.Header; +import org.eclipse.packager.rpm.parse.InputHeader; +import org.eclipse.packager.rpm.parse.RpmInputStream; + +public class RawRpmFileSignatureProcessor implements SignatureProcessor { + + private RpmLead lead; + + private InputHeader payloadHeader; + + private Header signatureHeader; + + private CpioArchiveOutputStream cpioArchiveOutputStream; + + public void perform(RpmInputStream input, PGPKeyPair pgpKeyPair) throws IOException { + input.available(); + this.lead = input.getLead(); + this.payloadHeader = input.getPayloadHeader(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtils.copy(input.getCpioStream(), out); + this.cpioArchiveOutputStream = new CpioArchiveOutputStream(out); + + byte[] headerBytes = new byte[] {}; + input.read(headerBytes, (int) payloadHeader.getStart(), (int) payloadHeader.getLength()); + ByteBuffer header = ByteBuffer.allocate((int) payloadHeader.getLength()); + ReadableByteChannel headerChannel = Channels.newChannel(new ByteArrayInputStream(headerBytes)); + IOUtils.readFully(headerChannel, header); + + ByteBuffer data = ByteBuffer.allocate(input.getCpioStream().available()); + ReadableByteChannel payloadChannel = Channels.newChannel(new ByteArrayInputStream(headerBytes)); + IOUtils.readFully(payloadChannel, data); + + feedHeader(header); + feedPayloadData(data); + this.signatureHeader = new Header<>(); + finish(signatureHeader); + } + + @Override + public void feedHeader(ByteBuffer header) { + // TODO Auto-generated method stub + + } + + @Override + public void feedPayloadData(ByteBuffer data) { + // TODO Auto-generated method stub + + } + + @Override + public void finish(Header signature) { + // TODO Auto-generated method stub + + } + +} From 032fb06d01ed4a2e61172af45fc31e8195da1de4 Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Thu, 2 Mar 2023 11:34:44 +0100 Subject: [PATCH 02/17] not implements Processor + fix reading datas --- .../RawRpmFileSignatureProcessor.java | 34 +++++-------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java index 2c2d322..8e40710 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java @@ -17,7 +17,7 @@ import org.eclipse.packager.rpm.parse.InputHeader; import org.eclipse.packager.rpm.parse.RpmInputStream; -public class RawRpmFileSignatureProcessor implements SignatureProcessor { +public class RawRpmFileSignatureProcessor { private RpmLead lead; @@ -41,32 +41,16 @@ public void perform(RpmInputStream input, PGPKeyPair pgpKeyPair) throws IOExcept ReadableByteChannel headerChannel = Channels.newChannel(new ByteArrayInputStream(headerBytes)); IOUtils.readFully(headerChannel, header); - ByteBuffer data = ByteBuffer.allocate(input.getCpioStream().available()); - ReadableByteChannel payloadChannel = Channels.newChannel(new ByteArrayInputStream(headerBytes)); + byte[] dataBytes = IOUtils.toByteArray(input.getCpioStream()); + ByteBuffer data = ByteBuffer.allocate(dataBytes.length); + ReadableByteChannel payloadChannel = Channels.newChannel(new ByteArrayInputStream(dataBytes)); IOUtils.readFully(payloadChannel, data); - feedHeader(header); - feedPayloadData(data); - this.signatureHeader = new Header<>(); - finish(signatureHeader); - } - - @Override - public void feedHeader(ByteBuffer header) { - // TODO Auto-generated method stub - - } - - @Override - public void feedPayloadData(ByteBuffer data) { - // TODO Auto-generated method stub - - } - - @Override - public void finish(Header signature) { - // TODO Auto-generated method stub + RsaSignatureProcessor processor = new RsaSignatureProcessor(pgpKeyPair.getPrivateKey()); + processor.feedHeader(header); + processor.feedPayloadData(data); + this.signatureHeader = new Header<>(); + processor.finish(signatureHeader); } - } From 005e8b8ac4705194371c420285b2c9d5f5c19b99 Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Thu, 2 Mar 2023 11:35:13 +0100 Subject: [PATCH 03/17] init tests --- .../signature/RawRpmFileSignatureProcessorTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java new file mode 100644 index 0000000..fd62c52 --- /dev/null +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java @@ -0,0 +1,12 @@ +package org.eclipse.packager.rpm.signature; + +import org.junit.jupiter.api.Test; + +public class RawRpmFileSignatureProcessorTest { + + @Test + public void test_signing_existing_rpm() { + + } + +} From 7940d5ecd5a6e8586e495e18937a8f3425f5c25c Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Fri, 3 Mar 2023 17:10:47 +0100 Subject: [PATCH 04/17] somes fixes + test (not working ArrayOutOfBound) --- .../RawRpmFileSignatureProcessor.java | 12 +- .../RawRpmFileSignatureProcessorTest.java | 36 +++++- rpm/src/test/resources/key/private_key.txt | 116 ++++++++++++++++++ rpm/src/test/resources/key/public_key.txt | 56 +++++++++ 4 files changed, 211 insertions(+), 9 deletions(-) create mode 100644 rpm/src/test/resources/key/private_key.txt create mode 100644 rpm/src/test/resources/key/public_key.txt diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java index 8e40710..e03211c 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java @@ -9,7 +9,7 @@ import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; -import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPPrivateKey; import org.eclipse.packager.rpm.RpmLead; import org.eclipse.packager.rpm.RpmSignatureTag; import org.eclipse.packager.rpm.RpmTag; @@ -27,7 +27,7 @@ public class RawRpmFileSignatureProcessor { private CpioArchiveOutputStream cpioArchiveOutputStream; - public void perform(RpmInputStream input, PGPKeyPair pgpKeyPair) throws IOException { + public void perform(RpmInputStream input, PGPPrivateKey privateKey) throws IOException { input.available(); this.lead = input.getLead(); this.payloadHeader = input.getPayloadHeader(); @@ -35,7 +35,7 @@ public void perform(RpmInputStream input, PGPKeyPair pgpKeyPair) throws IOExcept IOUtils.copy(input.getCpioStream(), out); this.cpioArchiveOutputStream = new CpioArchiveOutputStream(out); - byte[] headerBytes = new byte[] {}; + byte[] headerBytes = new byte[(int) payloadHeader.getLength()]; input.read(headerBytes, (int) payloadHeader.getStart(), (int) payloadHeader.getLength()); ByteBuffer header = ByteBuffer.allocate((int) payloadHeader.getLength()); ReadableByteChannel headerChannel = Channels.newChannel(new ByteArrayInputStream(headerBytes)); @@ -46,9 +46,9 @@ public void perform(RpmInputStream input, PGPKeyPair pgpKeyPair) throws IOExcept ReadableByteChannel payloadChannel = Channels.newChannel(new ByteArrayInputStream(dataBytes)); IOUtils.readFully(payloadChannel, data); - RsaSignatureProcessor processor = new RsaSignatureProcessor(pgpKeyPair.getPrivateKey()); - processor.feedHeader(header); - processor.feedPayloadData(data); + RsaSignatureProcessor processor = new RsaSignatureProcessor(privateKey); + processor.feedHeader(header.slice()); + processor.feedPayloadData(data.slice()); this.signatureHeader = new Header<>(); processor.finish(signatureHeader); diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java index fd62c52..701b36b 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java @@ -1,12 +1,42 @@ package org.eclipse.packager.rpm.signature; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.bouncycastle.bcpg.ArmoredInputStream; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPrivateKey; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing; +import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.eclipse.packager.rpm.parse.RpmInputStream; import org.junit.jupiter.api.Test; public class RawRpmFileSignatureProcessorTest { - + @Test - public void test_signing_existing_rpm() { - + public void test_signing_existing_rpm() throws IOException, PGPException { + String passPhrase = "testkey"; + File rpm = new File("src/test/resources/data/org.eclipse.scada-0.2.1-1.noarch.rpm"); + File private_key = new File("src/test/resources/key/private_key.txt"); + if(!rpm.exists() || !private_key.exists()) { + return; + } + InputStream rpmStream = new FileInputStream(rpm); + InputStream privateKeyStream = new FileInputStream(private_key); + + RawRpmFileSignatureProcessor signatureProcessor = new RawRpmFileSignatureProcessor(); + + ArmoredInputStream armor = new ArmoredInputStream(privateKeyStream); + PGPSecretKeyRing secretKeyRing = new BcPGPSecretKeyRing(armor); + PGPSecretKey secretKey = secretKeyRing.getSecretKey(); + PGPPrivateKey privateKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase.toCharArray())); + + signatureProcessor.perform(new RpmInputStream(rpmStream), privateKey); } } diff --git a/rpm/src/test/resources/key/private_key.txt b/rpm/src/test/resources/key/private_key.txt new file mode 100644 index 0000000..a0e8804 --- /dev/null +++ b/rpm/src/test/resources/key/private_key.txt @@ -0,0 +1,116 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: OpenPGP.js v4.10.10 +Comment: https://openpgpjs.org + +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 +25uiY4mR +=iZMt +-----END PGP PRIVATE KEY BLOCK----- diff --git a/rpm/src/test/resources/key/public_key.txt b/rpm/src/test/resources/key/public_key.txt new file mode 100644 index 0000000..71cf1d4 --- /dev/null +++ b/rpm/src/test/resources/key/public_key.txt @@ -0,0 +1,56 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: OpenPGP.js v4.10.10 +Comment: https://openpgpjs.org + +xsFNBGQAqhABEAC5XnVvJlEeArPCduQlVs+ekqIYOTJP40ShB7EOnM3N6ep8 +nzYvcYfuxh+cO8DO/C71AEvs9YnHnoZwUX/ki31d1tOzW6LLsuspQqHZUXH2 +HQsm8ph5hCQi/Cx50Ym22LXYI7Dkg18xEVJK+np6p9cR/grQfprXbGeJxc1v +tdNJZNp4p9txlwNLMGi7cVxebn3WFMieXdwx9qPrZHob+r7r/Jm0ORUvPHRz +Q8SLR1SxpaUm1EVZKu6A8sQ8GLgqLtm4PZtUznucMZN5Vntn9OthSPXtfw7L +Z6jERwrSw9B9k8cRvOws1CFIDMzZwz8GXwa9QRQUF0doOCKW/MEX0h8q+B8i +kkWvpKKzYe8lGsuiXDjfiXgmnn8BlbUNej1vu2/kOUwZ6GtFq+7XWDv5mpHr +sr4+pcQULW8JqRlOxcCj4mU80ih6JE2BObUxbgGmuGk/B4ePkNv5GBYeGarg +LJz6ynlQDj6lo9yY8QmNPN5ddEAXOgVGyHKyjNNKWl1jj7PqS2vpOkWRPLto +3XWXVuWjifuEnV/tHVtiATOM6tOxjL51+357FtX7YnEDznMEiu6SLAeRkhwi +ffH6Cyj12rbEURQThk5uQIKAeiMMfjKlTdxoR1+xra5HgQCNZZ9B+L0fNEa9 +vwVDgK5GE+94sugK1kT3fL1GCQArysaqLU2J0wARAQABzRJtYXQgPHRlc3RA +dGVzdC5mcj7CwY0EEAEIACAFAmQAqhAGCwkHCAMCBBUICgIEFgIBAAIZAQIb +AwIeAQAhCRAYZHrDZ59XIxYhBAe/mYJMNSTs1hnZMxhkesNnn1cjkn0P/2Ny +S+m53O3ZTWGb47YWLrObfe5qIMUNkaOPXGAZTxvdtVn7dp74idWt2QnF9Tji +o8yWnADf/s/5/+lCoiv6n8BFja7sOm/UOv2R+BTLEFEvAHl/s9ZWQSrxOXfE +u88Jk1IaWsFrbv/xZtK/TS39YzjTrLdrC0uIBLbmm3MYgyo2fABFhsgCmcas +VdI+VpGhgl577a/xpIa8qF9M4mke7I5odGEab9OE5T8Jh5bYxfu31L57nm6u +bWOAjDCRaIrlkqSDd0pUr9iT5CyY72WOvmmCKTqbq1Vy9FvsSJahwqKhSep3 +mAq0nXEkpVw9hYAbBZDEiGgYYAWgqQy8R+89VaR7P5jpq8cwyarZIjBwi8FB +GAx1hFIfAYEaW7OWgMlg/xD173wwtqAd681TwKx+rtKYTb7lSn3LNxBZU6N2 +MYcbCAsy7MlLKF8cOaE0m0gQcQCjHPtTqNxsdSIhCR70Vzzyq0q9g8BaFzOI +jDCnl/m7pdgTZrW5NGFhC+/w5MC106DKufD1qVb0ETXCxVM9/0HLx6T1/rIH +7xxJXryv0EnGuAeLCK6EjXrhKVPH1dgU8puyo6iLwkkiRuoYEThB4CjYbHl0 ++ZdA3LqtNR9QPSXLGiPw57Ozy+EcUzUuBCBGv+P3ngUYV83qJ0XcL79fsQc7 +72ngjGja4BYP7VMJGN6XzsFNBGQAqhABEAD3r/nwOCKIsTuyOY0zPXJAMFtw +MO4SWiw6MvH4v1Hdl82luYCwjEozB6SOsq5o0Ic7lbeLNBY/HB50vdqnDljY +l/9ubQaB106o/eYWqXs0SrH6Qbas4Nzrg+twWSxzb49y1s1+3rS1eWGcJ8uF +REkHCkECKtCzBpMh/tFBA7Mq1zXkux6aOsK3XGpcImqGkokHKsq82j4KfDzw +SOtvl/P6ElggsvBQNtOfx9R6IUwKUBskSfMpm3ktGcsgBnyxG/5m2D5f4Hne +hKota1FnFKUEuueb0QCJdEWUsB/g7EsHHSHiwAfVTNSOhf0+UPwsLC5Ji1NM +mH4U6cTCl2bs3wpT/hyZxcnLis5k/c+VnoY4PSowhgYxjzFkMxejg8qMoFat +8Rw2bEtLYwOyWRBNKIHHh8PeFg7N/sWeKywlVOmBFF74Lqrzj8RVMZfzbf4t +ukwh9YT7+Jrpm8OAfedLQspsuRND+4/Wqm5EC1iYILIKomcCk6MCzDmji1uy +xO6+wVqf0m+s1pEeH/yV90XTo1wVvgepAAcgXHVP3pDjkZwUqwf2k55zl/jE +KS/mv8Iypij6VYfME8XycXUgJcLLVBam1On9oa/O2/pMj4pLNv7HEXWi6boa +ibJXfmMCR1NUuhyMHBIwlp1fxgMTyre8w9p9L+vr+AXEdxrKDzsLuDfNTQAR +AQABwsF2BBgBCAAJBQJkAKoQAhsMACEJEBhkesNnn1cjFiEEB7+Zgkw1JOzW +GdkzGGR6w2efVyNEQA/8D7c2JsqIa73Q6XgxAQfFtdWvbQwYL5dfASMUl8bY +6esdMSmYAjJAA0LYGYoAKjZM1GE6HD+98zdYhuNG/gEyJmLsZ24l7ZKK94xk +QlfyjREZBrAZBLjrYvX3EovRfnQ397BLyDy07q1+ss0OId5wJmJ7KL1vCONL +1osI8BwwjlW3F83XFJkU97V6dw1l6SZrMX1VjGeN2jxcSoZVmY5RLav6iJ7m +/7/+82x9P8lN10wOdv7mxT9rL4CCJi4ILlJOc8MucprKAGZ6KwSaTZ+KvzlE +XchldHQKhXxOTHH+IDEjQxU70N4YqtCroa9BNGqq3f6L0Zv7ya5fUZoNO4RY +T8YnFKKFuWDpV9ABTRKxnKKkeufwgTYApuyJzmkkiYx+0kN9zf7QROyOXdhh +8Cvdz8aB0nWKDNXg6Ha6ynpavOPGF5tqImqChiAHiT4prgdmASu+0MJRixkX +N3H6lg6Y99ltyZBGKCi3eAVukf/68mZ1OBOzj7utK9Ipd1jz1b6J0vRVdYGv +f4k6LdvbunWdZka9ic1DcW5GoO/4sX2CRC4fOEz4SVXVC29J5/p7ChvGeIuY +8KJ+msWSRDPrfeEi0AK50GpHnOP6eS2fg+Iz02lxTjjF228rfjsJnySFih/6 ++QBYhJWmJ7fgLetQ0RMwCKRGReGdTvi07YHbm6JjiZE= +=wNfm +-----END PGP PUBLIC KEY BLOCK----- From 2c40795317119b0b47313c0e7a960d3b2cac23a1 Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Mon, 6 Mar 2023 19:47:42 +0100 Subject: [PATCH 05/17] read headers ok, read payload wip --- .../RawRpmFileSignatureProcessor.java | 50 ++++++++----------- .../RawRpmFileSignatureProcessorTest.java | 3 +- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java index e03211c..310ada8 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java @@ -1,56 +1,50 @@ package org.eclipse.packager.rpm.signature; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; -import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; import org.bouncycastle.openpgp.PGPPrivateKey; -import org.eclipse.packager.rpm.RpmLead; import org.eclipse.packager.rpm.RpmSignatureTag; -import org.eclipse.packager.rpm.RpmTag; import org.eclipse.packager.rpm.header.Header; -import org.eclipse.packager.rpm.parse.InputHeader; import org.eclipse.packager.rpm.parse.RpmInputStream; -public class RawRpmFileSignatureProcessor { +import com.google.common.io.CountingInputStream; - private RpmLead lead; +public class RawRpmFileSignatureProcessor { - private InputHeader payloadHeader; + public void perform(InputStream in, PGPPrivateKey privateKey) throws IOException { - private Header signatureHeader; + RpmInputStream ref = new RpmInputStream(in); + ref.available(); +// in.reset(); - private CpioArchiveOutputStream cpioArchiveOutputStream; + CountingInputStream count = new CountingInputStream(in); + DataInputStream data = new DataInputStream(count); - public void perform(RpmInputStream input, PGPPrivateKey privateKey) throws IOException { - input.available(); - this.lead = input.getLead(); - this.payloadHeader = input.getPayloadHeader(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IOUtils.copy(input.getCpioStream(), out); - this.cpioArchiveOutputStream = new CpioArchiveOutputStream(out); + byte[] lead = IOUtils.readRange(data, 96); + byte[] signatureHeader = IOUtils.readRange(data, (int) ref.getSignatureHeader().getLength()); + byte[] payloadHeader = IOUtils.readRange(data, (int) ref.getPayloadHeader().getLength()); + byte[] payloadBytes = IOUtils.toByteArray(ref.getCpioStream()); - byte[] headerBytes = new byte[(int) payloadHeader.getLength()]; - input.read(headerBytes, (int) payloadHeader.getStart(), (int) payloadHeader.getLength()); - ByteBuffer header = ByteBuffer.allocate((int) payloadHeader.getLength()); - ReadableByteChannel headerChannel = Channels.newChannel(new ByteArrayInputStream(headerBytes)); + ByteBuffer header = ByteBuffer.allocate(payloadHeader.length); + ReadableByteChannel headerChannel = Channels.newChannel(new ByteArrayInputStream(payloadHeader)); IOUtils.readFully(headerChannel, header); - byte[] dataBytes = IOUtils.toByteArray(input.getCpioStream()); - ByteBuffer data = ByteBuffer.allocate(dataBytes.length); - ReadableByteChannel payloadChannel = Channels.newChannel(new ByteArrayInputStream(dataBytes)); - IOUtils.readFully(payloadChannel, data); + ByteBuffer payload = ByteBuffer.allocate(payloadBytes.length); + ReadableByteChannel payloadChannel = Channels.newChannel(new ByteArrayInputStream(payloadHeader)); + IOUtils.readFully(payloadChannel, payload); RsaSignatureProcessor processor = new RsaSignatureProcessor(privateKey); processor.feedHeader(header.slice()); - processor.feedPayloadData(data.slice()); + processor.feedPayloadData(payload.slice()); - this.signatureHeader = new Header<>(); - processor.finish(signatureHeader); + Header signature = new Header<>(); + processor.finish(signature); } } diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java index 701b36b..b1adb37 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java @@ -13,7 +13,6 @@ import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing; import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; -import org.eclipse.packager.rpm.parse.RpmInputStream; import org.junit.jupiter.api.Test; public class RawRpmFileSignatureProcessorTest { @@ -36,7 +35,7 @@ public void test_signing_existing_rpm() throws IOException, PGPException { PGPSecretKey secretKey = secretKeyRing.getSecretKey(); PGPPrivateKey privateKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase.toCharArray())); - signatureProcessor.perform(new RpmInputStream(rpmStream), privateKey); + signatureProcessor.perform(rpmStream, privateKey); } } From b26fc47438c2dd56e3768f1383eb7b2dcaed569f Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Tue, 7 Mar 2023 19:08:44 +0100 Subject: [PATCH 06/17] finally works, need to test signature --- .../RawRpmFileSignatureProcessor.java | 50 ------ .../signature/RpmFileSignatureProcessor.java | 146 ++++++++++++++++++ .../RawRpmFileSignatureProcessorTest.java | 19 +-- 3 files changed, 149 insertions(+), 66 deletions(-) delete mode 100644 rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java create mode 100644 rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java deleted file mode 100644 index 310ada8..0000000 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessor.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.eclipse.packager.rpm.signature; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; - -import org.apache.commons.compress.utils.IOUtils; -import org.bouncycastle.openpgp.PGPPrivateKey; -import org.eclipse.packager.rpm.RpmSignatureTag; -import org.eclipse.packager.rpm.header.Header; -import org.eclipse.packager.rpm.parse.RpmInputStream; - -import com.google.common.io.CountingInputStream; - -public class RawRpmFileSignatureProcessor { - - public void perform(InputStream in, PGPPrivateKey privateKey) throws IOException { - - RpmInputStream ref = new RpmInputStream(in); - ref.available(); -// in.reset(); - - CountingInputStream count = new CountingInputStream(in); - DataInputStream data = new DataInputStream(count); - - byte[] lead = IOUtils.readRange(data, 96); - byte[] signatureHeader = IOUtils.readRange(data, (int) ref.getSignatureHeader().getLength()); - byte[] payloadHeader = IOUtils.readRange(data, (int) ref.getPayloadHeader().getLength()); - byte[] payloadBytes = IOUtils.toByteArray(ref.getCpioStream()); - - ByteBuffer header = ByteBuffer.allocate(payloadHeader.length); - ReadableByteChannel headerChannel = Channels.newChannel(new ByteArrayInputStream(payloadHeader)); - IOUtils.readFully(headerChannel, header); - - ByteBuffer payload = ByteBuffer.allocate(payloadBytes.length); - ReadableByteChannel payloadChannel = Channels.newChannel(new ByteArrayInputStream(payloadHeader)); - IOUtils.readFully(payloadChannel, payload); - - RsaSignatureProcessor processor = new RsaSignatureProcessor(privateKey); - processor.feedHeader(header.slice()); - processor.feedPayloadData(payload.slice()); - - Header signature = new Header<>(); - processor.finish(signature); - } -} diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java new file mode 100644 index 0000000..0069853 --- /dev/null +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -0,0 +1,146 @@ +package org.eclipse.packager.rpm.signature; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; + +import org.apache.commons.compress.utils.IOUtils; +import org.bouncycastle.bcpg.ArmoredInputStream; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPrivateKey; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing; +import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.eclipse.packager.rpm.RpmSignatureTag; +import org.eclipse.packager.rpm.Rpms; +import org.eclipse.packager.rpm.header.Header; +import org.eclipse.packager.rpm.header.Headers; +import org.eclipse.packager.rpm.parse.RpmInputStream; + +public class RpmFileSignatureProcessor { + + /** + *

+ * Perform the signature of the given RPM file with the given private key. + *

+ * + * @param rpmIn : RPM file as an {@link InputStream} + * @param privateKeyIn : encrypted private key as {@link InputStream} + * @param passphrase : passphrase to decrypt the private key + * @return The signed RPM as an {@link OutputStream} + * @throws IOException + * @throws PGPException + */ + public OutputStream perform(InputStream rpmIn, InputStream privateKeyIn, String passphrase) + throws IOException, PGPException { + + PGPPrivateKey privateKey = getPrivateKey(privateKeyIn, passphrase); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtils.copy(rpmIn, out); + byte[] buf = out.toByteArray(); + RpmInputStream ref = getRpmInputStream(buf); + ByteArrayInputStream data = new ByteArrayInputStream(buf); + + byte[] lead = IOUtils.readRange(data, 96); + IOUtils.readRange(data, (int) ref.getSignatureHeader().getLength()); // skip existing signature header + byte[] payloadHeader = IOUtils.readRange(data, (int) ref.getPayloadHeader().getLength()); + byte[] payload = IOUtils.toByteArray(data); + + byte[] signature = buildSignature(privateKey, payloadHeader, payload); + + ByteArrayOutputStream result = new ByteArrayOutputStream(); + result.write(lead); + result.write(signature); + result.write(payloadHeader); + result.write(payload); + + return result; + } + + /** + *

+ * Sign the payload with its header with the given private key and return the + * signature header as a bytes array. For more information about RPM format, see + * https://rpm-software-management.github.io/rpm/manual/format.html + *

+ * + * @param privateKey : private key already extracted + * @param payloadHeader : Payload's header as byte array + * @param payload : payload as byte array + * @return signature header as a bytes array + * @throws IOException + */ + private byte[] buildSignature(PGPPrivateKey privateKey, byte[] payloadHeader, byte[] payload) throws IOException { + ByteBuffer headerBuf = bufBytes(payloadHeader); + ByteBuffer payloadBuf = bufBytes(payload); + + RsaSignatureProcessor processor = new RsaSignatureProcessor(privateKey); + processor.feedHeader(headerBuf.slice()); + processor.feedPayloadData(payloadBuf.slice()); + + Header signatureHeader = new Header<>(); + processor.finish(signatureHeader); + + return Headers.render(signatureHeader.makeEntries(), true, Rpms.IMMUTABLE_TAG_SIGNATURE).array(); + } + + /** + *

+ * Convert an array of bytes into a ByteBuffer + *

+ * + * @param data : byte array to convert + * @return a {@link ByteBuffer} built with data + * @throws IOException + */ + private ByteBuffer bufBytes(byte[] data) throws IOException { + ByteBuffer buf = ByteBuffer.allocate(data.length); + ReadableByteChannel headerChannel = Channels.newChannel(new ByteArrayInputStream(data)); + IOUtils.readFully(headerChannel, buf); + return buf; + } + + /** + *

+ * Parse the byte[] to an RpmInputStream + *

+ * + * @param buf : byte array representing the rpm file + * @return {@link RpmInputStream} + * @throws IOException + */ + private RpmInputStream getRpmInputStream(byte[] buf) throws IOException { + RpmInputStream ref = new RpmInputStream(new ByteArrayInputStream(buf)); + ref.available(); // init RpmInputStream + ref.close(); + return ref; + } + + /** + *

+ * Decrypt and retrieve the private key + *

+ * + * @param privateKeyIn : InputStream containing the encrypted private key + * @param passphrase : passphrase to decrypt private key + * @return private key as {@link PGPPrivateKey} + * @throws PGPException : if the private key cannot be extrated + * @throws IOException : if error happened with InputStream + */ + private PGPPrivateKey getPrivateKey(InputStream privateKeyIn, String passphrase) throws PGPException, IOException { + ArmoredInputStream armor = new ArmoredInputStream(privateKeyIn); + PGPSecretKeyRing secretKeyRing = new BcPGPSecretKeyRing(armor); + PGPSecretKey secretKey = secretKeyRing.getSecretKey(); + return secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()) + .build(passphrase.toCharArray())); + } +} diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java index b1adb37..c273396 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java @@ -5,14 +5,7 @@ import java.io.IOException; import java.io.InputStream; -import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPrivateKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing; -import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; -import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; import org.junit.jupiter.api.Test; public class RawRpmFileSignatureProcessorTest { @@ -22,20 +15,14 @@ public void test_signing_existing_rpm() throws IOException, PGPException { String passPhrase = "testkey"; File rpm = new File("src/test/resources/data/org.eclipse.scada-0.2.1-1.noarch.rpm"); File private_key = new File("src/test/resources/key/private_key.txt"); - if(!rpm.exists() || !private_key.exists()) { + if (!rpm.exists() || !private_key.exists()) { return; } InputStream rpmStream = new FileInputStream(rpm); InputStream privateKeyStream = new FileInputStream(private_key); - RawRpmFileSignatureProcessor signatureProcessor = new RawRpmFileSignatureProcessor(); - - ArmoredInputStream armor = new ArmoredInputStream(privateKeyStream); - PGPSecretKeyRing secretKeyRing = new BcPGPSecretKeyRing(armor); - PGPSecretKey secretKey = secretKeyRing.getSecretKey(); - PGPPrivateKey privateKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase.toCharArray())); - - signatureProcessor.perform(rpmStream, privateKey); + RpmFileSignatureProcessor signatureProcessor = new RpmFileSignatureProcessor(); + signatureProcessor.perform(rpmStream, privateKeyStream, passPhrase); } } From 935d4496ffad1a10aa00f4a1e687fec50041045e Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Wed, 8 Mar 2023 16:49:29 +0100 Subject: [PATCH 07/17] fix signature header + test parsing signed file ok --- .../signature/RpmFileSignatureProcessor.java | 73 +++++++++++++++++-- .../RawRpmFileSignatureProcessorTest.java | 28 ------- .../RpmFileSignatureProcessorTest.java | 40 ++++++++++ 3 files changed, 105 insertions(+), 36 deletions(-) delete mode 100644 rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java create mode 100644 rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java index 0069853..93ba7c5 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -8,6 +8,8 @@ import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; +import java.util.ArrayList; +import java.util.List; import org.apache.commons.compress.utils.IOUtils; import org.bouncycastle.bcpg.ArmoredInputStream; @@ -24,6 +26,12 @@ import org.eclipse.packager.rpm.header.Headers; import org.eclipse.packager.rpm.parse.RpmInputStream; +/** + * + * @author mat1e + * (member of Groupe EDF) + * + */ public class RpmFileSignatureProcessor { /** @@ -38,7 +46,7 @@ public class RpmFileSignatureProcessor { * @throws IOException * @throws PGPException */ - public OutputStream perform(InputStream rpmIn, InputStream privateKeyIn, String passphrase) + public ByteArrayOutputStream perform(InputStream rpmIn, InputStream privateKeyIn, String passphrase) throws IOException, PGPException { PGPPrivateKey privateKey = getPrivateKey(privateKeyIn, passphrase); @@ -80,17 +88,66 @@ public OutputStream perform(InputStream rpmIn, InputStream privateKeyIn, String * @throws IOException */ private byte[] buildSignature(PGPPrivateKey privateKey, byte[] payloadHeader, byte[] payload) throws IOException { + long archiveSize = payloadHeader.length + payload.length + 0L; ByteBuffer headerBuf = bufBytes(payloadHeader); ByteBuffer payloadBuf = bufBytes(payload); - - RsaSignatureProcessor processor = new RsaSignatureProcessor(privateKey); - processor.feedHeader(headerBuf.slice()); - processor.feedPayloadData(payloadBuf.slice()); - Header signatureHeader = new Header<>(); - processor.finish(signatureHeader); + List signatureProcessors = getDefaultsSignatureProcessors(); + signatureProcessors.add(new RsaSignatureProcessor(privateKey)); + for (SignatureProcessor processor : signatureProcessors) { + processor.init(archiveSize); + processor.feedHeader(headerBuf.slice()); + processor.feedPayloadData(payloadBuf.slice()); + headerBuf.clear(); + payloadBuf.clear(); + processor.finish(signatureHeader); + } + ByteBuffer signatureBuf = Headers.render(signatureHeader.makeEntries(), true, Rpms.IMMUTABLE_TAG_SIGNATURE); + final int payloadSize = signatureBuf.remaining(); + final int padding = Rpms.padding(payloadSize); + byte[] signature = safeReadBuffer(signatureBuf); + ByteArrayOutputStream result = new ByteArrayOutputStream(); + result.write(signature); + if (padding > 0) { + result.write(safeReadBuffer(ByteBuffer.wrap(Rpms.EMPTY_128, 0, padding))); + } + return result.toByteArray(); + } + + /** + *

+ * Safe read (without buffer bytes) the given buffer and return it to a byte + * array + *

+ * + * @param buf : the {@link ByteBuffer} to read + * @return byte array + */ + private byte[] safeReadBuffer(ByteBuffer buf) { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + while (buf.hasRemaining()) { + result.write(buf.get()); + } + return result.toByteArray(); + } - return Headers.render(signatureHeader.makeEntries(), true, Rpms.IMMUTABLE_TAG_SIGNATURE).array(); + /** + *

+ * Return all default {@link SignatureProcessor} defined in + * {@link SignatureProcessors} + *

+ * + * @return {@link List} of {@link SignatureProcessor} + */ + private List getDefaultsSignatureProcessors() { + List signatureProcessors = new ArrayList<>(); + signatureProcessors.add(SignatureProcessors.size()); + signatureProcessors.add(SignatureProcessors.sha256Header()); + signatureProcessors.add(SignatureProcessors.sha1Header()); + signatureProcessors.add(SignatureProcessors.md5()); + signatureProcessors.add(SignatureProcessors.payloadSize()); + + return signatureProcessors; } /** diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java deleted file mode 100644 index c273396..0000000 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RawRpmFileSignatureProcessorTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.eclipse.packager.rpm.signature; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -import org.bouncycastle.openpgp.PGPException; -import org.junit.jupiter.api.Test; - -public class RawRpmFileSignatureProcessorTest { - - @Test - public void test_signing_existing_rpm() throws IOException, PGPException { - String passPhrase = "testkey"; - File rpm = new File("src/test/resources/data/org.eclipse.scada-0.2.1-1.noarch.rpm"); - File private_key = new File("src/test/resources/key/private_key.txt"); - if (!rpm.exists() || !private_key.exists()) { - return; - } - InputStream rpmStream = new FileInputStream(rpm); - InputStream privateKeyStream = new FileInputStream(private_key); - - RpmFileSignatureProcessor signatureProcessor = new RpmFileSignatureProcessor(); - signatureProcessor.perform(rpmStream, privateKeyStream, passPhrase); - } - -} diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java new file mode 100644 index 0000000..3915827 --- /dev/null +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java @@ -0,0 +1,40 @@ +package org.eclipse.packager.rpm.signature; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.bouncycastle.openpgp.PGPException; +import org.eclipse.packager.rpm.app.Dumper; +import org.eclipse.packager.rpm.parse.RpmInputStream; +import org.junit.jupiter.api.Test; + +public class RpmFileSignatureProcessorTest { + + @Test + public void test_signing_existing_rpm() throws IOException, PGPException { + String passPhrase = "testkey"; + File rpm = new File("src/test/resources/data/org.eclipse.scada-0.2.1-1.noarch.rpm"); + File private_key = new File("src/test/resources/key/private_key.txt"); + if (!rpm.exists() || !private_key.exists()) { + return; + } + InputStream rpmStream = new FileInputStream(rpm); + InputStream privateKeyStream = new FileInputStream(private_key); + System.out.println("###########################################################"); + Dumper.dumpAll(new RpmInputStream(new FileInputStream(rpm))); + System.out.println("###########################################################"); + + RpmFileSignatureProcessor signatureProcessor = new RpmFileSignatureProcessor(); + ByteArrayOutputStream signedPackage = signatureProcessor.perform(rpmStream, privateKeyStream, passPhrase); + byte[] bytes = signedPackage.toByteArray(); + RpmInputStream rpmSigned = new RpmInputStream(new ByteArrayInputStream(bytes)); + rpmSigned.available(); + System.out.println("###########################################################"); + Dumper.dumpAll(rpmSigned); + System.out.println("###########################################################"); + } +} From 213f33d722c08303ef8b2ca27507af7fb82ee54c Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Wed, 8 Mar 2023 19:37:16 +0100 Subject: [PATCH 08/17] fix ARCHIVE_SIZE --- .../signature/RpmFileSignatureProcessor.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java index 93ba7c5..51ff74e 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -24,12 +24,14 @@ import org.eclipse.packager.rpm.Rpms; import org.eclipse.packager.rpm.header.Header; import org.eclipse.packager.rpm.header.Headers; +import org.eclipse.packager.rpm.info.RpmInformation; +import org.eclipse.packager.rpm.info.RpmInformations; import org.eclipse.packager.rpm.parse.RpmInputStream; /** * - * @author mat1e - * (member of Groupe EDF) + * @author mat1e (member of + * Groupe EDF) * */ public class RpmFileSignatureProcessor { @@ -55,6 +57,7 @@ public ByteArrayOutputStream perform(InputStream rpmIn, InputStream privateKeyIn IOUtils.copy(rpmIn, out); byte[] buf = out.toByteArray(); RpmInputStream ref = getRpmInputStream(buf); + RpmInformation info = RpmInformations.makeInformation(ref); ByteArrayInputStream data = new ByteArrayInputStream(buf); byte[] lead = IOUtils.readRange(data, 96); @@ -62,7 +65,7 @@ public ByteArrayOutputStream perform(InputStream rpmIn, InputStream privateKeyIn byte[] payloadHeader = IOUtils.readRange(data, (int) ref.getPayloadHeader().getLength()); byte[] payload = IOUtils.toByteArray(data); - byte[] signature = buildSignature(privateKey, payloadHeader, payload); + byte[] signature = buildSignature(privateKey, payloadHeader, payload, info.getArchiveSize()); ByteArrayOutputStream result = new ByteArrayOutputStream(); result.write(lead); @@ -84,22 +87,23 @@ public ByteArrayOutputStream perform(InputStream rpmIn, InputStream privateKeyIn * @param privateKey : private key already extracted * @param payloadHeader : Payload's header as byte array * @param payload : payload as byte array + * @param archiveSize : archiveSize retrieved in {@link RpmInformation} * @return signature header as a bytes array * @throws IOException */ - private byte[] buildSignature(PGPPrivateKey privateKey, byte[] payloadHeader, byte[] payload) throws IOException { - long archiveSize = payloadHeader.length + payload.length + 0L; + private byte[] buildSignature(PGPPrivateKey privateKey, byte[] payloadHeader, byte[] payload, long archiveSize) + throws IOException { ByteBuffer headerBuf = bufBytes(payloadHeader); ByteBuffer payloadBuf = bufBytes(payload); Header signatureHeader = new Header<>(); List signatureProcessors = getDefaultsSignatureProcessors(); signatureProcessors.add(new RsaSignatureProcessor(privateKey)); for (SignatureProcessor processor : signatureProcessors) { + headerBuf.clear(); + payloadBuf.clear(); processor.init(archiveSize); processor.feedHeader(headerBuf.slice()); processor.feedPayloadData(payloadBuf.slice()); - headerBuf.clear(); - payloadBuf.clear(); processor.finish(signatureHeader); } ByteBuffer signatureBuf = Headers.render(signatureHeader.makeEntries(), true, Rpms.IMMUTABLE_TAG_SIGNATURE); From 6eac5b38f4599f50eda45d0c7fc2502dcc4e610a Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Fri, 10 Mar 2023 15:05:46 +0100 Subject: [PATCH 09/17] finalize tests + javadoc --- .../signature/RpmFileSignatureProcessor.java | 3 +- .../RpmFileSignatureProcessorTest.java | 89 +++++++++++++++++-- 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java index 51ff74e..0cfe327 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -38,7 +38,8 @@ public class RpmFileSignatureProcessor { /** *

- * Perform the signature of the given RPM file with the given private key. + * Perform the signature of the given RPM file with the given private key. This + * support only PGP. *

* * @param rpmIn : RPM file as an {@link InputStream} diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java index 3915827..bd7ace9 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java @@ -1,40 +1,113 @@ package org.eclipse.packager.rpm.signature; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.openpgp.PGPException; -import org.eclipse.packager.rpm.app.Dumper; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRing; +import org.eclipse.packager.rpm.RpmSignatureTag; +import org.eclipse.packager.rpm.Rpms; +import org.eclipse.packager.rpm.parse.InputHeader; import org.eclipse.packager.rpm.parse.RpmInputStream; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +@TestMethodOrder(OrderAnnotation.class) public class RpmFileSignatureProcessorTest { + private static final String RESULT_FILE_PATH = "src/test/resources/result/org.eclipse.scada-0.2.1-1.noarch.rpm"; + private static final String RESULT_DIR = "src/test/resources/result"; + @Test + @Order(1) public void test_signing_existing_rpm() throws IOException, PGPException { String passPhrase = "testkey"; File rpm = new File("src/test/resources/data/org.eclipse.scada-0.2.1-1.noarch.rpm"); File private_key = new File("src/test/resources/key/private_key.txt"); if (!rpm.exists() || !private_key.exists()) { - return; + fail("Input files rpm or private_key does not exist"); } InputStream rpmStream = new FileInputStream(rpm); InputStream privateKeyStream = new FileInputStream(private_key); - System.out.println("###########################################################"); - Dumper.dumpAll(new RpmInputStream(new FileInputStream(rpm))); - System.out.println("###########################################################"); RpmFileSignatureProcessor signatureProcessor = new RpmFileSignatureProcessor(); ByteArrayOutputStream signedPackage = signatureProcessor.perform(rpmStream, privateKeyStream, passPhrase); byte[] bytes = signedPackage.toByteArray(); + + File resultDirectory = new File(RESULT_DIR); + resultDirectory.mkdir(); + File signedRpm = new File(RESULT_FILE_PATH); + signedRpm.createNewFile(); + FileOutputStream resultOut = new FileOutputStream(signedRpm); + resultOut.write(bytes); + resultOut.close(); + + RpmInputStream initialRpm = new RpmInputStream(new FileInputStream(rpm)); + initialRpm.available(); + initialRpm.close(); + InputHeader initialHeader = initialRpm.getSignatureHeader(); RpmInputStream rpmSigned = new RpmInputStream(new ByteArrayInputStream(bytes)); rpmSigned.available(); - System.out.println("###########################################################"); - Dumper.dumpAll(rpmSigned); - System.out.println("###########################################################"); + rpmSigned.close(); + InputHeader signedHeader = rpmSigned.getSignatureHeader(); + + int initialSize = (int) initialHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); + int initialPayloadSize = (int) initialHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); + String initialSha1 = initialHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); + String initialMd5 = Rpms.dumpValue(initialHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); + + int signedSize = (int) signedHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); + int signedPayloadSize = (int) signedHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); + String signedSha1 = signedHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); + String signedMd5 = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); + String pgpSignature = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.PGP).get().getValue()); + + assertEquals(initialSize, signedSize); + assertEquals(initialPayloadSize, signedPayloadSize); + assertEquals(initialSha1, signedSha1); + assertEquals(initialMd5, signedMd5); + assertNotNull(pgpSignature); + } + + @Test + @Order(2) + public void verify_rpm_signature() throws IOException, PGPException { + File public_key = new File("src/test/resources/key/public_key.txt"); + File signedRpm = new File(RESULT_FILE_PATH); + if (!public_key.exists() || !signedRpm.exists()) { + fail("Input files signedRpm or public_key does not exist"); + } + InputStream publicKeyStream = new FileInputStream(public_key); + ArmoredInputStream armoredInputStream = new ArmoredInputStream(publicKeyStream); + PGPPublicKeyRing publicKeyRing = new BcPGPPublicKeyRing(armoredInputStream); + PGPPublicKey publicKey = publicKeyRing.getPublicKey(); + // TODO Signature Check + } + + @AfterAll + public static void clean() { + File resultDir = new File(RESULT_DIR); + File signedRpm = new File(RESULT_FILE_PATH); + if (resultDir.exists()) { + if (signedRpm.exists()) { + signedRpm.delete(); + } + resultDir.delete(); + } } } From c74bdcbc49f903e10c2c8a4e48cb8dac29921e6d Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Tue, 14 Mar 2023 17:56:52 +0100 Subject: [PATCH 10/17] code review --- .../signature/RpmFileSignatureProcessor.java | 42 ++++++++++++------- .../RpmFileSignatureProcessorTest.java | 15 +++++-- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java index 0cfe327..957bae3 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -1,3 +1,15 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * mat1e, Groupe EDF - initial API and implementation + ********************************************************************************/ package org.eclipse.packager.rpm.signature; import java.io.ByteArrayInputStream; @@ -30,8 +42,9 @@ /** * - * @author mat1e (member of - * Groupe EDF) + * Sign existing RPM file by calling + * {@link #perform(InputStream, InputStream, String)} + * * */ public class RpmFileSignatureProcessor { @@ -49,7 +62,7 @@ public class RpmFileSignatureProcessor { * @throws IOException * @throws PGPException */ - public ByteArrayOutputStream perform(InputStream rpmIn, InputStream privateKeyIn, String passphrase) + public static ByteArrayOutputStream perform(InputStream rpmIn, InputStream privateKeyIn, String passphrase) throws IOException, PGPException { PGPPrivateKey privateKey = getPrivateKey(privateKeyIn, passphrase); @@ -92,8 +105,8 @@ public ByteArrayOutputStream perform(InputStream rpmIn, InputStream privateKeyIn * @return signature header as a bytes array * @throws IOException */ - private byte[] buildSignature(PGPPrivateKey privateKey, byte[] payloadHeader, byte[] payload, long archiveSize) - throws IOException { + private static byte[] buildSignature(PGPPrivateKey privateKey, byte[] payloadHeader, byte[] payload, + long archiveSize) throws IOException { ByteBuffer headerBuf = bufBytes(payloadHeader); ByteBuffer payloadBuf = bufBytes(payload); Header signatureHeader = new Header<>(); @@ -128,7 +141,7 @@ private byte[] buildSignature(PGPPrivateKey privateKey, byte[] payloadHeader, by * @param buf : the {@link ByteBuffer} to read * @return byte array */ - private byte[] safeReadBuffer(ByteBuffer buf) { + private static byte[] safeReadBuffer(ByteBuffer buf) { ByteArrayOutputStream result = new ByteArrayOutputStream(); while (buf.hasRemaining()) { result.write(buf.get()); @@ -144,7 +157,7 @@ private byte[] safeReadBuffer(ByteBuffer buf) { * * @return {@link List} of {@link SignatureProcessor} */ - private List getDefaultsSignatureProcessors() { + private static List getDefaultsSignatureProcessors() { List signatureProcessors = new ArrayList<>(); signatureProcessors.add(SignatureProcessors.size()); signatureProcessors.add(SignatureProcessors.sha256Header()); @@ -164,7 +177,7 @@ private List getDefaultsSignatureProcessors() { * @return a {@link ByteBuffer} built with data * @throws IOException */ - private ByteBuffer bufBytes(byte[] data) throws IOException { + private static ByteBuffer bufBytes(byte[] data) throws IOException { ByteBuffer buf = ByteBuffer.allocate(data.length); ReadableByteChannel headerChannel = Channels.newChannel(new ByteArrayInputStream(data)); IOUtils.readFully(headerChannel, buf); @@ -180,11 +193,11 @@ private ByteBuffer bufBytes(byte[] data) throws IOException { * @return {@link RpmInputStream} * @throws IOException */ - private RpmInputStream getRpmInputStream(byte[] buf) throws IOException { - RpmInputStream ref = new RpmInputStream(new ByteArrayInputStream(buf)); - ref.available(); // init RpmInputStream - ref.close(); - return ref; + private static RpmInputStream getRpmInputStream(byte[] buf) throws IOException { + try (RpmInputStream ref = new RpmInputStream(new ByteArrayInputStream(buf))) { + ref.available(); // init RpmInputStream + return ref; + } } /** @@ -198,7 +211,8 @@ private RpmInputStream getRpmInputStream(byte[] buf) throws IOException { * @throws PGPException : if the private key cannot be extrated * @throws IOException : if error happened with InputStream */ - private PGPPrivateKey getPrivateKey(InputStream privateKeyIn, String passphrase) throws PGPException, IOException { + private static PGPPrivateKey getPrivateKey(InputStream privateKeyIn, String passphrase) + throws PGPException, IOException { ArmoredInputStream armor = new ArmoredInputStream(privateKeyIn); PGPSecretKeyRing secretKeyRing = new BcPGPSecretKeyRing(armor); PGPSecretKey secretKey = secretKeyRing.getSecretKey(); diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java index bd7ace9..0f1814f 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java @@ -35,7 +35,8 @@ public class RpmFileSignatureProcessorTest { @Test @Order(1) - public void test_signing_existing_rpm() throws IOException, PGPException { + public void testSigningExistingRpm() throws IOException, PGPException { + // Read files String passPhrase = "testkey"; File rpm = new File("src/test/resources/data/org.eclipse.scada-0.2.1-1.noarch.rpm"); File private_key = new File("src/test/resources/key/private_key.txt"); @@ -45,10 +46,11 @@ public void test_signing_existing_rpm() throws IOException, PGPException { InputStream rpmStream = new FileInputStream(rpm); InputStream privateKeyStream = new FileInputStream(private_key); - RpmFileSignatureProcessor signatureProcessor = new RpmFileSignatureProcessor(); - ByteArrayOutputStream signedPackage = signatureProcessor.perform(rpmStream, privateKeyStream, passPhrase); + // Sign the RPM + ByteArrayOutputStream signedPackage = RpmFileSignatureProcessor.perform(rpmStream, privateKeyStream, passPhrase); byte[] bytes = signedPackage.toByteArray(); + // Write the signed RPM File resultDirectory = new File(RESULT_DIR); resultDirectory.mkdir(); File signedRpm = new File(RESULT_FILE_PATH); @@ -57,6 +59,7 @@ public void test_signing_existing_rpm() throws IOException, PGPException { resultOut.write(bytes); resultOut.close(); + // Read the initial (non signed) rpm file RpmInputStream initialRpm = new RpmInputStream(new FileInputStream(rpm)); initialRpm.available(); initialRpm.close(); @@ -66,27 +69,31 @@ public void test_signing_existing_rpm() throws IOException, PGPException { rpmSigned.close(); InputHeader signedHeader = rpmSigned.getSignatureHeader(); + // Get informations of the initial rpm file int initialSize = (int) initialHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); int initialPayloadSize = (int) initialHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); String initialSha1 = initialHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); String initialMd5 = Rpms.dumpValue(initialHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); + // Read information of the signed rpm file int signedSize = (int) signedHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); int signedPayloadSize = (int) signedHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); String signedSha1 = signedHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); String signedMd5 = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); String pgpSignature = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.PGP).get().getValue()); + // Compare informations values of initial rpm and signed rpm assertEquals(initialSize, signedSize); assertEquals(initialPayloadSize, signedPayloadSize); assertEquals(initialSha1, signedSha1); assertEquals(initialMd5, signedMd5); + // verify if signature is present assertNotNull(pgpSignature); } @Test @Order(2) - public void verify_rpm_signature() throws IOException, PGPException { + public void verifyRpmSignature() throws IOException, PGPException { File public_key = new File("src/test/resources/key/public_key.txt"); File signedRpm = new File(RESULT_FILE_PATH); if (!public_key.exists() || !signedRpm.exists()) { From fa261407dc384c83c0d24aa01864a384b9e0aca6 Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Tue, 14 Mar 2023 17:58:49 +0100 Subject: [PATCH 11/17] license for test class --- .../rpm/signature/RpmFileSignatureProcessorTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java index 0f1814f..257580b 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java @@ -1,3 +1,15 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * mat1e, Groupe EDF - initial API and implementation + ********************************************************************************/ package org.eclipse.packager.rpm.signature; import static org.junit.jupiter.api.Assertions.assertEquals; From 5deea622bbbac238178ba35a47ba04986f0da55e Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Wed, 15 Mar 2023 17:31:52 +0100 Subject: [PATCH 12/17] hide RpmFileSignatureProcessor construc + try-with-resource in tests --- .../signature/RpmFileSignatureProcessor.java | 4 + .../RpmFileSignatureProcessorTest.java | 92 ++++++++++--------- 2 files changed, 55 insertions(+), 41 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java index 957bae3..c497b93 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -49,6 +49,10 @@ */ public class RpmFileSignatureProcessor { + private RpmFileSignatureProcessor() { + // Hide default constructor because of the static context + } + /** *

* Perform the signature of the given RPM file with the given private key. This diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java index 257580b..8bad6dc 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java @@ -55,52 +55,62 @@ public void testSigningExistingRpm() throws IOException, PGPException { if (!rpm.exists() || !private_key.exists()) { fail("Input files rpm or private_key does not exist"); } - InputStream rpmStream = new FileInputStream(rpm); - InputStream privateKeyStream = new FileInputStream(private_key); + try (InputStream rpmStream = new FileInputStream(rpm)) { + try (InputStream privateKeyStream = new FileInputStream(private_key)) { + // Sign the RPM + try (ByteArrayOutputStream signedPackage = RpmFileSignatureProcessor.perform(rpmStream, + privateKeyStream, passPhrase)) { + byte[] bytes = signedPackage.toByteArray(); - // Sign the RPM - ByteArrayOutputStream signedPackage = RpmFileSignatureProcessor.perform(rpmStream, privateKeyStream, passPhrase); - byte[] bytes = signedPackage.toByteArray(); + // Write the signed RPM + File resultDirectory = new File(RESULT_DIR); + resultDirectory.mkdir(); + File signedRpm = new File(RESULT_FILE_PATH); + signedRpm.createNewFile(); + try (FileOutputStream resultOut = new FileOutputStream(signedRpm)) { + resultOut.write(bytes); + resultOut.close(); - // Write the signed RPM - File resultDirectory = new File(RESULT_DIR); - resultDirectory.mkdir(); - File signedRpm = new File(RESULT_FILE_PATH); - signedRpm.createNewFile(); - FileOutputStream resultOut = new FileOutputStream(signedRpm); - resultOut.write(bytes); - resultOut.close(); - - // Read the initial (non signed) rpm file - RpmInputStream initialRpm = new RpmInputStream(new FileInputStream(rpm)); - initialRpm.available(); - initialRpm.close(); - InputHeader initialHeader = initialRpm.getSignatureHeader(); - RpmInputStream rpmSigned = new RpmInputStream(new ByteArrayInputStream(bytes)); - rpmSigned.available(); - rpmSigned.close(); - InputHeader signedHeader = rpmSigned.getSignatureHeader(); + // Read the initial (non signed) rpm file + RpmInputStream initialRpm = new RpmInputStream(new FileInputStream(rpm)); + initialRpm.available(); + initialRpm.close(); + InputHeader initialHeader = initialRpm.getSignatureHeader(); + RpmInputStream rpmSigned = new RpmInputStream(new ByteArrayInputStream(bytes)); + rpmSigned.available(); + rpmSigned.close(); + InputHeader signedHeader = rpmSigned.getSignatureHeader(); - // Get informations of the initial rpm file - int initialSize = (int) initialHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); - int initialPayloadSize = (int) initialHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); - String initialSha1 = initialHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); - String initialMd5 = Rpms.dumpValue(initialHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); + // Get informations of the initial rpm file + int initialSize = (int) initialHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); + int initialPayloadSize = (int) initialHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get() + .getValue(); + String initialSha1 = initialHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue() + .toString(); + String initialMd5 = Rpms + .dumpValue(initialHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); - // Read information of the signed rpm file - int signedSize = (int) signedHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); - int signedPayloadSize = (int) signedHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); - String signedSha1 = signedHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); - String signedMd5 = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); - String pgpSignature = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.PGP).get().getValue()); + // Read information of the signed rpm file + int signedSize = (int) signedHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); + int signedPayloadSize = (int) signedHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get() + .getValue(); + String signedSha1 = signedHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue() + .toString(); + String signedMd5 = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); + String pgpSignature = Rpms + .dumpValue(signedHeader.getEntry(RpmSignatureTag.PGP).get().getValue()); - // Compare informations values of initial rpm and signed rpm - assertEquals(initialSize, signedSize); - assertEquals(initialPayloadSize, signedPayloadSize); - assertEquals(initialSha1, signedSha1); - assertEquals(initialMd5, signedMd5); - // verify if signature is present - assertNotNull(pgpSignature); + // Compare informations values of initial rpm and signed rpm + assertEquals(initialSize, signedSize); + assertEquals(initialPayloadSize, signedPayloadSize); + assertEquals(initialSha1, signedSha1); + assertEquals(initialMd5, signedMd5); + // verify if signature is present + assertNotNull(pgpSignature); + } + } + } + } } @Test From b033128470e2efb36cd13992a7f9974777975b83 Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Mon, 20 Mar 2023 19:08:50 +0100 Subject: [PATCH 13/17] rework for memory optimization --- .../signature/RpmFileSignatureProcessor.java | 159 ++++++++---------- .../RpmFileSignatureProcessorTest.java | 92 +++++----- 2 files changed, 107 insertions(+), 144 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java index c497b93..b368418 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -12,18 +12,18 @@ ********************************************************************************/ package org.eclipse.packager.rpm.signature; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; +import java.nio.channels.FileChannel; +import java.nio.channels.WritableByteChannel; import java.util.ArrayList; import java.util.List; -import org.apache.commons.compress.utils.IOUtils; import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPrivateKey; @@ -56,154 +56,127 @@ private RpmFileSignatureProcessor() { /** *

* Perform the signature of the given RPM file with the given private key. This - * support only PGP. + * support only PGP. Write the result into the given {@link OutputStream} *

* - * @param rpmIn : RPM file as an {@link InputStream} + * @param rpm : RPM file * @param privateKeyIn : encrypted private key as {@link InputStream} * @param passphrase : passphrase to decrypt the private key - * @return The signed RPM as an {@link OutputStream} + * @param out : {@link OutputStream} to write to * @throws IOException * @throws PGPException */ - public static ByteArrayOutputStream perform(InputStream rpmIn, InputStream privateKeyIn, String passphrase) + public static void perform(File rpm, InputStream privateKeyIn, String passphrase, OutputStream out) throws IOException, PGPException { - PGPPrivateKey privateKey = getPrivateKey(privateKeyIn, passphrase); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IOUtils.copy(rpmIn, out); - byte[] buf = out.toByteArray(); - RpmInputStream ref = getRpmInputStream(buf); - RpmInformation info = RpmInformations.makeInformation(ref); - ByteArrayInputStream data = new ByteArrayInputStream(buf); - - byte[] lead = IOUtils.readRange(data, 96); - IOUtils.readRange(data, (int) ref.getSignatureHeader().getLength()); // skip existing signature header - byte[] payloadHeader = IOUtils.readRange(data, (int) ref.getPayloadHeader().getLength()); - byte[] payload = IOUtils.toByteArray(data); - - byte[] signature = buildSignature(privateKey, payloadHeader, payload, info.getArchiveSize()); + if (!rpm.exists()) { + throw new IOException("The file " + rpm.getName() + " does not exist"); + } - ByteArrayOutputStream result = new ByteArrayOutputStream(); - result.write(lead); - result.write(signature); - result.write(payloadHeader); - result.write(payload); + PGPPrivateKey privateKey = getPrivateKey(privateKeyIn, passphrase); - return result; + try (RpmInputStream rpmIn = new RpmInputStream(new FileInputStream(rpm))) { + long signatureHeaderStart = rpmIn.getSignatureHeader().getStart(); + long signatureHeaderLength = rpmIn.getSignatureHeader().getLength(); + long payloadHeaderStart = rpmIn.getPayloadHeader().getStart(); + long payloadHeaderLength = rpmIn.getPayloadHeader().getLength(); + RpmInformation info = RpmInformations.makeInformation(rpmIn); + long payloadStart = info.getHeaderEnd(); + long archiveSize = info.getArchiveSize(); + + if (signatureHeaderStart == 0L || signatureHeaderLength == 0L || payloadHeaderStart == 0L + || payloadHeaderLength == 0L || archiveSize == 0L) { + throw new IOException("Unable to read " + rpm.getName() + " informations."); + } + + try (FileInputStream in = new FileInputStream(rpm)) { + FileChannel channelIn = in.getChannel(); + ByteBuffer payloadHeaderBuff = ByteBuffer.allocate((int) payloadHeaderLength); + channelIn.read(payloadHeaderBuff, payloadHeaderStart); + ByteBuffer payloadBuff = ByteBuffer.allocate((int) (channelIn.size() - payloadStart)); + channelIn.read(payloadBuff, payloadStart); + + try (WritableByteChannel channelOut = Channels.newChannel(out)) { + channelIn.transferTo(0, 96, channelOut); + writeSignature(privateKey, payloadHeaderBuff, payloadBuff, info.getArchiveSize(), channelOut); + channelIn.transferTo(payloadHeaderStart, payloadHeaderLength, channelOut); + channelIn.transferTo(payloadStart, channelIn.size() - payloadStart, channelOut); + } + } + } } /** *

- * Sign the payload with its header with the given private key and return the - * signature header as a bytes array. For more information about RPM format, see - * https://rpm-software-management.github.io/rpm/manual/format.html *

* * @param privateKey : private key already extracted - * @param payloadHeader : Payload's header as byte array - * @param payload : payload as byte array + * @param payloadHeader : Payload's header as {@link ByteBuffer} + * @param payload : Payload as {@link ByteBuffer} * @param archiveSize : archiveSize retrieved in {@link RpmInformation} - * @return signature header as a bytes array + * @param channelOut : output to write to * @throws IOException */ - private static byte[] buildSignature(PGPPrivateKey privateKey, byte[] payloadHeader, byte[] payload, - long archiveSize) throws IOException { - ByteBuffer headerBuf = bufBytes(payloadHeader); - ByteBuffer payloadBuf = bufBytes(payload); + private static void writeSignature(PGPPrivateKey privateKey, ByteBuffer payloadHeader, ByteBuffer payload, + long archiveSize, WritableByteChannel channelOut) throws IOException { Header signatureHeader = new Header<>(); - List signatureProcessors = getDefaultsSignatureProcessors(); - signatureProcessors.add(new RsaSignatureProcessor(privateKey)); + List signatureProcessors = getSignatureProcessors(privateKey); + payloadHeader.flip(); + payload.flip(); for (SignatureProcessor processor : signatureProcessors) { - headerBuf.clear(); - payloadBuf.clear(); processor.init(archiveSize); - processor.feedHeader(headerBuf.slice()); - processor.feedPayloadData(payloadBuf.slice()); + processor.feedHeader(payloadHeader.slice()); + processor.feedPayloadData(payload.slice()); processor.finish(signatureHeader); } ByteBuffer signatureBuf = Headers.render(signatureHeader.makeEntries(), true, Rpms.IMMUTABLE_TAG_SIGNATURE); final int payloadSize = signatureBuf.remaining(); final int padding = Rpms.padding(payloadSize); - byte[] signature = safeReadBuffer(signatureBuf); - ByteArrayOutputStream result = new ByteArrayOutputStream(); - result.write(signature); + safeWrite(signatureBuf, channelOut); if (padding > 0) { - result.write(safeReadBuffer(ByteBuffer.wrap(Rpms.EMPTY_128, 0, padding))); + safeWrite(ByteBuffer.wrap(Rpms.EMPTY_128, 0, padding), channelOut); } - return result.toByteArray(); } /** *

- * Safe read (without buffer bytes) the given buffer and return it to a byte - * array + * Safe write (without buffer bytes) the given buffer into the channel array *

* - * @param buf : the {@link ByteBuffer} to read - * @return byte array + * @param buf : the {@link ByteBuffer} to write + * @param out : the {@link WritableByteChannel} output + * @throws IOException */ - private static byte[] safeReadBuffer(ByteBuffer buf) { - ByteArrayOutputStream result = new ByteArrayOutputStream(); + private static void safeWrite(ByteBuffer buf, WritableByteChannel out) throws IOException { while (buf.hasRemaining()) { - result.write(buf.get()); + out.write(buf); } - return result.toByteArray(); } /** *

- * Return all default {@link SignatureProcessor} defined in + * Return all {@link SignatureProcessor} required to perform signature * {@link SignatureProcessors} *

* + * @param privateKey : the private key, already extracted + * * @return {@link List} of {@link SignatureProcessor} */ - private static List getDefaultsSignatureProcessors() { + private static List getSignatureProcessors(PGPPrivateKey privateKey) { List signatureProcessors = new ArrayList<>(); signatureProcessors.add(SignatureProcessors.size()); signatureProcessors.add(SignatureProcessors.sha256Header()); signatureProcessors.add(SignatureProcessors.sha1Header()); signatureProcessors.add(SignatureProcessors.md5()); signatureProcessors.add(SignatureProcessors.payloadSize()); - + signatureProcessors.add(new RsaSignatureProcessor(privateKey)); return signatureProcessors; } - /** - *

- * Convert an array of bytes into a ByteBuffer - *

- * - * @param data : byte array to convert - * @return a {@link ByteBuffer} built with data - * @throws IOException - */ - private static ByteBuffer bufBytes(byte[] data) throws IOException { - ByteBuffer buf = ByteBuffer.allocate(data.length); - ReadableByteChannel headerChannel = Channels.newChannel(new ByteArrayInputStream(data)); - IOUtils.readFully(headerChannel, buf); - return buf; - } - - /** - *

- * Parse the byte[] to an RpmInputStream - *

- * - * @param buf : byte array representing the rpm file - * @return {@link RpmInputStream} - * @throws IOException - */ - private static RpmInputStream getRpmInputStream(byte[] buf) throws IOException { - try (RpmInputStream ref = new RpmInputStream(new ByteArrayInputStream(buf))) { - ref.available(); // init RpmInputStream - return ref; - } - } - /** *

* Decrypt and retrieve the private key diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java index 8bad6dc..15d9399 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java @@ -16,8 +16,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -31,6 +29,7 @@ import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRing; import org.eclipse.packager.rpm.RpmSignatureTag; import org.eclipse.packager.rpm.Rpms; +import org.eclipse.packager.rpm.app.Dumper; import org.eclipse.packager.rpm.parse.InputHeader; import org.eclipse.packager.rpm.parse.RpmInputStream; import org.junit.jupiter.api.AfterAll; @@ -55,60 +54,51 @@ public void testSigningExistingRpm() throws IOException, PGPException { if (!rpm.exists() || !private_key.exists()) { fail("Input files rpm or private_key does not exist"); } - try (InputStream rpmStream = new FileInputStream(rpm)) { - try (InputStream privateKeyStream = new FileInputStream(private_key)) { - // Sign the RPM - try (ByteArrayOutputStream signedPackage = RpmFileSignatureProcessor.perform(rpmStream, - privateKeyStream, passPhrase)) { - byte[] bytes = signedPackage.toByteArray(); + // Write the signed RPM + File resultDirectory = new File(RESULT_DIR); + resultDirectory.mkdir(); + File signedRpm = new File(RESULT_FILE_PATH); + signedRpm.createNewFile(); - // Write the signed RPM - File resultDirectory = new File(RESULT_DIR); - resultDirectory.mkdir(); - File signedRpm = new File(RESULT_FILE_PATH); - signedRpm.createNewFile(); - try (FileOutputStream resultOut = new FileOutputStream(signedRpm)) { - resultOut.write(bytes); - resultOut.close(); + try (FileOutputStream resultOut = new FileOutputStream(signedRpm)) { + // Sign the RPM + try (InputStream privateKeyStream = new FileInputStream(private_key)) { + RpmFileSignatureProcessor.perform(rpm, privateKeyStream, passPhrase, resultOut); - // Read the initial (non signed) rpm file - RpmInputStream initialRpm = new RpmInputStream(new FileInputStream(rpm)); - initialRpm.available(); - initialRpm.close(); - InputHeader initialHeader = initialRpm.getSignatureHeader(); - RpmInputStream rpmSigned = new RpmInputStream(new ByteArrayInputStream(bytes)); - rpmSigned.available(); - rpmSigned.close(); - InputHeader signedHeader = rpmSigned.getSignatureHeader(); + // Read the initial (non signed) rpm file + RpmInputStream initialRpm = new RpmInputStream(new FileInputStream(rpm)); + initialRpm.available(); + System.out.println("#########################################################################"); + Dumper.dumpAll(initialRpm); + initialRpm.close(); + System.out.println("#########################################################################"); + InputHeader initialHeader = initialRpm.getSignatureHeader(); + RpmInputStream rpmSigned = new RpmInputStream(new FileInputStream(signedRpm)); + rpmSigned.available(); + Dumper.dumpAll(rpmSigned); + rpmSigned.close(); + InputHeader signedHeader = rpmSigned.getSignatureHeader(); - // Get informations of the initial rpm file - int initialSize = (int) initialHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); - int initialPayloadSize = (int) initialHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get() - .getValue(); - String initialSha1 = initialHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue() - .toString(); - String initialMd5 = Rpms - .dumpValue(initialHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); + // Get informations of the initial rpm file + int initialSize = (int) initialHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); + int initialPayloadSize = (int) initialHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); + String initialSha1 = initialHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); + String initialMd5 = Rpms.dumpValue(initialHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); - // Read information of the signed rpm file - int signedSize = (int) signedHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); - int signedPayloadSize = (int) signedHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get() - .getValue(); - String signedSha1 = signedHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue() - .toString(); - String signedMd5 = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); - String pgpSignature = Rpms - .dumpValue(signedHeader.getEntry(RpmSignatureTag.PGP).get().getValue()); + // Read information of the signed rpm file + int signedSize = (int) signedHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); + int signedPayloadSize = (int) signedHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); + String signedSha1 = signedHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); + String signedMd5 = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); + String pgpSignature = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.PGP).get().getValue()); - // Compare informations values of initial rpm and signed rpm - assertEquals(initialSize, signedSize); - assertEquals(initialPayloadSize, signedPayloadSize); - assertEquals(initialSha1, signedSha1); - assertEquals(initialMd5, signedMd5); - // verify if signature is present - assertNotNull(pgpSignature); - } - } + // Compare informations values of initial rpm and signed rpm + assertEquals(initialSize, signedSize); + assertEquals(initialPayloadSize, signedPayloadSize); + assertEquals(initialSha1, signedSha1); + assertEquals(initialMd5, signedMd5); + // Verify if signature is present + assertNotNull(pgpSignature); } } } From deff6d61e862a78c14d84036ae541e53a3109342 Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Mon, 20 Mar 2023 19:28:15 +0100 Subject: [PATCH 14/17] close RpmInputStream after read informations --- .../signature/RpmFileSignatureProcessor.java | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java index b368418..d34ee1f 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -69,39 +69,51 @@ private RpmFileSignatureProcessor() { public static void perform(File rpm, InputStream privateKeyIn, String passphrase, OutputStream out) throws IOException, PGPException { + long signatureHeaderStart = 0L; + long signatureHeaderLength = 0L; + long payloadHeaderStart = 0L; + long payloadHeaderLength = 0L; + long payloadStart = 0L; + long archiveSize = 0L; + if (!rpm.exists()) { throw new IOException("The file " + rpm.getName() + " does not exist"); } + // Extract private key PGPPrivateKey privateKey = getPrivateKey(privateKeyIn, passphrase); + // Get the informations of the RPM try (RpmInputStream rpmIn = new RpmInputStream(new FileInputStream(rpm))) { - long signatureHeaderStart = rpmIn.getSignatureHeader().getStart(); - long signatureHeaderLength = rpmIn.getSignatureHeader().getLength(); - long payloadHeaderStart = rpmIn.getPayloadHeader().getStart(); - long payloadHeaderLength = rpmIn.getPayloadHeader().getLength(); + signatureHeaderStart = rpmIn.getSignatureHeader().getStart(); + signatureHeaderLength = rpmIn.getSignatureHeader().getLength(); + payloadHeaderStart = rpmIn.getPayloadHeader().getStart(); + payloadHeaderLength = rpmIn.getPayloadHeader().getLength(); RpmInformation info = RpmInformations.makeInformation(rpmIn); - long payloadStart = info.getHeaderEnd(); - long archiveSize = info.getArchiveSize(); + payloadStart = info.getHeaderEnd(); + archiveSize = info.getArchiveSize(); + } - if (signatureHeaderStart == 0L || signatureHeaderLength == 0L || payloadHeaderStart == 0L - || payloadHeaderLength == 0L || archiveSize == 0L) { - throw new IOException("Unable to read " + rpm.getName() + " informations."); - } + if (signatureHeaderStart == 0L || signatureHeaderLength == 0L || payloadHeaderStart == 0L + || payloadHeaderLength == 0L || payloadStart == 0L || archiveSize == 0L) { + throw new IOException("Unable to read " + rpm.getName() + " informations."); + } + + // Read the file parts for the signature (payload header + payload) + try (FileInputStream in = new FileInputStream(rpm)) { + FileChannel channelIn = in.getChannel(); + ByteBuffer payloadHeaderBuff = ByteBuffer.allocate((int) payloadHeaderLength); + channelIn.read(payloadHeaderBuff, payloadHeaderStart); + ByteBuffer payloadBuff = ByteBuffer.allocate((int) (channelIn.size() - payloadStart)); + channelIn.read(payloadBuff, payloadStart); - try (FileInputStream in = new FileInputStream(rpm)) { - FileChannel channelIn = in.getChannel(); - ByteBuffer payloadHeaderBuff = ByteBuffer.allocate((int) payloadHeaderLength); - channelIn.read(payloadHeaderBuff, payloadHeaderStart); - ByteBuffer payloadBuff = ByteBuffer.allocate((int) (channelIn.size() - payloadStart)); - channelIn.read(payloadBuff, payloadStart); - - try (WritableByteChannel channelOut = Channels.newChannel(out)) { - channelIn.transferTo(0, 96, channelOut); - writeSignature(privateKey, payloadHeaderBuff, payloadBuff, info.getArchiveSize(), channelOut); - channelIn.transferTo(payloadHeaderStart, payloadHeaderLength, channelOut); - channelIn.transferTo(payloadStart, channelIn.size() - payloadStart, channelOut); - } + // Write into out + try (WritableByteChannel channelOut = Channels.newChannel(out)) { + channelIn.transferTo(0, 96, channelOut); + //Generate and write signature + writeSignature(privateKey, payloadHeaderBuff, payloadBuff, archiveSize, channelOut); + channelIn.transferTo(payloadHeaderStart, payloadHeaderLength, channelOut); + channelIn.transferTo(payloadStart, channelIn.size() - payloadStart, channelOut); } } } From 63248e19209be5d16d1657e25d9bcf46b7a39ada Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Tue, 21 Mar 2023 15:30:19 +0100 Subject: [PATCH 15/17] check number of bytes read for each parts --- .../signature/RpmFileSignatureProcessor.java | 38 ++++++++-- .../RpmFileSignatureProcessorTest.java | 71 +++++++++---------- 2 files changed, 66 insertions(+), 43 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java index d34ee1f..ad01a58 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -75,6 +75,8 @@ public static void perform(File rpm, InputStream privateKeyIn, String passphrase long payloadHeaderLength = 0L; long payloadStart = 0L; long archiveSize = 0L; + long payloadSize = 0L; + long bytesRead = 0L; if (!rpm.exists()) { throw new IOException("The file " + rpm.getName() + " does not exist"); @@ -102,18 +104,24 @@ public static void perform(File rpm, InputStream privateKeyIn, String passphrase // Read the file parts for the signature (payload header + payload) try (FileInputStream in = new FileInputStream(rpm)) { FileChannel channelIn = in.getChannel(); + payloadSize = channelIn.size() - payloadStart; ByteBuffer payloadHeaderBuff = ByteBuffer.allocate((int) payloadHeaderLength); - channelIn.read(payloadHeaderBuff, payloadHeaderStart); - ByteBuffer payloadBuff = ByteBuffer.allocate((int) (channelIn.size() - payloadStart)); - channelIn.read(payloadBuff, payloadStart); + bytesRead = channelIn.read(payloadHeaderBuff, payloadHeaderStart); + checkBytes(bytesRead, payloadHeaderLength); + ByteBuffer payloadBuff = ByteBuffer.allocate((int) payloadSize); + bytesRead = channelIn.read(payloadBuff, payloadStart); + checkBytes(bytesRead, payloadSize); // Write into out try (WritableByteChannel channelOut = Channels.newChannel(out)) { - channelIn.transferTo(0, 96, channelOut); - //Generate and write signature + bytesRead = channelIn.transferTo(0, 96, channelOut); + checkBytes(bytesRead, 96); + // Generate and write signature writeSignature(privateKey, payloadHeaderBuff, payloadBuff, archiveSize, channelOut); - channelIn.transferTo(payloadHeaderStart, payloadHeaderLength, channelOut); - channelIn.transferTo(payloadStart, channelIn.size() - payloadStart, channelOut); + bytesRead = channelIn.transferTo(payloadHeaderStart, payloadHeaderLength, channelOut); + checkBytes(bytesRead, payloadHeaderLength); + bytesRead = channelIn.transferTo(payloadStart, payloadSize, channelOut); + checkBytes(bytesRead, payloadSize); } } } @@ -168,6 +176,22 @@ private static void safeWrite(ByteBuffer buf, WritableByteChannel out) throws IO } } + /** + *

+ * Check if the good number of bytes was read from the channel + *

+ * + * @param actual : number of bytes read from the channel + * @param expected: expected number + * @throws IOException if actual is different of the expected + */ + private static void checkBytes(long actual, long expected) throws IOException { + if (actual != expected) { + throw new IOException( + "The number of bytes read (" + actual + ") are differents of the attemp (" + expected + ")"); + } + } + /** *

* Return all {@link SignatureProcessor} required to perform signature diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java index 15d9399..9754901 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java @@ -54,52 +54,51 @@ public void testSigningExistingRpm() throws IOException, PGPException { if (!rpm.exists() || !private_key.exists()) { fail("Input files rpm or private_key does not exist"); } - // Write the signed RPM + // Init the signed RPM File resultDirectory = new File(RESULT_DIR); resultDirectory.mkdir(); File signedRpm = new File(RESULT_FILE_PATH); signedRpm.createNewFile(); - try (FileOutputStream resultOut = new FileOutputStream(signedRpm)) { + try (FileOutputStream resultOut = new FileOutputStream(signedRpm); + InputStream privateKeyStream = new FileInputStream(private_key)) { // Sign the RPM - try (InputStream privateKeyStream = new FileInputStream(private_key)) { - RpmFileSignatureProcessor.perform(rpm, privateKeyStream, passPhrase, resultOut); + RpmFileSignatureProcessor.perform(rpm, privateKeyStream, passPhrase, resultOut); - // Read the initial (non signed) rpm file - RpmInputStream initialRpm = new RpmInputStream(new FileInputStream(rpm)); - initialRpm.available(); - System.out.println("#########################################################################"); - Dumper.dumpAll(initialRpm); - initialRpm.close(); - System.out.println("#########################################################################"); - InputHeader initialHeader = initialRpm.getSignatureHeader(); - RpmInputStream rpmSigned = new RpmInputStream(new FileInputStream(signedRpm)); - rpmSigned.available(); - Dumper.dumpAll(rpmSigned); - rpmSigned.close(); - InputHeader signedHeader = rpmSigned.getSignatureHeader(); + // Read the initial (non signed) rpm file + RpmInputStream initialRpm = new RpmInputStream(new FileInputStream(rpm)); + initialRpm.available(); + System.out.println("#########################################################################"); + Dumper.dumpAll(initialRpm); + initialRpm.close(); + System.out.println("#########################################################################"); + InputHeader initialHeader = initialRpm.getSignatureHeader(); + RpmInputStream rpmSigned = new RpmInputStream(new FileInputStream(signedRpm)); + rpmSigned.available(); + Dumper.dumpAll(rpmSigned); + rpmSigned.close(); + InputHeader signedHeader = rpmSigned.getSignatureHeader(); - // Get informations of the initial rpm file - int initialSize = (int) initialHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); - int initialPayloadSize = (int) initialHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); - String initialSha1 = initialHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); - String initialMd5 = Rpms.dumpValue(initialHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); + // Get informations of the initial rpm file + int initialSize = (int) initialHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); + int initialPayloadSize = (int) initialHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); + String initialSha1 = initialHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); + String initialMd5 = Rpms.dumpValue(initialHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); - // Read information of the signed rpm file - int signedSize = (int) signedHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); - int signedPayloadSize = (int) signedHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); - String signedSha1 = signedHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); - String signedMd5 = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); - String pgpSignature = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.PGP).get().getValue()); + // Read information of the signed rpm file + int signedSize = (int) signedHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); + int signedPayloadSize = (int) signedHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); + String signedSha1 = signedHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); + String signedMd5 = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); + String pgpSignature = Rpms.dumpValue(signedHeader.getEntry(RpmSignatureTag.PGP).get().getValue()); - // Compare informations values of initial rpm and signed rpm - assertEquals(initialSize, signedSize); - assertEquals(initialPayloadSize, signedPayloadSize); - assertEquals(initialSha1, signedSha1); - assertEquals(initialMd5, signedMd5); - // Verify if signature is present - assertNotNull(pgpSignature); - } + // Compare informations values of initial rpm and signed rpm + assertEquals(initialSize, signedSize); + assertEquals(initialPayloadSize, signedPayloadSize); + assertEquals(initialSha1, signedSha1); + assertEquals(initialMd5, signedMd5); + // Verify if signature is present + assertNotNull(pgpSignature); } } From a52f461c8dcbae647ee285bdb7f6cb667c743dee Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Wed, 22 Mar 2023 12:07:01 +0100 Subject: [PATCH 16/17] rework with commons-compress --- .../signature/RpmFileSignatureProcessor.java | 82 ++++++++----------- .../RpmFileSignatureProcessorTest.java | 2 + 2 files changed, 36 insertions(+), 48 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java index ad01a58..b53b577 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -12,18 +12,18 @@ ********************************************************************************/ package org.eclipse.packager.rpm.signature; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.nio.channels.Channels; import java.nio.channels.FileChannel; -import java.nio.channels.WritableByteChannel; import java.util.ArrayList; import java.util.List; +import org.apache.commons.compress.utils.IOUtils; import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPrivateKey; @@ -69,6 +69,7 @@ private RpmFileSignatureProcessor() { public static void perform(File rpm, InputStream privateKeyIn, String passphrase, OutputStream out) throws IOException, PGPException { + final long leadLength = 96; long signatureHeaderStart = 0L; long signatureHeaderLength = 0L; long payloadHeaderStart = 0L; @@ -76,7 +77,7 @@ public static void perform(File rpm, InputStream privateKeyIn, String passphrase long payloadStart = 0L; long archiveSize = 0L; long payloadSize = 0L; - long bytesRead = 0L; + byte[] signatureHeader; if (!rpm.exists()) { throw new IOException("The file " + rpm.getName() + " does not exist"); @@ -101,35 +102,30 @@ public static void perform(File rpm, InputStream privateKeyIn, String passphrase throw new IOException("Unable to read " + rpm.getName() + " informations."); } - // Read the file parts for the signature (payload header + payload) + // Build the signature header by digest payload header + payload try (FileInputStream in = new FileInputStream(rpm)) { FileChannel channelIn = in.getChannel(); payloadSize = channelIn.size() - payloadStart; + channelIn.position(leadLength + signatureHeaderLength); ByteBuffer payloadHeaderBuff = ByteBuffer.allocate((int) payloadHeaderLength); - bytesRead = channelIn.read(payloadHeaderBuff, payloadHeaderStart); - checkBytes(bytesRead, payloadHeaderLength); + IOUtils.readFully(channelIn, payloadHeaderBuff); ByteBuffer payloadBuff = ByteBuffer.allocate((int) payloadSize); - bytesRead = channelIn.read(payloadBuff, payloadStart); - checkBytes(bytesRead, payloadSize); - - // Write into out - try (WritableByteChannel channelOut = Channels.newChannel(out)) { - bytesRead = channelIn.transferTo(0, 96, channelOut); - checkBytes(bytesRead, 96); - // Generate and write signature - writeSignature(privateKey, payloadHeaderBuff, payloadBuff, archiveSize, channelOut); - bytesRead = channelIn.transferTo(payloadHeaderStart, payloadHeaderLength, channelOut); - checkBytes(bytesRead, payloadHeaderLength); - bytesRead = channelIn.transferTo(payloadStart, payloadSize, channelOut); - checkBytes(bytesRead, payloadSize); - } + IOUtils.readFully(channelIn, payloadBuff); + signatureHeader = getSignature(privateKey, payloadHeaderBuff, payloadBuff, archiveSize); + } + + // Write to the OutputStream + try (FileInputStream in = new FileInputStream(rpm)) { + IOUtils.copyRange(in, leadLength, out); + IOUtils.skip(in, signatureHeaderLength); + out.write(signatureHeader); + IOUtils.copy(in, out); } } /** *

- * Sign the payload with its header with the given private key and write it in - * channelOut, see https://rpm-software-management.github.io/rpm/manual/format.html *

* @@ -137,11 +133,11 @@ public static void perform(File rpm, InputStream privateKeyIn, String passphrase * @param payloadHeader : Payload's header as {@link ByteBuffer} * @param payload : Payload as {@link ByteBuffer} * @param archiveSize : archiveSize retrieved in {@link RpmInformation} - * @param channelOut : output to write to + * @return the signature header as a bytes array * @throws IOException */ - private static void writeSignature(PGPPrivateKey privateKey, ByteBuffer payloadHeader, ByteBuffer payload, - long archiveSize, WritableByteChannel channelOut) throws IOException { + private static byte[] getSignature(PGPPrivateKey privateKey, ByteBuffer payloadHeader, ByteBuffer payload, + long archiveSize) throws IOException { Header signatureHeader = new Header<>(); List signatureProcessors = getSignatureProcessors(privateKey); payloadHeader.flip(); @@ -155,41 +151,31 @@ private static void writeSignature(PGPPrivateKey privateKey, ByteBuffer payloadH ByteBuffer signatureBuf = Headers.render(signatureHeader.makeEntries(), true, Rpms.IMMUTABLE_TAG_SIGNATURE); final int payloadSize = signatureBuf.remaining(); final int padding = Rpms.padding(payloadSize); - safeWrite(signatureBuf, channelOut); + byte[] signature = safeReadBuffer(signatureBuf); + ByteArrayOutputStream result = new ByteArrayOutputStream(); + result.write(signature); if (padding > 0) { - safeWrite(ByteBuffer.wrap(Rpms.EMPTY_128, 0, padding), channelOut); + result.write(safeReadBuffer(ByteBuffer.wrap(Rpms.EMPTY_128, 0, padding))); } + return result.toByteArray(); } /** *

- * Safe write (without buffer bytes) the given buffer into the channel array + * Safe read (without buffer bytes) the given buffer and return it as a byte + * array *

* - * @param buf : the {@link ByteBuffer} to write - * @param out : the {@link WritableByteChannel} output + * @param buf : the {@link ByteBuffer} to read + * @return a bytes array * @throws IOException */ - private static void safeWrite(ByteBuffer buf, WritableByteChannel out) throws IOException { + private static byte[] safeReadBuffer(ByteBuffer buf) throws IOException { + ByteArrayOutputStream result = new ByteArrayOutputStream(); while (buf.hasRemaining()) { - out.write(buf); - } - } - - /** - *

- * Check if the good number of bytes was read from the channel - *

- * - * @param actual : number of bytes read from the channel - * @param expected: expected number - * @throws IOException if actual is different of the expected - */ - private static void checkBytes(long actual, long expected) throws IOException { - if (actual != expected) { - throw new IOException( - "The number of bytes read (" + actual + ") are differents of the attemp (" + expected + ")"); + result.write(buf.get()); } + return result.toByteArray(); } /** diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java index 9754901..a20eee8 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java @@ -33,6 +33,7 @@ import org.eclipse.packager.rpm.parse.InputHeader; import org.eclipse.packager.rpm.parse.RpmInputStream; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -104,6 +105,7 @@ public void testSigningExistingRpm() throws IOException, PGPException { @Test @Order(2) + @Disabled public void verifyRpmSignature() throws IOException, PGPException { File public_key = new File("src/test/resources/key/public_key.txt"); File signedRpm = new File(RESULT_FILE_PATH); From 019a1101796d7e24f4aaab93ee750e89f4dc05ac Mon Sep 17 00:00:00 2001 From: Mathieu Delrocq Date: Wed, 22 Mar 2023 17:40:35 +0100 Subject: [PATCH 17/17] fix javadoc + format test --- .../signature/RpmFileSignatureProcessor.java | 2 +- .../RpmFileSignatureProcessorTest.java | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java index b53b577..130f05b 100644 --- a/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java +++ b/rpm/src/main/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessor.java @@ -43,7 +43,7 @@ /** * * Sign existing RPM file by calling - * {@link #perform(InputStream, InputStream, String)} + * {@link #perform(File, InputStream, String, OutputStream)} * * */ diff --git a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java index a20eee8..f894dc7 100644 --- a/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java +++ b/rpm/src/test/java/org/eclipse/packager/rpm/signature/RpmFileSignatureProcessorTest.java @@ -29,7 +29,6 @@ import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRing; import org.eclipse.packager.rpm.RpmSignatureTag; import org.eclipse.packager.rpm.Rpms; -import org.eclipse.packager.rpm.app.Dumper; import org.eclipse.packager.rpm.parse.InputHeader; import org.eclipse.packager.rpm.parse.RpmInputStream; import org.junit.jupiter.api.AfterAll; @@ -42,6 +41,9 @@ @TestMethodOrder(OrderAnnotation.class) public class RpmFileSignatureProcessorTest { + private static final String SOURCE_FILE_PATH = "src/test/resources/data/org.eclipse.scada-0.2.1-1.noarch.rpm"; + private static final String PRIVATE_KEY_PATH = "src/test/resources/key/private_key.txt"; + private static final String PUBLIC_KEY_PATH = "src/test/resources/key/public_key.txt"; private static final String RESULT_FILE_PATH = "src/test/resources/result/org.eclipse.scada-0.2.1-1.noarch.rpm"; private static final String RESULT_DIR = "src/test/resources/result"; @@ -49,9 +51,9 @@ public class RpmFileSignatureProcessorTest { @Order(1) public void testSigningExistingRpm() throws IOException, PGPException { // Read files - String passPhrase = "testkey"; - File rpm = new File("src/test/resources/data/org.eclipse.scada-0.2.1-1.noarch.rpm"); - File private_key = new File("src/test/resources/key/private_key.txt"); + final String passPhrase = "testkey"; // Do not change + File rpm = new File(SOURCE_FILE_PATH); + File private_key = new File(PRIVATE_KEY_PATH); if (!rpm.exists() || !private_key.exists()) { fail("Input files rpm or private_key does not exist"); } @@ -69,14 +71,12 @@ public void testSigningExistingRpm() throws IOException, PGPException { // Read the initial (non signed) rpm file RpmInputStream initialRpm = new RpmInputStream(new FileInputStream(rpm)); initialRpm.available(); - System.out.println("#########################################################################"); - Dumper.dumpAll(initialRpm); initialRpm.close(); - System.out.println("#########################################################################"); InputHeader initialHeader = initialRpm.getSignatureHeader(); + + // Read the signed rpm file RpmInputStream rpmSigned = new RpmInputStream(new FileInputStream(signedRpm)); rpmSigned.available(); - Dumper.dumpAll(rpmSigned); rpmSigned.close(); InputHeader signedHeader = rpmSigned.getSignatureHeader(); @@ -86,7 +86,7 @@ public void testSigningExistingRpm() throws IOException, PGPException { String initialSha1 = initialHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); String initialMd5 = Rpms.dumpValue(initialHeader.getEntry(RpmSignatureTag.MD5).get().getValue()); - // Read information of the signed rpm file + // Get informations of the signed rpm file int signedSize = (int) signedHeader.getEntry(RpmSignatureTag.SIZE).get().getValue(); int signedPayloadSize = (int) signedHeader.getEntry(RpmSignatureTag.PAYLOAD_SIZE).get().getValue(); String signedSha1 = signedHeader.getEntry(RpmSignatureTag.SHA1HEADER).get().getValue().toString(); @@ -98,6 +98,7 @@ public void testSigningExistingRpm() throws IOException, PGPException { assertEquals(initialPayloadSize, signedPayloadSize); assertEquals(initialSha1, signedSha1); assertEquals(initialMd5, signedMd5); + // Verify if signature is present assertNotNull(pgpSignature); } @@ -107,7 +108,7 @@ public void testSigningExistingRpm() throws IOException, PGPException { @Order(2) @Disabled public void verifyRpmSignature() throws IOException, PGPException { - File public_key = new File("src/test/resources/key/public_key.txt"); + File public_key = new File(PUBLIC_KEY_PATH); File signedRpm = new File(RESULT_FILE_PATH); if (!public_key.exists() || !signedRpm.exists()) { fail("Input files signedRpm or public_key does not exist");