mirror of
https://github.com/google/nomulus.git
synced 2025-05-14 16:37:13 +02:00
Simplify the RyDE API
Second step of RDE encoding refactoring. Creates a single OutputStream encode RyDE files. This replaces the 5 OutputStreams that were needed before. Also removes all the factories that were injected. It's an encoding, there's no point in injecting it. Finally, removed the buffer-size configuration and replaced with a static final const value in each individual OutputStream. This doesn't yet include a decoder (InputStream). And there's still a lot of overlap between the Ryde and the Ghostryde code. Both of those are left for the next CLs. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=204898369
This commit is contained in:
parent
c4a2b5fa8d
commit
8ec2eaf39c
15 changed files with 215 additions and 345 deletions
|
@ -672,20 +672,6 @@ public final class RegistryConfig {
|
||||||
return config.rde.reportUrlPrefix;
|
return config.rde.reportUrlPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Size of RYDE generator buffer in bytes for each of the five layers.
|
|
||||||
*
|
|
||||||
* @see google.registry.rde.RydePgpCompressionOutputStream
|
|
||||||
* @see google.registry.rde.RydePgpFileOutputStream
|
|
||||||
* @see google.registry.rde.RydePgpSigningOutputStream
|
|
||||||
* @see google.registry.rde.RydeTarOutputStream
|
|
||||||
*/
|
|
||||||
@Provides
|
|
||||||
@Config("rdeRydeBufferSize")
|
|
||||||
public static Integer provideRdeRydeBufferSize() {
|
|
||||||
return 64 * 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum amount of time generating an escrow deposit for a TLD could take, before killing.
|
* Maximum amount of time generating an escrow deposit for a TLD could take, before killing.
|
||||||
*
|
*
|
||||||
|
|
|
@ -64,11 +64,6 @@ public final class BrdaCopyAction implements Runnable {
|
||||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||||
|
|
||||||
@Inject GcsUtils gcsUtils;
|
@Inject GcsUtils gcsUtils;
|
||||||
@Inject RydePgpCompressionOutputStreamFactory pgpCompressionFactory;
|
|
||||||
@Inject RydePgpFileOutputStreamFactory pgpFileFactory;
|
|
||||||
@Inject RydePgpEncryptionOutputStreamFactory pgpEncryptionFactory;
|
|
||||||
@Inject RydePgpSigningOutputStreamFactory pgpSigningFactory;
|
|
||||||
@Inject RydeTarOutputStreamFactory tarFactory;
|
|
||||||
@Inject @Config("brdaBucket") String brdaBucket;
|
@Inject @Config("brdaBucket") String brdaBucket;
|
||||||
@Inject @Config("rdeBucket") String stagingBucket;
|
@Inject @Config("rdeBucket") String stagingBucket;
|
||||||
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
|
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
|
||||||
|
@ -96,25 +91,17 @@ public final class BrdaCopyAction implements Runnable {
|
||||||
|
|
||||||
long xmlLength = readXmlLength(xmlLengthFilename);
|
long xmlLength = readXmlLength(xmlLengthFilename);
|
||||||
|
|
||||||
logger.atInfo().log("Writing %s", rydeFile);
|
logger.atInfo().log("Writing %s and %s", rydeFile, sigFile);
|
||||||
byte[] signature;
|
|
||||||
try (InputStream gcsInput = gcsUtils.openInputStream(xmlFilename);
|
try (InputStream gcsInput = gcsUtils.openInputStream(xmlFilename);
|
||||||
InputStream ghostrydeDecoder = Ghostryde.decoder(gcsInput, stagingDecryptionKey);
|
InputStream ghostrydeDecoder = Ghostryde.decoder(gcsInput, stagingDecryptionKey);
|
||||||
OutputStream gcsOutput = gcsUtils.openOutputStream(rydeFile);
|
OutputStream rydeOut = gcsUtils.openOutputStream(rydeFile);
|
||||||
RydePgpSigningOutputStream signLayer = pgpSigningFactory.create(gcsOutput, signingKey)) {
|
OutputStream sigOut = gcsUtils.openOutputStream(sigFile);
|
||||||
try (OutputStream encryptLayer = pgpEncryptionFactory.create(signLayer, receiverKey);
|
RydeEncoder rydeEncoder = new RydeEncoder.Builder()
|
||||||
OutputStream compressLayer = pgpCompressionFactory.create(encryptLayer);
|
.setRydeOutput(rydeOut, receiverKey)
|
||||||
OutputStream fileLayer = pgpFileFactory.create(compressLayer, watermark, prefix + ".tar");
|
.setSignatureOutput(sigOut, signingKey)
|
||||||
OutputStream tarLayer =
|
.setFileMetadata(prefix, xmlLength, watermark)
|
||||||
tarFactory.create(fileLayer, xmlLength, watermark, prefix + ".xml")) {
|
.build()) {
|
||||||
ByteStreams.copy(ghostrydeDecoder, tarLayer);
|
ByteStreams.copy(ghostrydeDecoder, rydeEncoder);
|
||||||
}
|
|
||||||
signature = signLayer.getSignature();
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.atInfo().log("Writing %s", sigFile);
|
|
||||||
try (OutputStream gcsOutput = gcsUtils.openOutputStream(sigFile)) {
|
|
||||||
gcsOutput.write(signature);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ import google.registry.util.Retrier;
|
||||||
import google.registry.util.TaskQueueUtils;
|
import google.registry.util.TaskQueueUtils;
|
||||||
import google.registry.util.TeeOutputStream;
|
import google.registry.util.TeeOutputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -103,11 +104,6 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||||
|
|
||||||
@Inject JSchSshSessionFactory jschSshSessionFactory;
|
@Inject JSchSshSessionFactory jschSshSessionFactory;
|
||||||
@Inject Response response;
|
@Inject Response response;
|
||||||
@Inject RydePgpCompressionOutputStreamFactory pgpCompressionFactory;
|
|
||||||
@Inject RydePgpEncryptionOutputStreamFactory pgpEncryptionFactory;
|
|
||||||
@Inject RydePgpFileOutputStreamFactory pgpFileFactory;
|
|
||||||
@Inject RydePgpSigningOutputStreamFactory pgpSigningFactory;
|
|
||||||
@Inject RydeTarOutputStreamFactory tarFactory;
|
|
||||||
@Inject SftpProgressMonitor sftpProgressMonitor;
|
@Inject SftpProgressMonitor sftpProgressMonitor;
|
||||||
@Inject TaskQueueUtils taskQueueUtils;
|
@Inject TaskQueueUtils taskQueueUtils;
|
||||||
@Inject Retrier retrier;
|
@Inject Retrier retrier;
|
||||||
|
@ -214,28 +210,27 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||||
InputStream ghostrydeDecoder = Ghostryde.decoder(gcsInput, stagingDecryptionKey)) {
|
InputStream ghostrydeDecoder = Ghostryde.decoder(gcsInput, stagingDecryptionKey)) {
|
||||||
try (JSchSshSession session = jschSshSessionFactory.create(lazyJsch.get(), uploadUrl);
|
try (JSchSshSession session = jschSshSessionFactory.create(lazyJsch.get(), uploadUrl);
|
||||||
JSchSftpChannel ftpChan = session.openSftpChannel()) {
|
JSchSftpChannel ftpChan = session.openSftpChannel()) {
|
||||||
byte[] signature;
|
ByteArrayOutputStream sigOut = new ByteArrayOutputStream();
|
||||||
String rydeFilename = name + ".ryde";
|
String rydeFilename = name + ".ryde";
|
||||||
GcsFilename rydeGcsFilename = new GcsFilename(bucket, rydeFilename);
|
GcsFilename rydeGcsFilename = new GcsFilename(bucket, rydeFilename);
|
||||||
try (OutputStream ftpOutput =
|
try (OutputStream ftpOutput =
|
||||||
ftpChan.get().put(rydeFilename, sftpProgressMonitor, OVERWRITE);
|
ftpChan.get().put(rydeFilename, sftpProgressMonitor, OVERWRITE);
|
||||||
OutputStream gcsOutput = gcsUtils.openOutputStream(rydeGcsFilename);
|
OutputStream gcsOutput = gcsUtils.openOutputStream(rydeGcsFilename);
|
||||||
TeeOutputStream teeOutput = new TeeOutputStream(asList(ftpOutput, gcsOutput));
|
TeeOutputStream teeOutput = new TeeOutputStream(asList(ftpOutput, gcsOutput));
|
||||||
RydePgpSigningOutputStream signer = pgpSigningFactory.create(teeOutput, signingKey)) {
|
RydeEncoder rydeEncoder =
|
||||||
try (OutputStream encryptLayer = pgpEncryptionFactory.create(signer, receiverKey);
|
new RydeEncoder.Builder()
|
||||||
OutputStream kompressor = pgpCompressionFactory.create(encryptLayer);
|
.setRydeOutput(teeOutput, receiverKey)
|
||||||
OutputStream fileLayer = pgpFileFactory.create(kompressor, watermark, name + ".tar");
|
.setSignatureOutput(sigOut, signingKey)
|
||||||
OutputStream tarLayer =
|
.setFileMetadata(name, xmlLength, watermark)
|
||||||
tarFactory.create(fileLayer, xmlLength, watermark, name + ".xml")) {
|
.build()) {
|
||||||
ByteStreams.copy(ghostrydeDecoder, tarLayer);
|
long bytesCopied = ByteStreams.copy(ghostrydeDecoder, rydeEncoder);
|
||||||
}
|
logger.atInfo().log("uploaded %,d bytes: %s", bytesCopied, rydeFilename);
|
||||||
signature = signer.getSignature();
|
|
||||||
logger.atInfo().log("uploaded %,d bytes: %s.ryde", signer.getBytesWritten(), name);
|
|
||||||
}
|
}
|
||||||
String sigFilename = name + ".sig";
|
String sigFilename = name + ".sig";
|
||||||
|
byte[] signature = sigOut.toByteArray();
|
||||||
gcsUtils.createFromBytes(new GcsFilename(bucket, sigFilename), signature);
|
gcsUtils.createFromBytes(new GcsFilename(bucket, sigFilename), signature);
|
||||||
ftpChan.get().put(new ByteArrayInputStream(signature), sigFilename);
|
ftpChan.get().put(new ByteArrayInputStream(signature), sigFilename);
|
||||||
logger.atInfo().log("uploaded %,d bytes: %s.sig", signature.length, name);
|
logger.atInfo().log("uploaded %,d bytes: %s", signature.length, sigFilename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
153
java/google/registry/rde/RydeEncoder.java
Normal file
153
java/google/registry/rde/RydeEncoder.java
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// Copyright 2018 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.rde;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.io.Closer;
|
||||||
|
import java.io.FilterOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream that performs the full RyDE encryption.
|
||||||
|
*
|
||||||
|
* <p>The RyDE format has 2 files:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>the "data" file, encoded in data -> tar -> PgpFile -> compression -> encryption
|
||||||
|
* <li>the signature of the resulting file
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Hence, the encoder needs to receive 2 OutputStreams - one for the data and one for the
|
||||||
|
* signature.
|
||||||
|
*
|
||||||
|
* <p>Because of the external tar file encoding - the encoder must know the total length of the data
|
||||||
|
* from the start. This is a bit annoying, but necessary.
|
||||||
|
*/
|
||||||
|
@NotThreadSafe
|
||||||
|
public final class RydeEncoder extends FilterOutputStream {
|
||||||
|
|
||||||
|
private final OutputStream sigOutput;
|
||||||
|
private final RydePgpSigningOutputStream signer;
|
||||||
|
private final OutputStream encryptLayer;
|
||||||
|
private final OutputStream kompressor;
|
||||||
|
private final OutputStream fileLayer;
|
||||||
|
private final OutputStream tarLayer;
|
||||||
|
// We use a Closer to handle the stream .close, to make sure it's done correctly.
|
||||||
|
private final Closer closer = Closer.create();
|
||||||
|
private boolean isClosed = false;
|
||||||
|
|
||||||
|
private RydeEncoder(
|
||||||
|
OutputStream rydeOutput,
|
||||||
|
OutputStream sigOutput,
|
||||||
|
long dataLength,
|
||||||
|
String filenamePrefix,
|
||||||
|
DateTime modified,
|
||||||
|
PGPKeyPair signingKey,
|
||||||
|
Collection<PGPPublicKey> receiverKeys) {
|
||||||
|
super(null);
|
||||||
|
this.sigOutput = sigOutput;
|
||||||
|
signer = closer.register(new RydePgpSigningOutputStream(checkNotNull(rydeOutput), signingKey));
|
||||||
|
encryptLayer = closer.register(new RydePgpEncryptionOutputStream(signer, receiverKeys));
|
||||||
|
kompressor = closer.register(new RydePgpCompressionOutputStream(encryptLayer));
|
||||||
|
fileLayer =
|
||||||
|
closer.register(new RydePgpFileOutputStream(kompressor, modified, filenamePrefix + ".tar"));
|
||||||
|
tarLayer =
|
||||||
|
closer.register(
|
||||||
|
new RydeTarOutputStream(fileLayer, dataLength, modified, filenamePrefix + ".xml"));
|
||||||
|
this.out = tarLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call the underlying 3 input write.
|
||||||
|
*
|
||||||
|
* <p>FilterInputStream implements the 3 input write using a for loop over the single-byte write.
|
||||||
|
* For efficiency reasons, we want it to use the 3 input write instead.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
out.write(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (isClosed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Close all the streams we opened
|
||||||
|
closer.close();
|
||||||
|
isClosed = true;
|
||||||
|
try {
|
||||||
|
sigOutput.write(signer.getSignature());
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new RuntimeException("Failed to generate signature", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builder for {@link RydeEncoder}. */
|
||||||
|
public static class Builder {
|
||||||
|
OutputStream rydeOutput;
|
||||||
|
OutputStream sigOutput;
|
||||||
|
Long dataLength;
|
||||||
|
String filenamePrefix;
|
||||||
|
DateTime modified;
|
||||||
|
PGPKeyPair signingKey;
|
||||||
|
ImmutableList<PGPPublicKey> receiverKeys;
|
||||||
|
|
||||||
|
/** Sets the OutputStream for the Ryde-encoded data, and the keys used for the encryption. */
|
||||||
|
public Builder setRydeOutput(
|
||||||
|
OutputStream rydeOutput, PGPPublicKey receiverKey, PGPPublicKey... moreReceiverKeys) {
|
||||||
|
this.rydeOutput = rydeOutput;
|
||||||
|
this.receiverKeys =
|
||||||
|
new ImmutableList.Builder<PGPPublicKey>().add(receiverKey).add(moreReceiverKeys).build();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the OutputStream for the signature, and the key used to sign. */
|
||||||
|
public Builder setSignatureOutput(OutputStream sigOutput, PGPKeyPair signingKey) {
|
||||||
|
this.sigOutput = sigOutput;
|
||||||
|
this.signingKey = signingKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the information about the unencoded data that will follow. */
|
||||||
|
public Builder setFileMetadata(String filenamePrefix, long dataLength, DateTime modified) {
|
||||||
|
this.filenamePrefix = filenamePrefix;
|
||||||
|
this.dataLength = dataLength;
|
||||||
|
this.modified = modified;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the built {@link RydeEncoder}. */
|
||||||
|
public RydeEncoder build() {
|
||||||
|
return new RydeEncoder(
|
||||||
|
checkNotNull(rydeOutput, "Must call 'setRydeOutput'"),
|
||||||
|
checkNotNull(sigOutput, "Must call 'setSignatureOutput'"),
|
||||||
|
checkNotNull(dataLength, "Must call 'setFileMetadata'"),
|
||||||
|
checkNotNull(filenamePrefix, "Must call 'setFileMetadata'"),
|
||||||
|
checkNotNull(modified, "Must call 'setFileMetadata'"),
|
||||||
|
checkNotNull(signingKey, "Must call 'setSignatureOutput'"),
|
||||||
|
checkNotNull(receiverKeys, "Must call 'setRydeOutput'"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,9 +16,6 @@ package google.registry.rde;
|
||||||
|
|
||||||
import static org.bouncycastle.bcpg.CompressionAlgorithmTags.ZIP;
|
import static org.bouncycastle.bcpg.CompressionAlgorithmTags.ZIP;
|
||||||
|
|
||||||
import com.google.auto.factory.AutoFactory;
|
|
||||||
import com.google.auto.factory.Provided;
|
|
||||||
import google.registry.config.RegistryConfig.Config;
|
|
||||||
import google.registry.util.ImprovedOutputStream;
|
import google.registry.util.ImprovedOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -31,9 +28,10 @@ import org.bouncycastle.openpgp.PGPException;
|
||||||
*
|
*
|
||||||
* <p>This uses the ZIP compression algorithm per the ICANN escrow specification.
|
* <p>This uses the ZIP compression algorithm per the ICANN escrow specification.
|
||||||
*/
|
*/
|
||||||
@AutoFactory(allowSubclasses = true)
|
|
||||||
public class RydePgpCompressionOutputStream extends ImprovedOutputStream {
|
public class RydePgpCompressionOutputStream extends ImprovedOutputStream {
|
||||||
|
|
||||||
|
private static final int BUFFER_SIZE = 64 * 1024;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance that compresses data.
|
* Creates a new instance that compresses data.
|
||||||
*
|
*
|
||||||
|
@ -41,9 +39,8 @@ public class RydePgpCompressionOutputStream extends ImprovedOutputStream {
|
||||||
* @throws RuntimeException to rethrow {@link PGPException} and {@link IOException}
|
* @throws RuntimeException to rethrow {@link PGPException} and {@link IOException}
|
||||||
*/
|
*/
|
||||||
public RydePgpCompressionOutputStream(
|
public RydePgpCompressionOutputStream(
|
||||||
@Provided @Config("rdeRydeBufferSize") Integer bufferSize,
|
|
||||||
@WillNotClose OutputStream os) {
|
@WillNotClose OutputStream os) {
|
||||||
super("RydePgpCompressionOutputStream", createDelegate(bufferSize, os));
|
super("RydePgpCompressionOutputStream", createDelegate(BUFFER_SIZE, os));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OutputStream createDelegate(int bufferSize, OutputStream os) {
|
private static OutputStream createDelegate(int bufferSize, OutputStream os) {
|
||||||
|
|
|
@ -14,18 +14,17 @@
|
||||||
|
|
||||||
package google.registry.rde;
|
package google.registry.rde;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags.AES_128;
|
import static org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags.AES_128;
|
||||||
import static org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
|
import static org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
|
||||||
|
|
||||||
import com.google.auto.factory.AutoFactory;
|
|
||||||
import com.google.auto.factory.Provided;
|
|
||||||
import google.registry.config.RegistryConfig.Config;
|
|
||||||
import google.registry.util.ImprovedOutputStream;
|
import google.registry.util.ImprovedOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.ProviderException;
|
import java.security.ProviderException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Collection;
|
||||||
import javax.annotation.WillNotClose;
|
import javax.annotation.WillNotClose;
|
||||||
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
|
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
@ -53,9 +52,10 @@ import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodG
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc4880">RFC 4880 (OpenPGP Message Format)</a>
|
* @see <a href="http://tools.ietf.org/html/rfc4880">RFC 4880 (OpenPGP Message Format)</a>
|
||||||
* @see <a href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES (Wikipedia)</a>
|
* @see <a href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES (Wikipedia)</a>
|
||||||
*/
|
*/
|
||||||
@AutoFactory(allowSubclasses = true)
|
|
||||||
public class RydePgpEncryptionOutputStream extends ImprovedOutputStream {
|
public class RydePgpEncryptionOutputStream extends ImprovedOutputStream {
|
||||||
|
|
||||||
|
private static final int BUFFER_SIZE = 64 * 1024;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The symmetric encryption algorithm to use. Do not change this value without checking the
|
* The symmetric encryption algorithm to use. Do not change this value without checking the
|
||||||
* RFCs to make sure the encryption algorithm and strength combination is allowed.
|
* RFCs to make sure the encryption algorithm and strength combination is allowed.
|
||||||
|
@ -92,22 +92,23 @@ public class RydePgpEncryptionOutputStream extends ImprovedOutputStream {
|
||||||
* @throws RuntimeException to rethrow {@link PGPException} and {@link IOException}
|
* @throws RuntimeException to rethrow {@link PGPException} and {@link IOException}
|
||||||
*/
|
*/
|
||||||
public RydePgpEncryptionOutputStream(
|
public RydePgpEncryptionOutputStream(
|
||||||
@Provided @Config("rdeRydeBufferSize") Integer bufferSize,
|
|
||||||
@WillNotClose OutputStream os,
|
@WillNotClose OutputStream os,
|
||||||
PGPPublicKey receiverKey) {
|
Collection<PGPPublicKey> receiverKeys) {
|
||||||
super("RydePgpEncryptionOutputStream", createDelegate(bufferSize, os, receiverKey));
|
super("RydePgpEncryptionOutputStream", createDelegate(os, receiverKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static
|
private static OutputStream createDelegate(
|
||||||
OutputStream createDelegate(int bufferSize, OutputStream os, PGPPublicKey receiverKey) {
|
OutputStream os, Collection<PGPPublicKey> receiverKeys) {
|
||||||
try {
|
try {
|
||||||
PGPEncryptedDataGenerator encryptor = new PGPEncryptedDataGenerator(
|
PGPEncryptedDataGenerator encryptor = new PGPEncryptedDataGenerator(
|
||||||
new JcePGPDataEncryptorBuilder(CIPHER)
|
new JcePGPDataEncryptorBuilder(CIPHER)
|
||||||
.setWithIntegrityPacket(USE_INTEGRITY_PACKET)
|
.setWithIntegrityPacket(USE_INTEGRITY_PACKET)
|
||||||
.setSecureRandom(SecureRandom.getInstance(RANDOM_SOURCE))
|
.setSecureRandom(SecureRandom.getInstance(RANDOM_SOURCE))
|
||||||
.setProvider(PROVIDER_NAME));
|
.setProvider(PROVIDER_NAME));
|
||||||
encryptor.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(receiverKey));
|
checkArgument(!receiverKeys.isEmpty(), "Must give at least one receiver key");
|
||||||
return encryptor.open(os, new byte[bufferSize]);
|
receiverKeys.forEach(
|
||||||
|
key -> encryptor.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(key)));
|
||||||
|
return encryptor.open(os, new byte[BUFFER_SIZE]);
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
throw new ProviderException(e);
|
throw new ProviderException(e);
|
||||||
} catch (IOException | PGPException e) {
|
} catch (IOException | PGPException e) {
|
||||||
|
|
|
@ -17,9 +17,6 @@ package google.registry.rde;
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static org.bouncycastle.openpgp.PGPLiteralData.BINARY;
|
import static org.bouncycastle.openpgp.PGPLiteralData.BINARY;
|
||||||
|
|
||||||
import com.google.auto.factory.AutoFactory;
|
|
||||||
import com.google.auto.factory.Provided;
|
|
||||||
import google.registry.config.RegistryConfig.Config;
|
|
||||||
import google.registry.util.ImprovedOutputStream;
|
import google.registry.util.ImprovedOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -36,9 +33,10 @@ import org.joda.time.DateTime;
|
||||||
*
|
*
|
||||||
* <p>According to escrow spec, the PGP message should contain a single tar file.
|
* <p>According to escrow spec, the PGP message should contain a single tar file.
|
||||||
*/
|
*/
|
||||||
@AutoFactory(allowSubclasses = true)
|
|
||||||
public class RydePgpFileOutputStream extends ImprovedOutputStream {
|
public class RydePgpFileOutputStream extends ImprovedOutputStream {
|
||||||
|
|
||||||
|
private static final int BUFFER_SIZE = 64 * 1024;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance for a particular file.
|
* Creates a new instance for a particular file.
|
||||||
*
|
*
|
||||||
|
@ -47,20 +45,19 @@ public class RydePgpFileOutputStream extends ImprovedOutputStream {
|
||||||
* @throws RuntimeException to rethrow {@link IOException}
|
* @throws RuntimeException to rethrow {@link IOException}
|
||||||
*/
|
*/
|
||||||
public RydePgpFileOutputStream(
|
public RydePgpFileOutputStream(
|
||||||
@Provided @Config("rdeRydeBufferSize") Integer bufferSize,
|
|
||||||
@WillNotClose OutputStream os,
|
@WillNotClose OutputStream os,
|
||||||
DateTime modified,
|
DateTime modified,
|
||||||
String filename) {
|
String filename) {
|
||||||
super("RydePgpFileOutputStream", createDelegate(bufferSize, os, modified, filename));
|
super("RydePgpFileOutputStream", createDelegate(os, modified, filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OutputStream
|
private static OutputStream
|
||||||
createDelegate(int bufferSize, OutputStream os, DateTime modified, String filename) {
|
createDelegate(OutputStream os, DateTime modified, String filename) {
|
||||||
try {
|
try {
|
||||||
checkArgument(filename.endsWith(".tar"),
|
checkArgument(filename.endsWith(".tar"),
|
||||||
"Ryde PGP message should contain a tar file.");
|
"Ryde PGP message should contain a tar file.");
|
||||||
return new PGPLiteralDataGenerator().open(
|
return new PGPLiteralDataGenerator().open(
|
||||||
os, BINARY, filename, modified.toDate(), new byte[bufferSize]);
|
os, BINARY, filename, modified.toDate(), new byte[BUFFER_SIZE]);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import static org.bouncycastle.bcpg.HashAlgorithmTags.SHA256;
|
||||||
import static org.bouncycastle.bcpg.PublicKeyAlgorithmTags.RSA_GENERAL;
|
import static org.bouncycastle.bcpg.PublicKeyAlgorithmTags.RSA_GENERAL;
|
||||||
import static org.bouncycastle.openpgp.PGPSignature.BINARY_DOCUMENT;
|
import static org.bouncycastle.openpgp.PGPSignature.BINARY_DOCUMENT;
|
||||||
|
|
||||||
import com.google.auto.factory.AutoFactory;
|
|
||||||
import google.registry.util.ImprovedOutputStream;
|
import google.registry.util.ImprovedOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -40,7 +39,6 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
|
||||||
* who receive a deposit to check the signature against our public key so they can know the
|
* who receive a deposit to check the signature against our public key so they can know the
|
||||||
* data hasn't been forged.
|
* data hasn't been forged.
|
||||||
*/
|
*/
|
||||||
@AutoFactory(allowSubclasses = true)
|
|
||||||
public class RydePgpSigningOutputStream extends ImprovedOutputStream {
|
public class RydePgpSigningOutputStream extends ImprovedOutputStream {
|
||||||
|
|
||||||
private final PGPSignatureGenerator signer;
|
private final PGPSignatureGenerator signer;
|
||||||
|
|
|
@ -16,7 +16,6 @@ package google.registry.rde;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
import com.google.auto.factory.AutoFactory;
|
|
||||||
import google.registry.util.ImprovedOutputStream;
|
import google.registry.util.ImprovedOutputStream;
|
||||||
import google.registry.util.PosixTarHeader;
|
import google.registry.util.PosixTarHeader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -27,7 +26,6 @@ import org.joda.time.DateTime;
|
||||||
/**
|
/**
|
||||||
* Single-file POSIX tar archive creator that wraps an {@link OutputStream}.
|
* Single-file POSIX tar archive creator that wraps an {@link OutputStream}.
|
||||||
*/
|
*/
|
||||||
@AutoFactory(allowSubclasses = true)
|
|
||||||
public class RydeTarOutputStream extends ImprovedOutputStream {
|
public class RydeTarOutputStream extends ImprovedOutputStream {
|
||||||
|
|
||||||
private final long expectedSize;
|
private final long expectedSize;
|
||||||
|
|
|
@ -20,16 +20,7 @@ import com.google.common.io.ByteStreams;
|
||||||
import google.registry.keyring.api.KeyModule.Key;
|
import google.registry.keyring.api.KeyModule.Key;
|
||||||
import google.registry.model.rde.RdeNamingUtils;
|
import google.registry.model.rde.RdeNamingUtils;
|
||||||
import google.registry.rde.RdeUtil;
|
import google.registry.rde.RdeUtil;
|
||||||
import google.registry.rde.RydePgpCompressionOutputStream;
|
import google.registry.rde.RydeEncoder;
|
||||||
import google.registry.rde.RydePgpCompressionOutputStreamFactory;
|
|
||||||
import google.registry.rde.RydePgpEncryptionOutputStream;
|
|
||||||
import google.registry.rde.RydePgpEncryptionOutputStreamFactory;
|
|
||||||
import google.registry.rde.RydePgpFileOutputStream;
|
|
||||||
import google.registry.rde.RydePgpFileOutputStreamFactory;
|
|
||||||
import google.registry.rde.RydePgpSigningOutputStream;
|
|
||||||
import google.registry.rde.RydePgpSigningOutputStreamFactory;
|
|
||||||
import google.registry.rde.RydeTarOutputStream;
|
|
||||||
import google.registry.rde.RydeTarOutputStreamFactory;
|
|
||||||
import google.registry.xml.XmlException;
|
import google.registry.xml.XmlException;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -40,7 +31,6 @@ import java.nio.file.Path;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPKeyPair;
|
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
@ -50,18 +40,13 @@ final class EscrowDepositEncryptor {
|
||||||
|
|
||||||
private static final int PEEK_BUFFER_SIZE = 64 * 1024;
|
private static final int PEEK_BUFFER_SIZE = 64 * 1024;
|
||||||
|
|
||||||
@Inject RydePgpCompressionOutputStreamFactory pgpCompressionFactory;
|
|
||||||
@Inject RydePgpEncryptionOutputStreamFactory pgpEncryptionFactory;
|
|
||||||
@Inject RydePgpFileOutputStreamFactory pgpFileFactory;
|
|
||||||
@Inject RydePgpSigningOutputStreamFactory pgpSigningFactory;
|
|
||||||
@Inject RydeTarOutputStreamFactory tarFactory;
|
|
||||||
@Inject @Key("rdeSigningKey") Provider<PGPKeyPair> rdeSigningKey;
|
@Inject @Key("rdeSigningKey") Provider<PGPKeyPair> rdeSigningKey;
|
||||||
@Inject @Key("rdeReceiverKey") Provider<PGPPublicKey> rdeReceiverKey;
|
@Inject @Key("rdeReceiverKey") Provider<PGPPublicKey> rdeReceiverKey;
|
||||||
@Inject EscrowDepositEncryptor() {}
|
@Inject EscrowDepositEncryptor() {}
|
||||||
|
|
||||||
/** Creates a {@code .ryde} and {@code .sig} file, provided an XML deposit file. */
|
/** Creates a {@code .ryde} and {@code .sig} file, provided an XML deposit file. */
|
||||||
void encrypt(String tld, Path xmlFile, Path outdir)
|
void encrypt(String tld, Path xmlFile, Path outdir)
|
||||||
throws IOException, PGPException, XmlException {
|
throws IOException, XmlException {
|
||||||
try (InputStream xmlFileInput = Files.newInputStream(xmlFile);
|
try (InputStream xmlFileInput = Files.newInputStream(xmlFile);
|
||||||
BufferedInputStream xmlInput = new BufferedInputStream(xmlFileInput, PEEK_BUFFER_SIZE)) {
|
BufferedInputStream xmlInput = new BufferedInputStream(xmlFileInput, PEEK_BUFFER_SIZE)) {
|
||||||
DateTime watermark = RdeUtil.peekWatermark(xmlInput);
|
DateTime watermark = RdeUtil.peekWatermark(xmlInput);
|
||||||
|
@ -71,19 +56,14 @@ final class EscrowDepositEncryptor {
|
||||||
Path pubPath = outdir.resolve(tld + ".pub");
|
Path pubPath = outdir.resolve(tld + ".pub");
|
||||||
PGPKeyPair signingKey = rdeSigningKey.get();
|
PGPKeyPair signingKey = rdeSigningKey.get();
|
||||||
try (OutputStream rydeOutput = Files.newOutputStream(rydePath);
|
try (OutputStream rydeOutput = Files.newOutputStream(rydePath);
|
||||||
RydePgpSigningOutputStream signLayer =
|
OutputStream sigOutput = Files.newOutputStream(sigPath);
|
||||||
pgpSigningFactory.create(rydeOutput, signingKey)) {
|
RydeEncoder rydeEncoder = new RydeEncoder.Builder()
|
||||||
try (RydePgpEncryptionOutputStream encryptLayer =
|
.setRydeOutput(rydeOutput, rdeReceiverKey.get())
|
||||||
pgpEncryptionFactory.create(signLayer, rdeReceiverKey.get());
|
.setSignatureOutput(sigOutput, signingKey)
|
||||||
RydePgpCompressionOutputStream compressLayer =
|
.setFileMetadata(name, Files.size(xmlFile), watermark)
|
||||||
pgpCompressionFactory.create(encryptLayer);
|
.build()) {
|
||||||
RydePgpFileOutputStream fileLayer =
|
ByteStreams.copy(xmlInput, rydeEncoder);
|
||||||
pgpFileFactory.create(compressLayer, watermark, name + ".tar");
|
|
||||||
RydeTarOutputStream tarLayer =
|
|
||||||
tarFactory.create(fileLayer, Files.size(xmlFile), watermark, name + ".xml")) {
|
|
||||||
ByteStreams.copy(xmlInput, tarLayer);
|
|
||||||
}
|
}
|
||||||
Files.write(sigPath, signLayer.getSignature());
|
|
||||||
try (OutputStream pubOutput = Files.newOutputStream(pubPath);
|
try (OutputStream pubOutput = Files.newOutputStream(pubPath);
|
||||||
ArmoredOutputStream ascOutput = new ArmoredOutputStream(pubOutput)) {
|
ArmoredOutputStream ascOutput = new ArmoredOutputStream(pubOutput)) {
|
||||||
signingKey.getPublicKey().encode(ascOutput);
|
signingKey.getPublicKey().encode(ascOutput);
|
||||||
|
@ -91,4 +71,3 @@ final class EscrowDepositEncryptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -100,11 +100,6 @@ public class BrdaCopyActionTest extends ShardableTestCase {
|
||||||
@Before
|
@Before
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
action.gcsUtils = gcsUtils;
|
action.gcsUtils = gcsUtils;
|
||||||
action.pgpCompressionFactory = new RydePgpCompressionOutputStreamFactory(() -> 1024);
|
|
||||||
action.pgpEncryptionFactory = new RydePgpEncryptionOutputStreamFactory(() -> 1024);
|
|
||||||
action.pgpFileFactory = new RydePgpFileOutputStreamFactory(() -> 1024);
|
|
||||||
action.pgpSigningFactory = new RydePgpSigningOutputStreamFactory();
|
|
||||||
action.tarFactory = new RydeTarOutputStreamFactory();
|
|
||||||
action.tld = "lol";
|
action.tld = "lol";
|
||||||
action.watermark = DateTime.parse("2010-10-17TZ");
|
action.watermark = DateTime.parse("2010-10-17TZ");
|
||||||
action.brdaBucket = "tub";
|
action.brdaBucket = "tub";
|
||||||
|
|
|
@ -70,7 +70,6 @@ import google.registry.testing.FakeKeyringModule;
|
||||||
import google.registry.testing.FakeResponse;
|
import google.registry.testing.FakeResponse;
|
||||||
import google.registry.testing.FakeSleeper;
|
import google.registry.testing.FakeSleeper;
|
||||||
import google.registry.testing.GpgSystemCommandRule;
|
import google.registry.testing.GpgSystemCommandRule;
|
||||||
import google.registry.testing.IoSpyRule;
|
|
||||||
import google.registry.testing.Lazies;
|
import google.registry.testing.Lazies;
|
||||||
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
||||||
import google.registry.testing.sftp.SftpServerRule;
|
import google.registry.testing.sftp.SftpServerRule;
|
||||||
|
@ -81,10 +80,8 @@ import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import org.bouncycastle.openpgp.PGPKeyPair;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -131,11 +128,6 @@ public class RdeUploadActionTest {
|
||||||
RdeTestData.loadBytes("pgp-public-keyring.asc"),
|
RdeTestData.loadBytes("pgp-public-keyring.asc"),
|
||||||
RdeTestData.loadBytes("pgp-private-keyring-escrow.asc"));
|
RdeTestData.loadBytes("pgp-private-keyring-escrow.asc"));
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final IoSpyRule ioSpy = new IoSpyRule()
|
|
||||||
.checkClosedOnlyOnce()
|
|
||||||
.checkCharIoMaxCalls(10);
|
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public final AppEngineRule appEngine = AppEngineRule.builder()
|
public final AppEngineRule appEngine = AppEngineRule.builder()
|
||||||
.withDatastore()
|
.withDatastore()
|
||||||
|
@ -146,42 +138,6 @@ public class RdeUploadActionTest {
|
||||||
private final EscrowTaskRunner runner = mock(EscrowTaskRunner.class);
|
private final EscrowTaskRunner runner = mock(EscrowTaskRunner.class);
|
||||||
private final FakeClock clock = new FakeClock(DateTime.parse("2010-10-17TZ"));
|
private final FakeClock clock = new FakeClock(DateTime.parse("2010-10-17TZ"));
|
||||||
|
|
||||||
private final RydeTarOutputStreamFactory tarFactory =
|
|
||||||
new RydeTarOutputStreamFactory() {
|
|
||||||
@Override
|
|
||||||
public RydeTarOutputStream create(
|
|
||||||
OutputStream os, long size, DateTime modified, String filename) {
|
|
||||||
return ioSpy.register(super.create(os, size, modified, filename));
|
|
||||||
}};
|
|
||||||
|
|
||||||
private final RydePgpFileOutputStreamFactory literalFactory =
|
|
||||||
new RydePgpFileOutputStreamFactory(() -> BUFFER_SIZE) {
|
|
||||||
@Override
|
|
||||||
public RydePgpFileOutputStream create(OutputStream os, DateTime modified, String filename) {
|
|
||||||
return ioSpy.register(super.create(os, modified, filename));
|
|
||||||
}};
|
|
||||||
|
|
||||||
private final RydePgpEncryptionOutputStreamFactory encryptFactory =
|
|
||||||
new RydePgpEncryptionOutputStreamFactory(() -> BUFFER_SIZE) {
|
|
||||||
@Override
|
|
||||||
public RydePgpEncryptionOutputStream create(OutputStream os, PGPPublicKey publicKey) {
|
|
||||||
return ioSpy.register(super.create(os, publicKey));
|
|
||||||
}};
|
|
||||||
|
|
||||||
private final RydePgpCompressionOutputStreamFactory compressFactory =
|
|
||||||
new RydePgpCompressionOutputStreamFactory(() -> BUFFER_SIZE) {
|
|
||||||
@Override
|
|
||||||
public RydePgpCompressionOutputStream create(OutputStream os) {
|
|
||||||
return ioSpy.register(super.create(os));
|
|
||||||
}};
|
|
||||||
|
|
||||||
private final RydePgpSigningOutputStreamFactory signFactory =
|
|
||||||
new RydePgpSigningOutputStreamFactory() {
|
|
||||||
@Override
|
|
||||||
public RydePgpSigningOutputStream create(OutputStream os, PGPKeyPair signingKey) {
|
|
||||||
return ioSpy.register(super.create(os, signingKey));
|
|
||||||
}};
|
|
||||||
|
|
||||||
private RdeUploadAction createAction(URI uploadUrl) {
|
private RdeUploadAction createAction(URI uploadUrl) {
|
||||||
try (Keyring keyring = new FakeKeyringModule().get()) {
|
try (Keyring keyring = new FakeKeyringModule().get()) {
|
||||||
RdeUploadAction action = new RdeUploadAction();
|
RdeUploadAction action = new RdeUploadAction();
|
||||||
|
@ -195,11 +151,6 @@ public class RdeUploadActionTest {
|
||||||
keyring.getRdeSshClientPublicKey());
|
keyring.getRdeSshClientPublicKey());
|
||||||
action.jschSshSessionFactory = new JSchSshSessionFactory(standardSeconds(3));
|
action.jschSshSessionFactory = new JSchSshSessionFactory(standardSeconds(3));
|
||||||
action.response = response;
|
action.response = response;
|
||||||
action.pgpCompressionFactory = compressFactory;
|
|
||||||
action.pgpEncryptionFactory = encryptFactory;
|
|
||||||
action.pgpFileFactory = literalFactory;
|
|
||||||
action.pgpSigningFactory = signFactory;
|
|
||||||
action.tarFactory = tarFactory;
|
|
||||||
action.bucket = "bucket";
|
action.bucket = "bucket";
|
||||||
action.interval = standardDays(1);
|
action.interval = standardDays(1);
|
||||||
action.timeout = standardSeconds(23);
|
action.timeout = standardSeconds(23);
|
||||||
|
|
|
@ -68,12 +68,6 @@ public class RydeGpgIntegrationTest extends ShardableTestCase {
|
||||||
new GpgCommand("gpg2"),
|
new GpgCommand("gpg2"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@DataPoints
|
|
||||||
public static BufferSize[] bufferSizes = new BufferSize[] {
|
|
||||||
new BufferSize(1),
|
|
||||||
new BufferSize(7),
|
|
||||||
};
|
|
||||||
|
|
||||||
@DataPoints
|
@DataPoints
|
||||||
public static Filename[] filenames = new Filename[] {
|
public static Filename[] filenames = new Filename[] {
|
||||||
new Filename("sloth"),
|
new Filename("sloth"),
|
||||||
|
@ -88,22 +82,11 @@ public class RydeGpgIntegrationTest extends ShardableTestCase {
|
||||||
};
|
};
|
||||||
|
|
||||||
@Theory
|
@Theory
|
||||||
public void test(GpgCommand cmd, BufferSize bufSize, Filename name, Content content)
|
public void test(GpgCommand cmd, Filename name, Content content)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
assumeTrue(hasCommand("tar"));
|
assumeTrue(hasCommand("tar"));
|
||||||
assumeTrue(hasCommand(cmd.get() + " --version"));
|
assumeTrue(hasCommand(cmd.get() + " --version"));
|
||||||
|
|
||||||
RydeTarOutputStreamFactory tarFactory =
|
|
||||||
new RydeTarOutputStreamFactory();
|
|
||||||
RydePgpFileOutputStreamFactory pgpFileFactory =
|
|
||||||
new RydePgpFileOutputStreamFactory(bufSize::get);
|
|
||||||
RydePgpEncryptionOutputStreamFactory pgpEncryptionFactory =
|
|
||||||
new RydePgpEncryptionOutputStreamFactory(bufSize::get);
|
|
||||||
RydePgpCompressionOutputStreamFactory pgpCompressionFactory =
|
|
||||||
new RydePgpCompressionOutputStreamFactory(bufSize::get);
|
|
||||||
RydePgpSigningOutputStreamFactory pgpSigningFactory =
|
|
||||||
new RydePgpSigningOutputStreamFactory();
|
|
||||||
|
|
||||||
Keyring keyring = keyringFactory.get();
|
Keyring keyring = keyringFactory.get();
|
||||||
PGPKeyPair signingKey = keyring.getRdeSigningKey();
|
PGPKeyPair signingKey = keyring.getRdeSigningKey();
|
||||||
PGPPublicKey receiverKey = keyring.getRdeReceiverKey();
|
PGPPublicKey receiverKey = keyring.getRdeReceiverKey();
|
||||||
|
@ -116,20 +99,13 @@ public class RydeGpgIntegrationTest extends ShardableTestCase {
|
||||||
byte[] data = content.get().getBytes(UTF_8);
|
byte[] data = content.get().getBytes(UTF_8);
|
||||||
|
|
||||||
try (OutputStream rydeOut = new FileOutputStream(rydeFile);
|
try (OutputStream rydeOut = new FileOutputStream(rydeFile);
|
||||||
RydePgpSigningOutputStream signLayer = pgpSigningFactory.create(rydeOut, signingKey)) {
|
OutputStream sigOut = new FileOutputStream(sigFile);
|
||||||
try (RydePgpEncryptionOutputStream encryptLayer =
|
RydeEncoder rydeEncoder = new RydeEncoder.Builder()
|
||||||
pgpEncryptionFactory.create(signLayer, receiverKey);
|
.setRydeOutput(rydeOut, receiverKey)
|
||||||
RydePgpCompressionOutputStream compressLayer =
|
.setSignatureOutput(sigOut, signingKey)
|
||||||
pgpCompressionFactory.create(encryptLayer);
|
.setFileMetadata(name.get(), data.length, modified)
|
||||||
RydePgpFileOutputStream fileLayer =
|
.build()) {
|
||||||
pgpFileFactory.create(compressLayer, modified, name.get() + ".tar");
|
rydeEncoder.write(data);
|
||||||
RydeTarOutputStream tarLayer =
|
|
||||||
tarFactory.create(fileLayer, data.length, modified, name.get() + ".xml")) {
|
|
||||||
tarLayer.write(data);
|
|
||||||
}
|
|
||||||
try (OutputStream sigOut = new FileOutputStream(sigFile)) {
|
|
||||||
sigOut.write(signLayer.getSignature());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iron Mountain examines the ryde file to see what sort of OpenPGP layers it contains.
|
// Iron Mountain examines the ryde file to see what sort of OpenPGP layers it contains.
|
||||||
|
@ -252,18 +228,6 @@ public class RydeGpgIntegrationTest extends ShardableTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class BufferSize {
|
|
||||||
private final int value;
|
|
||||||
|
|
||||||
BufferSize(int value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Filename {
|
private static class Filename {
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package google.registry.testing;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static org.mockito.Matchers.anyInt;
|
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
|
||||||
import static org.mockito.Mockito.atMost;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.annotation.CheckReturnValue;
|
|
||||||
import org.junit.rules.ExternalResource;
|
|
||||||
|
|
||||||
/** JUnit Rule that uses Mockito to spy on I/O streams to make sure they're healthy. */
|
|
||||||
public final class IoSpyRule extends ExternalResource {
|
|
||||||
|
|
||||||
private boolean checkClosedOnlyOnce;
|
|
||||||
private boolean checkClosedAtLeastOnce;
|
|
||||||
private int checkCharIoMaxCalls = -1;
|
|
||||||
private final List<Closeable> spiedCloseables = new ArrayList<>();
|
|
||||||
private final List<InputStream> spiedInputStreams = new ArrayList<>();
|
|
||||||
private final List<OutputStream> spiedOutputStreams = new ArrayList<>();
|
|
||||||
|
|
||||||
public IoSpyRule() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables check where {@link Closeable#close() close} must be called EXACTLY once.
|
|
||||||
*
|
|
||||||
* <p>This is sort of pedantic, since Java's contract for close specifies that it must permit
|
|
||||||
* multiple calls.
|
|
||||||
*/
|
|
||||||
public IoSpyRule checkClosedOnlyOnce() {
|
|
||||||
checkState(!checkClosedAtLeastOnce, "you're already using checkClosedAtLeastOnce()");
|
|
||||||
checkClosedOnlyOnce = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Enables check where {@link Closeable#close() close} must be called at least once. */
|
|
||||||
public IoSpyRule checkClosedAtLeastOnce() {
|
|
||||||
checkState(!checkClosedOnlyOnce, "you're already using checkClosedOnlyOnce()");
|
|
||||||
checkClosedAtLeastOnce = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Enables check to make sure your streams aren't going too slow with char-based I/O. */
|
|
||||||
public IoSpyRule checkCharIoMaxCalls(int value) {
|
|
||||||
checkArgument(value >= 0, "value >= 0");
|
|
||||||
checkCharIoMaxCalls = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Adds your {@link Closeable} to the list of streams to check, and returns its mocked self. */
|
|
||||||
@CheckReturnValue
|
|
||||||
public <T extends Closeable> T register(T stream) {
|
|
||||||
T res = spy(stream);
|
|
||||||
spiedCloseables.add(res);
|
|
||||||
if (stream instanceof InputStream) {
|
|
||||||
spiedInputStreams.add((InputStream) res);
|
|
||||||
}
|
|
||||||
if (stream instanceof OutputStream) {
|
|
||||||
spiedOutputStreams.add((OutputStream) res);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void after() {
|
|
||||||
checkState(checkClosedOnlyOnce
|
|
||||||
|| checkClosedAtLeastOnce
|
|
||||||
|| checkCharIoMaxCalls != -1,
|
|
||||||
"At least one check must be enabled.");
|
|
||||||
try {
|
|
||||||
check();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
spiedCloseables.clear();
|
|
||||||
spiedInputStreams.clear();
|
|
||||||
spiedOutputStreams.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void check() throws IOException {
|
|
||||||
for (Closeable stream : spiedCloseables) {
|
|
||||||
if (checkClosedAtLeastOnce) {
|
|
||||||
verify(stream, atLeastOnce()).close();
|
|
||||||
} else if (checkClosedOnlyOnce) {
|
|
||||||
verify(stream, times(1)).close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (checkCharIoMaxCalls != -1) {
|
|
||||||
for (InputStream stream : spiedInputStreams) {
|
|
||||||
verify(stream, atMost(checkCharIoMaxCalls)).read();
|
|
||||||
}
|
|
||||||
for (OutputStream stream : spiedOutputStreams) {
|
|
||||||
verify(stream, atMost(checkCharIoMaxCalls)).write(anyInt());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,11 +20,6 @@ import static google.registry.testing.TestDataHelper.loadBytes;
|
||||||
import com.google.common.io.ByteSource;
|
import com.google.common.io.ByteSource;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import google.registry.rde.RdeTestData;
|
import google.registry.rde.RdeTestData;
|
||||||
import google.registry.rde.RydePgpCompressionOutputStreamFactory;
|
|
||||||
import google.registry.rde.RydePgpEncryptionOutputStreamFactory;
|
|
||||||
import google.registry.rde.RydePgpFileOutputStreamFactory;
|
|
||||||
import google.registry.rde.RydePgpSigningOutputStreamFactory;
|
|
||||||
import google.registry.rde.RydeTarOutputStreamFactory;
|
|
||||||
import google.registry.testing.BouncyCastleProviderRule;
|
import google.registry.testing.BouncyCastleProviderRule;
|
||||||
import google.registry.testing.FakeKeyringModule;
|
import google.registry.testing.FakeKeyringModule;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -43,11 +38,6 @@ public class EncryptEscrowDepositCommandTest
|
||||||
|
|
||||||
static EscrowDepositEncryptor createEncryptor() {
|
static EscrowDepositEncryptor createEncryptor() {
|
||||||
EscrowDepositEncryptor res = new EscrowDepositEncryptor();
|
EscrowDepositEncryptor res = new EscrowDepositEncryptor();
|
||||||
res.pgpCompressionFactory = new RydePgpCompressionOutputStreamFactory(() -> 1024);
|
|
||||||
res.pgpEncryptionFactory = new RydePgpEncryptionOutputStreamFactory(() -> 1024);
|
|
||||||
res.pgpFileFactory = new RydePgpFileOutputStreamFactory(() -> 1024);
|
|
||||||
res.pgpSigningFactory = new RydePgpSigningOutputStreamFactory();
|
|
||||||
res.tarFactory = new RydeTarOutputStreamFactory();
|
|
||||||
res.rdeReceiverKey = () -> new FakeKeyringModule().get().getRdeReceiverKey();
|
res.rdeReceiverKey = () -> new FakeKeyringModule().get().getRdeReceiverKey();
|
||||||
res.rdeSigningKey = () -> new FakeKeyringModule().get().getRdeSigningKey();
|
res.rdeSigningKey = () -> new FakeKeyringModule().get().getRdeSigningKey();
|
||||||
return res;
|
return res;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue