diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpChunkFramer.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpChunkFramer.java index 79b3fa7..e4a69fa 100644 --- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpChunkFramer.java +++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpChunkFramer.java @@ -24,8 +24,7 @@ * Interface that defines how an Oblivious HTTP implementation handles the framing of chunks. *
* Instances of {@link OHttpChunkFramer} are stateless. The state management and encryption is delegated to - * the {@link Decoder} and {@link Encoder} interfaces, which are typically implemented by - * {@link OHttpContentParser} and {@link OHttpContentSerializer}, respectively. + * the {@link Decoder} and {@link Encoder} interfaces. */ public interface OHttpChunkFramer { diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCiphersuite.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCiphersuite.java index 6466b86..6883f6b 100644 --- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCiphersuite.java +++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCiphersuite.java @@ -35,7 +35,8 @@ public final class OHttpCiphersuite { private static final int ENCODED_LENGTH = 7; - public OHttpCiphersuite(byte keyId, HybridPublicKeyEncryption.KEM kem, HybridPublicKeyEncryption.KDF kdf, HybridPublicKeyEncryption.AEAD aead) { + public OHttpCiphersuite(byte keyId, HybridPublicKeyEncryption.KEM kem, HybridPublicKeyEncryption.KDF kdf, + HybridPublicKeyEncryption.AEAD aead) { this.keyId = keyId; this.kem = requireNonNull(kem, "kem"); this.kdf = requireNonNull(kdf, "kdf"); diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpClientCodec.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpClientCodec.java index cd4453d..8bdc602 100644 --- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpClientCodec.java +++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpClientCodec.java @@ -36,12 +36,17 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.LastHttpContent; +import io.netty.incubator.codec.hpke.HybridPublicKeyEncryption; import io.netty.util.AsciiString; import io.netty.util.ReferenceCountUtil; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.List; +import java.util.function.Function; import static io.netty.handler.codec.ByteToMessageDecoder.MERGE_CUMULATOR; +import static java.util.Objects.requireNonNull; /** * {@link MessageToMessageCodec} that HTTP clients can use to encrypt outgoing HTTP requests into @@ -50,15 +55,48 @@ *

* Both incoming and outgoing messages are {@link HttpObject}s. */ -public abstract class OHttpClientCodec extends MessageToMessageCodec { +public final class OHttpClientCodec extends MessageToMessageCodec { - private OHttpContentSerializer serializer; - private OHttpContentParser parser; + private final Deque contextHolders = new ArrayDeque<>(); + + private static final class OHttpRequestResponseContextHolder { + + static final OHttpRequestResponseContextHolder NONE = new OHttpRequestResponseContextHolder(null); + + final OHttpRequestResponseContext handler; + + OHttpRequestResponseContextHolder(OHttpRequestResponseContext handler) { + this.handler = handler; + } + + void destroy() { + if (handler != null) { + handler.destroy(); + } + } + } + + private final HybridPublicKeyEncryption encryption; + private final Function encapsulationFunc; - private OHttpClientContext context; private ByteBuf cumulationBuffer = Unpooled.EMPTY_BUFFER; private boolean destroyed; + /** + * Creates a new instance + * + * @param encryption the {@link HybridPublicKeyEncryption} to use for all the crypto. + * @param encapsulationFunc the {@link Function} that will be used to return the correct + * {@link EncapsulationParameters} for a given {@link HttpRequest}. + * If {@link Function} returns {@code null} no encapsulation will + * take place. + */ + public OHttpClientCodec(HybridPublicKeyEncryption encryption, Function encapsulationFunc) { + this.encryption = requireNonNull(encryption, "encryption"); + this.encapsulationFunc = requireNonNull(encapsulationFunc, "encapsulationFunc"); + } + /** * Parameters that control the OHTTP encapsulation of an HTTP request. */ @@ -75,25 +113,80 @@ public interface EncapsulationParameters { String outerRequestAuthority(); /** - * Update outer HTTP request headers, if necessary. - * @param headers {@link HttpHeaders} to be updated. + * Create the headers for the other HTTP request. + * @return headers */ - default void outerRequestUpdateHeaders(HttpHeaders headers) { + default HttpHeaders outerRequestHeaders() { + return new DefaultHttpHeaders(); } /** - * @return {@link OHttpClientContext}. + * Return the {@link OHttpCiphersuite}s to use. + * + * @return the ciphersuites. */ - OHttpClientContext context(); - } + OHttpCiphersuite ciphersuite(); - /** - * Get the parameters to encapsulate a {@link HttpRequest} into OHTTP. - *
- * @param request outbound {@link HttpRequest} intercepted by the handler. - * @return {@link EncapsulationParameters} object if OHTTP encapsulation is required, or null otherwise. - */ - protected abstract EncapsulationParameters encapsulationParameters(HttpRequest request); + /** + * The public key bytes of the server. + * + * @return bytes. + */ + byte[] serverPublicKeyBytes(); + + /** + * The {@link OHttpVersion} to use. + * + * @return the version. + */ + OHttpVersion version(); + + /** + * Create a simple {@link EncapsulationParameters} instance. + * + * @param version the version to use. + * @param ciphersuite the suite to use. + * @param serverPublicKeyBytes the public key to use. + * @param outerRequestUri the outer requst uri. + * @param outerRequestAuthority the authority. + * @return created params. + */ + static EncapsulationParameters newInstance(OHttpVersion version, OHttpCiphersuite ciphersuite, + byte[] serverPublicKeyBytes, String outerRequestUri, + String outerRequestAuthority) { + requireNonNull(version, "version"); + requireNonNull(ciphersuite, "ciphersuite"); + requireNonNull(serverPublicKeyBytes, "serverPublicKeysBytes"); + requireNonNull(outerRequestUri, "outerRequestUri"); + requireNonNull(outerRequestAuthority, "outerRequestAuthority"); + return new EncapsulationParameters() { + @Override + public String outerRequestUri() { + return outerRequestUri; + } + + @Override + public String outerRequestAuthority() { + return outerRequestAuthority; + } + + @Override + public OHttpCiphersuite ciphersuite() { + return ciphersuite; + } + + @Override + public byte[] serverPublicKeyBytes() { + return serverPublicKeyBytes; + } + + @Override + public OHttpVersion version() { + return version; + } + }; + } + } @Override public final boolean isSharable() { @@ -106,30 +199,35 @@ protected final void decode(ChannelHandlerContext ctx, HttpObject msg, List framer; - - private final ContentEncoder contentEncoder = new ContentEncoder(); - - OHttpContentSerializer(OHttpChunkFramer framer) { - this.framer = framer; - } - - /** - * Serialize a {@link HttpObject} into a {@link ByteBuf}. - * @param msg {@link HttpObject} to serialize. - * @param out {@link ByteBuf} that serialized bytes are appended to. - */ - public final void serialize(HttpObject msg, ByteBuf out) throws CryptoException { - framer.serialize(msg, contentEncoder, out); - } - - /** - * Encrypt a chunk. - * @param chunk {@link ByteBuf} to encrypt. The function increases the reader index by chunkLength. - * @param chunkLength length of chunk. - * @param isFinal true if this is the last chunk. - * @param out {@link ByteBuf} into which the encrypted bytes are written. - * @throws CryptoException if the encryption fails. - */ - protected abstract void encryptChunk(ByteBuf chunk, int chunkLength, boolean isFinal, ByteBuf out) - throws CryptoException; - - /** - * Encode the beginning of the HTTP content body. - * @param out buffer to write the bytes. - * @throws CryptoException if the prefix cannot be encoded. - */ - protected abstract void encodePrefixNow(ByteBuf out) throws CryptoException; - - private class ContentEncoder implements OHttpChunkFramer.Encoder { - - private final BinaryHttpSerializer binaryHttpSerializer = new BinaryHttpSerializer(); - - private boolean encodedPrefix; - - @Override - public final boolean isPrefixNeeded() { - return !encodedPrefix; - } - - @Override - public final void encodeChunk(HttpObject msg, ByteBuf out) throws CryptoException { - ByteBuf binaryHttpBytes = out.alloc().buffer(); - try { - boolean isFinal = msg instanceof LastHttpContent; - binaryHttpSerializer.serialize(msg, binaryHttpBytes); - encryptChunk(binaryHttpBytes, binaryHttpBytes.readableBytes(), isFinal, out); - } finally { - binaryHttpBytes.release(); - } - } - - @Override - public final void encodePrefix(ByteBuf out) throws CryptoException { - if (encodedPrefix) { - throw new IllegalStateException("Prefix already encoded"); - } - encodePrefixNow(out); - encodedPrefix = true; - } - } -} diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoReceiver.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoReceiver.java index d008b38..b473859 100644 --- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoReceiver.java +++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoReceiver.java @@ -32,7 +32,6 @@ /** * {@link OHttpCryptoReceiver} handles all the server-side crypto for an OHTTP request/response. - * It is used internally by {@link OHttpServerContext}. */ public final class OHttpCryptoReceiver { private final OHttpCryptoConfiguration configuration; diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoSender.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoSender.java index 5e76364..b4022a9 100644 --- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoSender.java +++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpCryptoSender.java @@ -32,7 +32,6 @@ /** * {@link OHttpCryptoSender} handles all the client-side crypto for an OHTTP request/response. - * It is used internally by {@link OHttpClientContext}. */ public final class OHttpCryptoSender { diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpContentParser.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpRequestResponseContext.java similarity index 58% rename from codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpContentParser.java rename to codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpRequestResponseContext.java index 853aa53..407ef69 100644 --- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpContentParser.java +++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpRequestResponseContext.java @@ -15,32 +15,98 @@ */ package io.netty.incubator.codec.ohttp; -import io.netty.incubator.codec.bhttp.BinaryHttpParser; -import io.netty.incubator.codec.hpke.CryptoException; -import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.CorruptedFrameException; import io.netty.handler.codec.http.HttpContent; +import io.netty.incubator.codec.bhttp.BinaryHttpParser; +import io.netty.incubator.codec.bhttp.BinaryHttpSerializer; +import io.netty.incubator.codec.hpke.CryptoException; +import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.LastHttpContent; import java.util.List; import static io.netty.handler.codec.ByteToMessageDecoder.MERGE_CUMULATOR; /** - * Parser that parses {@link ByteBuf}s coming from the content of a OHTTP-encoded message - * into {@link HttpObject}s. + * Handler that parses and serializes {@link HttpObject}s to {@link ByteBuf} that can be used as OHTTP message content + * during a request-response cycle. */ -abstract class OHttpContentParser { +abstract class OHttpRequestResponseContext { + private final OHttpVersion version; + private final ContentEncoder contentEncoder = new ContentEncoder(); + private ContentDecoder decoder = new ContentDecoder(); - private final OHttpChunkFramer framer; + OHttpRequestResponseContext(OHttpVersion version) { + this.version = version; + } - private ContentDecoder decoder = new ContentDecoder(); + final OHttpVersion version() { + return version; + } - OHttpContentParser(OHttpChunkFramer framer) { - this.framer = framer; + /** + * Serialize a {@link HttpObject} into a {@link ByteBuf}. + * @param msg {@link HttpObject} to serialize. + * @param out {@link ByteBuf} that serialized bytes are appended to. + */ + final void serialize(HttpObject msg, ByteBuf out) throws CryptoException { + version.serialize(msg, contentEncoder, out); } + /** + * Encrypt a chunk. + * @param chunk {@link ByteBuf} to encrypt. The function increases the reader index by chunkLength. + * @param chunkLength length of chunk. + * @param isFinal true if this is the last chunk. + * @param out {@link ByteBuf} into which the encrypted bytes are written. + * @throws CryptoException if the encryption fails. + */ + protected abstract void encryptChunk(ByteBuf chunk, int chunkLength, boolean isFinal, ByteBuf out) + throws CryptoException; + + /** + * Encode the beginning of the HTTP content body. + * @param out buffer to write the bytes. + * @throws CryptoException if the prefix cannot be encoded. + */ + protected abstract void encodePrefixNow(ByteBuf out) throws CryptoException; + + private final class ContentEncoder implements OHttpChunkFramer.Encoder { + + private final BinaryHttpSerializer binaryHttpSerializer = new BinaryHttpSerializer(); + + private boolean encodedPrefix; + + @Override + public boolean isPrefixNeeded() { + return !encodedPrefix; + } + + @Override + public void encodeChunk(HttpObject msg, ByteBuf out) throws CryptoException { + ByteBuf binaryHttpBytes = out.alloc().buffer(); + try { + boolean isFinal = msg instanceof LastHttpContent; + binaryHttpSerializer.serialize(msg, binaryHttpBytes); + encryptChunk(binaryHttpBytes, binaryHttpBytes.readableBytes(), isFinal, out); + } finally { + binaryHttpBytes.release(); + } + } + + @Override + public void encodePrefix(ByteBuf out) throws CryptoException { + if (encodedPrefix) { + throw new IllegalStateException("Prefix already encoded"); + } + encodePrefixNow(out); + encodedPrefix = true; + } + } + + /** * Parse OHTTP-encoded HTTP content bytes. *
@@ -51,12 +117,12 @@ abstract class OHttpContentParser { * @param completeBodyReceived true if there are no more bytes following in. * @param out List that produced {@link HttpObject} are appended to. */ - public final void parse(ByteBuf in, boolean completeBodyReceived, List out) throws CryptoException { + final void parse(ByteBuf in, boolean completeBodyReceived, List out) throws CryptoException { if (decoder == null) { throw new IllegalStateException("Already destroyed"); } - framer.parse(in, completeBodyReceived, decoder, out); + version.parse(in, completeBodyReceived, decoder, out); if (completeBodyReceived && in.isReadable()) { throw new CorruptedFrameException("OHTTP stream has extra bytes"); @@ -80,9 +146,9 @@ public final void parse(ByteBuf in, boolean completeBodyReceived, List o protected abstract void decryptChunk(ByteBuf chunk, int chunkLength, boolean isFinal, ByteBuf out) throws CryptoException; /** - * Must be called once the {@link OHttpContentParser} will not be used anymore. + * Must be called once the {@link OHttpRequestResponseContext} will not be used anymore. */ - public void destroy() { + final void destroy() { if (decoder != null) { decoder.destroy(); decoder = null; @@ -109,7 +175,7 @@ public boolean decodePrefix(ByteBuf in) { if (decodedPrefix) { throw new IllegalStateException("Prefix already decoded"); } - if (OHttpContentParser.this.decodePrefix(in)) { + if (OHttpRequestResponseContext.this.decodePrefix(in)) { decodedPrefix = true; return true; } diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpServerCodec.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpServerCodec.java index 1ec9a20..3fd3525 100644 --- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpServerCodec.java +++ b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpServerCodec.java @@ -38,11 +38,13 @@ import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; +import io.netty.incubator.codec.hpke.HybridPublicKeyEncryption; import io.netty.util.ReferenceCountUtil; import java.util.List; import static io.netty.handler.codec.ByteToMessageDecoder.MERGE_CUMULATOR; +import static java.util.Objects.requireNonNull; /** * {@link MessageToMessageCodec} that HTTP servers can use to decrypt incoming @@ -51,36 +53,20 @@ *

* Both incoming and outgoing messages are {@link HttpObject}s. */ -public abstract class OHttpServerCodec extends MessageToMessageCodec { +public class OHttpServerCodec extends MessageToMessageCodec { + + private final HybridPublicKeyEncryption encryption; + private final OHttpServerKeys serverKeys; - private OHttpServerContext context; private HttpRequest request; private boolean sentResponse; - private OHttpContentSerializer serializer; - private OHttpContentParser parser; + private OHttpRequestResponseContext oHttpContext; private ByteBuf cumulationBuffer = Unpooled.EMPTY_BUFFER; private boolean destroyed; - /** - * Create a {@link OHttpServerContext} to handle an inbound OHTTP request. - *
- * @param request inbound {@link HttpRequest}. - * @param version {@link OHttpVersion} inferred from the Content-Type header. - * @return {@link OHttpServerContext} instance, or null to reject the request with a 403 error. - */ - protected abstract OHttpServerContext newServerContext(HttpRequest request, OHttpVersion version); - - /** - * Optional callback to report the outer HTTP request and response. - * @param request Incoming {@link HttpRequest}. - * @param response Outgoing {@link HttpResponse}. - */ - protected void onResponse(HttpRequest request, HttpResponse response) { - } - - @Override - public final boolean isSharable() { - return false; + public OHttpServerCodec(HybridPublicKeyEncryption encryption, OHttpServerKeys serverKeys) { + this.encryption = requireNonNull(encryption, "encryption"); + this.serverKeys = requireNonNull(serverKeys, "serverKeys"); } /** @@ -90,7 +76,7 @@ public final boolean isSharable() { * @param contentTypeValue the value of the content-type header. * @return the version or {@code null} if none could be selected. */ - protected OHttpVersion selectOHttpVersion(String contentTypeValue) { + protected OHttpVersion selectVersion(String contentTypeValue) { if (OHttpConstants.REQUEST_CONTENT_TYPE.contentEqualsIgnoreCase(contentTypeValue)) { return OHttpVersionDraft.INSTANCE; } @@ -100,6 +86,20 @@ protected OHttpVersion selectOHttpVersion(String contentTypeValue) { return null; } + /** + * Optional callback to report the outer HTTP request and response. + * @param request Incoming {@link HttpRequest}. + * @param response Outgoing {@link HttpResponse}. + */ + protected void onResponse(HttpRequest request, HttpResponse response) { + // TODO: Do we need this ? + } + + @Override + public final boolean isSharable() { + return false; + } + @Override protected final void decode(ChannelHandlerContext ctx, HttpObject msg, List out) { if (destroyed) { @@ -108,19 +108,28 @@ protected final void decode(ChannelHandlerContext ctx, HttpObject msg, List out) { try { if (msg instanceof HttpResponse) { - serializer = null; - if (context != null) { + if (oHttpContext != null) { assert request != null; - serializer = context.newContentSerializer(); sentResponse = true; HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - response.headers().set(HttpHeaderNames.CONTENT_TYPE, context.version().responseContentType()); + response.headers().set(HttpHeaderNames.CONTENT_TYPE, oHttpContext.version().responseContentType()); HttpUtil.setTransferEncodingChunked(response, true); HttpUtil.setKeepAlive(response, true); onResponse(request, response); out.add(response); } } - if (serializer != null) { + if (oHttpContext != null) { boolean isLast = msg instanceof LastHttpContent; ByteBuf contentBytes = ctx.alloc().buffer(); - serializer.serialize(msg, contentBytes); + oHttpContext.serialize(msg, contentBytes); // Use the correct version of HttpContent depending on if it was the last or not. HttpContent content = isLast ? new DefaultLastHttpContent(contentBytes) : new DefaultHttpContent(contentBytes); out.add(content); + if (isLast) { + destroyContext(); + } } else { // Retain the msg as MessageToMessageEncoder will release on it. out.add(ReferenceCountUtil.retain(msg)); @@ -202,10 +212,81 @@ public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception { cumulationBuffer.release(); cumulationBuffer = Unpooled.EMPTY_BUFFER; - if (parser != null) { - parser.destroy(); - } + destroyContext(); } super.handlerRemoved(ctx); } + + private void destroyContext() { + if (oHttpContext != null) { + oHttpContext.destroy(); + oHttpContext = null; + } + } + + private static final class OHttpServerRequestResponseContext extends OHttpRequestResponseContext { + + private final HybridPublicKeyEncryption encryption; + private final OHttpServerKeys keys; + + private OHttpCryptoReceiver receiver; + + public OHttpServerRequestResponseContext( + OHttpVersion version, HybridPublicKeyEncryption encryption, OHttpServerKeys keys) { + super(version); + this.encryption = encryption; + this.keys = keys; + } + + private void checkPrefixDecoded()throws CryptoException { + if (receiver == null) { + throw new CryptoException("Prefix was not decoded yet"); + } + } + + @Override + public boolean decodePrefix(ByteBuf in) { + final int initialReaderIndex = in.readerIndex(); + final OHttpCiphersuite ciphersuite = OHttpCiphersuite.decode(in); + if (ciphersuite == null) { + return false; + } + final int encapsulatedKeyLength = ciphersuite.encapsulatedKeyLength(); + if (in.readableBytes() < encapsulatedKeyLength) { + in.readerIndex(initialReaderIndex); + return false; + } + final byte[] encapsulatedKey = new byte[encapsulatedKeyLength]; + in.readBytes(encapsulatedKey); + receiver = OHttpCryptoReceiver.newBuilder() + .setHybridPublicKeyEncryption(encryption) + .setConfiguration(version()) + .setServerKeys(keys) + .setCiphersuite(ciphersuite) + .setEncapsulatedKey(encapsulatedKey) + .build(); + return true; + } + + @Override + protected void decryptChunk(ByteBuf chunk, int chunkSize, boolean isFinal, ByteBuf out) + throws CryptoException { + checkPrefixDecoded(); + receiver.decrypt(chunk, chunkSize, isFinal, out); + } + + + @Override + public void encodePrefixNow(ByteBuf out) throws CryptoException { + checkPrefixDecoded(); + out.writeBytes(receiver.responseNonce()); + } + + @Override + protected void encryptChunk(ByteBuf chunk, int chunkLength, boolean isFinal, ByteBuf out) + throws CryptoException { + checkPrefixDecoded(); + receiver.encrypt(chunk, chunkLength, isFinal, out); + } + } } diff --git a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpServerContext.java b/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpServerContext.java deleted file mode 100644 index 5f1bf9d..0000000 --- a/codec-ohttp/src/main/java/io/netty/incubator/codec/ohttp/OHttpServerContext.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2023 The Netty Project - * - * The Netty Project licenses this file to you 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: - * - * https://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 io.netty.incubator.codec.ohttp; - -import io.netty.incubator.codec.hpke.CryptoException; -import io.netty.buffer.ByteBuf; -import io.netty.incubator.codec.hpke.HybridPublicKeyEncryption; - -import static java.util.Objects.requireNonNull; - -/** - * Context for a OHTTP server. - */ -public final class OHttpServerContext { - - private final OHttpServerKeys serverKeys; - private final OHttpVersion version; - private final HybridPublicKeyEncryption encryption; - private OHttpCryptoReceiver receiver; - - /** - * @return {@link OHttpVersion}. - */ - OHttpVersion version() { - return version; - } - - private void checkPrefixDecoded()throws CryptoException { - if (receiver == null) { - throw new CryptoException("Prefix was not decoded yet"); - } - } - - /** - * Create {@link OHttpContentParser} for the {@link OHttpServerContext}. - * @return {@link OHttpContentParser}. - */ - OHttpContentParser newContentParser() { - return new OHttpContentParser(version) { - - @Override - public boolean decodePrefix(ByteBuf in) { - final int initialReaderIndex = in.readerIndex(); - final OHttpCiphersuite ciphersuite = OHttpCiphersuite.decode(in); - if (ciphersuite == null) { - return false; - } - final int encapsulatedKeyLength = ciphersuite.encapsulatedKeyLength(); - if (in.readableBytes() < encapsulatedKeyLength) { - in.readerIndex(initialReaderIndex); - return false; - } - final byte[] encapsulatedKey = new byte[encapsulatedKeyLength]; - in.readBytes(encapsulatedKey); - receiver = OHttpCryptoReceiver.newBuilder() - .setHybridPublicKeyEncryption(encryption) - .setConfiguration(version) - .setServerKeys(serverKeys) - .setCiphersuite(ciphersuite) - .setEncapsulatedKey(encapsulatedKey) - .build(); - return true; - } - - @Override - protected void decryptChunk(ByteBuf chunk, int chunkSize, boolean isFinal, ByteBuf out) - throws CryptoException { - checkPrefixDecoded(); - receiver.decrypt(chunk, chunkSize, isFinal, out); - } - }; - } - - /** - * Create {@link OHttpContentSerializer} for the {@link OHttpServerContext}. - * @return {@link OHttpContentSerializer}. - */ - OHttpContentSerializer newContentSerializer() { - return new OHttpContentSerializer(version) { - - @Override - public void encodePrefixNow(ByteBuf out) throws CryptoException { - checkPrefixDecoded(); - out.writeBytes(receiver.responseNonce()); - } - - @Override - protected void encryptChunk(ByteBuf chunk, int chunkLength, boolean isFinal, ByteBuf out) - throws CryptoException { - checkPrefixDecoded(); - receiver.encrypt(chunk, chunkLength, isFinal, out); - } - }; - } - - /** - * @param serverKeys set of key private keys and supported crypto algorithms. - * @param version {@link OHttpVersion}. - */ - public OHttpServerContext(OHttpServerKeys serverKeys, OHttpVersion version, HybridPublicKeyEncryption encryption) { - this.serverKeys = requireNonNull(serverKeys, "serverKeys"); - this.version = requireNonNull(version,"version"); - this.encryption = requireNonNull(encryption, "encryption"); - } -} diff --git a/codec-ohttp/src/test/java/io/netty/incubator/codec/ohttp/OHttpCodecsTest.java b/codec-ohttp/src/test/java/io/netty/incubator/codec/ohttp/OHttpCodecsTest.java index 9599765..f2bfd8a 100644 --- a/codec-ohttp/src/test/java/io/netty/incubator/codec/ohttp/OHttpCodecsTest.java +++ b/codec-ohttp/src/test/java/io/netty/incubator/codec/ohttp/OHttpCodecsTest.java @@ -35,7 +35,6 @@ import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpUtil; @@ -109,8 +108,6 @@ public static ChannelPair createChannelPair(OHttpVersion version) throws Excepti OHttpKey.newCipher(HybridPublicKeyEncryption.KDF.HKDF_SHA256, HybridPublicKeyEncryption.AEAD.CHACHA20_POLY1305)), kpR)); - OHttpServerContext serverContext = new OHttpServerContext(serverKeys, version, BouncyCastleHybridPublicKeyEncryption.INSTANCE); - OHttpCiphersuite ciphersuite = new OHttpCiphersuite(keyId, HybridPublicKeyEncryption.KEM.X25519_SHA256, HybridPublicKeyEncryption.KDF.HKDF_SHA256, @@ -118,62 +115,36 @@ public static ChannelPair createChannelPair(OHttpVersion version) throws Excepti byte[] publicKeyBytes = kpR.publicParameters().encoded(); - OHttpClientContext clientContext = new OHttpClientContext( - version, ciphersuite, publicKeyBytes, BouncyCastleHybridPublicKeyEncryption.INSTANCE); - return new ChannelPair() { @Override public EmbeddedChannel client() { - return createClientChannel(clientContext); + return createClientChannel(version, ciphersuite, publicKeyBytes); } @Override public EmbeddedChannel server() { - return createServerChannel(serverContext); + return createServerChannel(version, serverKeys); } }; } - private static EmbeddedChannel createClientChannel(OHttpClientContext context) { + private static EmbeddedChannel createClientChannel(OHttpVersion version, OHttpCiphersuite ciphersuite, byte[] publicKeyBytes) { return new EmbeddedChannel( new LoggingHandler("CLIENT-RAW"), new HttpClientCodec(), new LoggingHandler("CLIENT-OUTER"), - new OHttpClientCodec() { - @Override - protected EncapsulationParameters encapsulationParameters(HttpRequest request) { - return new EncapsulationParameters() { - @Override - public String outerRequestUri() { - return "/ohttp"; - } - - @Override - public String outerRequestAuthority() { - return "authority"; - } - - @Override - public OHttpClientContext context() { - return context; - } - }; - } - }, + new OHttpClientCodec(BouncyCastleHybridPublicKeyEncryption.INSTANCE, + __ -> OHttpClientCodec.EncapsulationParameters.newInstance(version, ciphersuite, publicKeyBytes, + "/ohttp", "autority")), new LoggingHandler("CLIENT-INNER")); } - private static EmbeddedChannel createServerChannel(OHttpServerContext context) { + private static EmbeddedChannel createServerChannel(OHttpVersion version, OHttpServerKeys keys) { return new EmbeddedChannel( new LoggingHandler("SERVER-RAW"), new HttpServerCodec(), new LoggingHandler("SERVER-OUTER"), - new OHttpServerCodec() { - @Override - protected OHttpServerContext newServerContext(HttpRequest request, OHttpVersion version) { - return context; - } - }, + new OHttpServerCodec(BouncyCastleHybridPublicKeyEncryption.INSTANCE, keys), new LoggingHandler("SERVER-INNER")); }