Skip to content

Commit

Permalink
Split crypto related interfaces
Browse files Browse the repository at this point in the history
Motivation:

When setup the HPKE context you either set it up for encryption or decryption. We should change our interfaces to reflect this and so guard the user from miss-usage.

Modifications:

Introduce sub-types to support either encryption or decryption

Result:

Guard users from incorrect setup and usage of context
  • Loading branch information
normanmaurer committed Dec 20, 2023
1 parent 6aa8909 commit 6dd4040
Show file tree
Hide file tree
Showing 15 changed files with 204 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
* {@link CryptoContext} implementation of
* <a href="https://datatracker.ietf.org/doc/html/rfc5116">AEAD encryption algorithm [RFC5116]</a>.
*/
public interface AEADContext extends CryptoContext {
public interface AEADContext extends CryptoDecryptContext, CryptoEncryptContext {
// TODO: Move some methods in here.
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,11 @@
*/
package io.netty.incubator.codec.hpke;

import io.netty.buffer.ByteBuf;

/**
* Cryptographic operations to encrypt and decrypt data.
* Context for Cryptographic operations.
*/
public interface CryptoContext extends AutoCloseable {

/**
* Authenticate and encrypt data. The {@link ByteBuf#readerIndex()} will be increased by the amount of
* data read and {@link ByteBuf#writerIndex()} by the bytes written.
*
* @param aad the AAD buffer
* @param pt the data to encrypt.
* @param out the buffer for writing into
* @throws CryptoException in case of an error.
*/
void seal(ByteBuf aad, ByteBuf pt, ByteBuf out) throws CryptoException;

/**
* Authenticate and decrypt data. The {@link ByteBuf#readerIndex()} will be increased by the amount of
* data read and {@link ByteBuf#writerIndex()} by the bytes written.
*
* @param aad the AAD buffer
* @param ct the data to decrypt
* @param out the buffer for writing into.
* @throws CryptoException in case of an error.
*/
void open(ByteBuf aad, ByteBuf ct, ByteBuf out) throws CryptoException;

/**
* Closes the {@link CryptoContext} and so release all resources. Calling any method after calling this method
* might result in an {@link IllegalStateException}.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.hpke;

import io.netty.buffer.ByteBuf;

/**
* {@link CryptoContext} that can be used for decryption.
*/
public interface CryptoDecryptContext extends CryptoContext {

/**
* Authenticate and decrypt data. The {@link ByteBuf#readerIndex()} will be increased by the amount of
* data read and {@link ByteBuf#writerIndex()} by the bytes written.
*
* @param aad the AAD buffer
* @param ct the data to decrypt
* @param out the buffer for writing into.
* @throws CryptoException in case of an error.
*/
void open(ByteBuf aad, ByteBuf ct, ByteBuf out) throws CryptoException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.hpke;

import io.netty.buffer.ByteBuf;

/**
* {@link CryptoContext} that can be used for encryption.
*/
public interface CryptoEncryptContext extends CryptoContext {

/**
* Authenticate and encrypt data. The {@link ByteBuf#readerIndex()} will be increased by the amount of
* data read and {@link ByteBuf#writerIndex()} by the bytes written.
*
* @param aad the AAD buffer
* @param pt the data to encrypt.
* @param out the buffer for writing into
* @throws CryptoException in case of an error.
*/
void seal(ByteBuf aad, ByteBuf pt, ByteBuf out) throws CryptoException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,10 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.incubator.codec.hpke.bouncycastle;
package io.netty.incubator.codec.hpke;


import org.bouncycastle.crypto.hpke.HPKEContextWithEncapsulation;

final class BouncyCastleHPKEContextWithEncapsulation extends BouncyCastleHPKEContext
implements io.netty.incubator.codec.hpke.HPKEContextWithEncapsulation {

BouncyCastleHPKEContextWithEncapsulation(HPKEContextWithEncapsulation context) {
super(context);
}

@Override
public byte[] encapsulation() {
return ((HPKEContextWithEncapsulation) context).getEncapsulation();
}
/**
* {@link HPKEContext} that can be used for decryption.
*/
public interface HPKERecipientContext extends HPKEContext, CryptoDecryptContext {
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
*/
package io.netty.incubator.codec.hpke;


/**
* A {@link HPKEContext} which uses encapsulation.
* {@link HPKEContext} that can be used for encryption.
*/
public interface HPKEContextWithEncapsulation extends HPKEContext {

public interface HPKESenderContext extends HPKEContext, CryptoEncryptContext {
/**
* Return the bytes that are used for encapsulation.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public interface OHttpCryptoProvider {
AEADContext setupAEAD(AEAD aead, byte[] key, byte[] baseNonce);

/**
* Establish a {@link HPKEContextWithEncapsulation} that can be used for encryption.
* Establish a {@link HPKESenderContext} that can be used for encryption.
*
* @param mode the {@link Mode} to use.
* @param kem the {@link KEM} to use.
Expand All @@ -43,11 +43,11 @@ public interface OHttpCryptoProvider {
* @param kpE the ephemeral keypair or {@code null} if none should be used.
* @return the context.
*/
HPKEContextWithEncapsulation setupHPKEBaseS(Mode mode, KEM kem, KDF kdf, AEAD aead,
HPKESenderContext setupHPKEBaseS(Mode mode, KEM kem, KDF kdf, AEAD aead,
AsymmetricKeyParameter pkR, byte[] info, AsymmetricCipherKeyPair kpE);

/**
* Establish a {@link HPKEContext} that can be used for decryption.
* Establish a {@link HPKERecipientContext} that can be used for decryption.
*
* @param mode the {@link Mode} to use.
* @param kem the {@link KEM} to use.
Expand All @@ -58,7 +58,7 @@ HPKEContextWithEncapsulation setupHPKEBaseS(Mode mode, KEM kem, KDF kdf, AEAD ae
* @param info info parameter.
* @return the context.
*/
HPKEContext setupHPKEBaseR(Mode mode, KEM kem, KDF kdf, AEAD aead, byte[] enc,
HPKERecipientContext setupHPKEBaseR(Mode mode, KEM kem, KDF kdf, AEAD aead, byte[] enc,
AsymmetricCipherKeyPair skR, byte[] info);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,12 @@

import java.nio.ByteBuffer;

class BouncyCastleHPKEContext implements HPKEContext {
abstract class BouncyCastleHPKEContext implements HPKEContext {
protected final org.bouncycastle.crypto.hpke.HPKEContext context;
private final BouncyCastleCryptoOperation seal;
private final BouncyCastleCryptoOperation open;

private boolean closed;

BouncyCastleHPKEContext(org.bouncycastle.crypto.hpke.HPKEContext context) {
this.context = context;
this.seal = new BouncyCastleCryptoOperation() {
@Override
protected byte[] execute(byte[] arg1, byte[] arg2, int offset2, int length2) throws InvalidCipherTextException {
return context.seal(arg1, arg2, offset2, length2);
}
};
this.open = new BouncyCastleCryptoOperation() {
@Override
protected byte[] execute(byte[] arg1, byte[] arg2, int offset2, int length2) throws InvalidCipherTextException {
return context.open(arg1, arg2, offset2, length2);
}
};
}

@Override
Expand All @@ -63,19 +48,7 @@ public byte[] expand(byte[] prk, byte[] info, int length) {
return context.expand(prk, info, length);
}

@Override
public void seal(ByteBuf aad, ByteBuf pt, ByteBuf out) throws CryptoException {
checkClosed();
seal.execute(aad, pt, out);
}

@Override
public void open(ByteBuf aad, ByteBuf ct, ByteBuf out) throws CryptoException {
checkClosed();
open.execute(aad, ct, out);
}

private void checkClosed() {
protected void checkClosed() {
if (closed) {
throw new IllegalStateException("AEADContext closed");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.hpke.bouncycastle;

import io.netty.buffer.ByteBuf;
import io.netty.incubator.codec.hpke.CryptoException;
import io.netty.incubator.codec.hpke.HPKERecipientContext;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.hpke.HPKEContext;

final class BouncyCastleHPKERecipientContext extends BouncyCastleHPKEContext implements HPKERecipientContext {
private final BouncyCastleCryptoOperation open;

BouncyCastleHPKERecipientContext(HPKEContext context) {
super(context);
open = new BouncyCastleCryptoOperation() {
@Override
protected byte[] execute(byte[] arg1, byte[] arg2, int offset2, int length2) throws InvalidCipherTextException {
return context.open(arg1, arg2, offset2, length2);
}
};
}

@Override
public void open(ByteBuf aad, ByteBuf ct, ByteBuf out) throws CryptoException {
checkClosed();
open.execute(aad, ct, out);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.hpke.bouncycastle;

import io.netty.buffer.ByteBuf;
import io.netty.incubator.codec.hpke.CryptoException;
import io.netty.incubator.codec.hpke.HPKESenderContext;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.hpke.HPKEContextWithEncapsulation;

final class BouncyCastleHPKESenderContext extends BouncyCastleHPKEContext implements HPKESenderContext {

private final BouncyCastleCryptoOperation seal;
public BouncyCastleHPKESenderContext(HPKEContextWithEncapsulation context) {
super(context);
this.seal = new BouncyCastleCryptoOperation() {
@Override
protected byte[] execute(byte[] arg1, byte[] arg2, int offset2, int length2)
throws InvalidCipherTextException {
return context.seal(arg1, arg2, offset2, length2);
}
};
}

@Override
public byte[] encapsulation() {
return ((HPKEContextWithEncapsulation) context).getEncapsulation();
}

@Override
public void seal(ByteBuf aad, ByteBuf pt, ByteBuf out) throws CryptoException {
checkClosed();
seal.execute(aad, pt, out);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
import io.netty.incubator.codec.hpke.AEADContext;
import io.netty.incubator.codec.hpke.AsymmetricCipherKeyPair;
import io.netty.incubator.codec.hpke.AsymmetricKeyParameter;
import io.netty.incubator.codec.hpke.HPKEContext;
import io.netty.incubator.codec.hpke.HPKEContextWithEncapsulation;
import io.netty.incubator.codec.hpke.HPKERecipientContext;
import io.netty.incubator.codec.hpke.HPKESenderContext;
import io.netty.incubator.codec.hpke.OHttpCryptoProvider;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
Expand Down Expand Up @@ -62,7 +62,7 @@ private static BouncyCastleAsymmetricCipherKeyPair castOrThrow(AsymmetricCipherK
}

@Override
public HPKEContextWithEncapsulation setupHPKEBaseS(Mode mode, KEM kem, KDF kdf, AEAD aead,
public HPKESenderContext setupHPKEBaseS(Mode mode, KEM kem, KDF kdf, AEAD aead,
AsymmetricKeyParameter pkR, byte[] info,
AsymmetricCipherKeyPair kpE) {
org.bouncycastle.crypto.hpke.HPKE hpke =
Expand All @@ -73,15 +73,15 @@ public HPKEContextWithEncapsulation setupHPKEBaseS(Mode mode, KEM kem, KDF kdf,
} else {
ctx = hpke.setupBaseS(castOrThrow(pkR).param, info, castOrThrow(kpE).pair);
}
return new BouncyCastleHPKEContextWithEncapsulation(ctx);
return new BouncyCastleHPKESenderContext(ctx);
}

@Override
public HPKEContext setupHPKEBaseR(Mode mode, KEM kem, KDF kdf, AEAD aead, byte[] enc,
AsymmetricCipherKeyPair skR, byte[] info) {
public HPKERecipientContext setupHPKEBaseR(Mode mode, KEM kem, KDF kdf, AEAD aead, byte[] enc,
AsymmetricCipherKeyPair skR, byte[] info) {
org.bouncycastle.crypto.hpke.HPKE hpke =
new org.bouncycastle.crypto.hpke.HPKE(mode.value(), kem.id(), kdf.id(), aead.id());
return new BouncyCastleHPKEContext(hpke.setupBaseR(enc, castOrThrow(skR).pair, info));
return new BouncyCastleHPKERecipientContext(hpke.setupBaseR(enc, castOrThrow(skR).pair, info));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package io.netty.incubator.codec.ohttp;

import io.netty.incubator.codec.hpke.AEADContext;
import io.netty.incubator.codec.hpke.CryptoContext;
import io.netty.incubator.codec.hpke.HPKEContext;
import io.netty.incubator.codec.hpke.OHttpCryptoProvider;
Expand Down Expand Up @@ -125,8 +126,8 @@ byte[] createResponseNonce() {
/*
* See https://ietf-wg-ohai.github.io/oblivious-http/draft-ietf-ohai-ohttp.html#name-encapsulation-of-responses
*/
CryptoContext createResponseAead(OHttpCryptoProvider provider, HPKEContext context, byte[] enc,
byte[] responseNonce, OHttpCryptoConfiguration configuration) {
AEADContext createResponseAead(OHttpCryptoProvider provider, HPKEContext context, byte[] enc,
byte[] responseNonce, OHttpCryptoConfiguration configuration) {
int secretLength = Math.max(aead.nk(), aead.nn());
byte[] secret = context.export(configuration.responseExportContext(), secretLength);
byte[] salt = Arrays.concatenate(enc, responseNonce);
Expand Down
Loading

0 comments on commit 6dd4040

Please sign in to comment.