#9397 add quiche_conn_peer_cert() API mapping

Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
Ludovic Orban 2023-05-16 14:38:44 +02:00
parent 07a1c124f2
commit b3b1d93152
7 changed files with 112 additions and 6 deletions

View File

@ -483,6 +483,31 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
}
}
public byte[] getPeerCertificate()
{
try (AutoLock ignore = lock.lock())
{
if (quicheConn == null)
throw new IllegalStateException("connection was released");
try (ResourceScope scope = ResourceScope.newConfinedScope())
{
MemorySegment outSegment = MemorySegment.allocateNative(CLinker.C_POINTER, scope);
MemorySegment outLenSegment = MemorySegment.allocateNative(CLinker.C_LONG, scope);
quiche_h.quiche_conn_peer_cert(quicheConn, outSegment.address(), outLenSegment.address());
long outLen = getLong(outLenSegment);
if (outLen == 0L)
return null;
byte[] out = new byte[(int)outLen];
// dereference outSegment pointer
MemoryAddress memoryAddress = MemoryAddress.ofLong(getLong(outSegment));
memoryAddress.asSegment(outLen, ResourceScope.globalScope()).asByteBuffer().get(out);
return out;
}
}
}
@Override
protected List<Long> iterableStreamIds(boolean write)
{

View File

@ -250,6 +250,12 @@ public class quiche_h
FunctionDescriptor.of(C_CHAR, C_POINTER)
);
private static final MethodHandle quiche_conn_peer_cert$MH = downcallHandle(
"quiche_conn_peer_cert",
"(Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;)V",
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_POINTER)
);
private static final MethodHandle quiche_conn_peer_error$MH = downcallHandle(
"quiche_conn_peer_error",
"(Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;)B",
@ -688,6 +694,18 @@ public class quiche_h
}
}
public static void quiche_conn_peer_cert(MemoryAddress conn, MemoryAddress out, MemoryAddress out_len)
{
try
{
quiche_conn_peer_cert$MH.invokeExact(conn, out, out_len);
}
catch (Throwable ex)
{
throw new AssertionError("should not reach here", ex);
}
}
public static byte quiche_conn_peer_error(MemoryAddress conn, MemoryAddress is_app, MemoryAddress error_code, MemoryAddress reason, MemoryAddress reason_len)
{
try

View File

@ -20,19 +20,19 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.eclipse.jetty.quic.quiche.QuicheConfig;
import org.eclipse.jetty.quic.quiche.QuicheConnection;
import org.eclipse.jetty.quic.quiche.SSLKeyPair;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -57,6 +57,7 @@ public class LowLevelQuicheTest
private QuicheConfig serverQuicheConfig;
private ForeignIncubatorQuicheConnection.TokenMinter tokenMinter;
private ForeignIncubatorQuicheConnection.TokenValidator tokenValidator;
private Certificate[] serverCertificateChain;
@BeforeEach
protected void setUp() throws Exception
@ -82,6 +83,7 @@ public class LowLevelQuicheTest
{
keyStore.load(is, "storepwd".toCharArray());
}
serverCertificateChain = keyStore.getCertificateChain("mykey");
SSLKeyPair serverKeyPair = new SSLKeyPair(keyStore, "mykey", "storepwd".toCharArray());
Path[] pemFiles = serverKeyPair.export(workDir.getEmptyPathDir());
serverQuicheConfig = new QuicheConfig();
@ -147,6 +149,11 @@ public class LowLevelQuicheTest
// assert that stream 0 is finished on server
assertThat(serverQuicheConnection.isStreamFinished(0), is(true));
// assert that the server certificate was correctly received by the client
byte[] peerCertificate = clientQuicheConnection.getPeerCertificate();
byte[] serverCert = serverCertificateChain[0].getEncoded();
assertThat(Arrays.equals(serverCert, peerCertificate), is(true));
}
@Test
@ -180,6 +187,11 @@ public class LowLevelQuicheTest
// assert that stream 0 is finished on server
assertThat(serverQuicheConnection.isStreamFinished(0), is(true));
// assert that the server certificate was correctly received by the client
byte[] peerCertificate = clientQuicheConnection.getPeerCertificate();
byte[] serverCert = serverCertificateChain[0].getEncoded();
assertThat(Arrays.equals(serverCert, peerCertificate), is(true));
}
@Test
@ -195,6 +207,11 @@ public class LowLevelQuicheTest
assertThat(clientQuicheConnection.getNegotiatedProtocol(), is(""));
assertThat(serverQuicheConnection.getNegotiatedProtocol(), is(""));
// assert that the server certificate was correctly received by the client
byte[] peerCertificate = clientQuicheConnection.getPeerCertificate();
byte[] serverCert = serverCertificateChain[0].getEncoded();
assertThat(Arrays.equals(serverCert, peerCertificate), is(true));
}
private void drainServerToFeedClient(Map.Entry<ForeignIncubatorQuicheConnection, ForeignIncubatorQuicheConnection> entry, int expectedSize) throws IOException

View File

@ -385,8 +385,29 @@ public class JnaQuicheConnection extends QuicheConnection
public void enableQlog(String filename, String title, String desc) throws IOException
{
if (!LibQuiche.INSTANCE.quiche_conn_set_qlog_path(quicheConn, filename, title, desc))
throw new IOException("unable to set qlog path to " + filename);
try (AutoLock ignore = lock.lock())
{
if (quicheConn == null)
throw new IllegalStateException("connection was released");
if (!LibQuiche.INSTANCE.quiche_conn_set_qlog_path(quicheConn, filename, title, desc))
throw new IOException("unable to set qlog path to " + filename);
}
}
public byte[] getPeerCertificate()
{
try (AutoLock ignore = lock.lock())
{
if (quicheConn == null)
throw new IllegalStateException("connection was released");
char_pointer out = new char_pointer();
size_t_pointer out_len = new size_t_pointer();
LibQuiche.INSTANCE.quiche_conn_peer_cert(quicheConn, out, out_len);
int len = out_len.getPointee().intValue();
return out.getValueAsBytes(len);
}
}
@Override

View File

@ -425,6 +425,9 @@ public interface LibQuiche extends Library
// Returns true if the connection was closed due to the idle timeout.
boolean quiche_conn_is_timed_out(quiche_conn conn);
// Returns the peer's leaf certificate (if any) as a DER-encoded buffer.
void quiche_conn_peer_cert(quiche_conn conn, char_pointer out, size_t_pointer out_len);
// Returns true if a connection error was received, and updates the provided
// parameters accordingly.
boolean quiche_conn_peer_error(quiche_conn conn,

View File

@ -23,4 +23,9 @@ public class char_pointer extends PointerByReference
{
return new String(getValue().getByteArray(0, len), charset);
}
public byte[] getValueAsBytes(int len)
{
return getValue().getByteArray(0, len);
}
}

View File

@ -19,19 +19,19 @@ import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.eclipse.jetty.quic.quiche.QuicheConfig;
import org.eclipse.jetty.quic.quiche.QuicheConnection;
import org.eclipse.jetty.quic.quiche.SSLKeyPair;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -56,6 +56,7 @@ public class LowLevelQuicheTest
private QuicheConfig serverQuicheConfig;
private JnaQuicheConnection.TokenMinter tokenMinter;
private JnaQuicheConnection.TokenValidator tokenValidator;
private Certificate[] serverCertificateChain;
@BeforeEach
protected void setUp() throws Exception
@ -81,6 +82,7 @@ public class LowLevelQuicheTest
{
keyStore.load(is, "storepwd".toCharArray());
}
serverCertificateChain = keyStore.getCertificateChain("mykey");
SSLKeyPair serverKeyPair = new SSLKeyPair(keyStore, "mykey", "storepwd".toCharArray());
Path[] pemFiles = serverKeyPair.export(workDir.getEmptyPathDir());
serverQuicheConfig = new QuicheConfig();
@ -146,6 +148,11 @@ public class LowLevelQuicheTest
// assert that stream 0 is finished on server
assertThat(serverQuicheConnection.isStreamFinished(0), is(true));
// assert that the server certificate was correctly received by the client
byte[] peerCertificate = clientQuicheConnection.getPeerCertificate();
byte[] serverCert = serverCertificateChain[0].getEncoded();
assertThat(Arrays.equals(serverCert, peerCertificate), is(true));
}
@Test
@ -179,6 +186,11 @@ public class LowLevelQuicheTest
// assert that stream 0 is finished on server
assertThat(serverQuicheConnection.isStreamFinished(0), is(true));
// assert that the server certificate was correctly received by the client
byte[] peerCertificate = clientQuicheConnection.getPeerCertificate();
byte[] serverCert = serverCertificateChain[0].getEncoded();
assertThat(Arrays.equals(serverCert, peerCertificate), is(true));
}
@Test
@ -194,6 +206,11 @@ public class LowLevelQuicheTest
assertThat(clientQuicheConnection.getNegotiatedProtocol(), is(""));
assertThat(serverQuicheConnection.getNegotiatedProtocol(), is(""));
// assert that the server certificate was correctly received by the client
byte[] peerCertificate = clientQuicheConnection.getPeerCertificate();
byte[] serverCert = serverCertificateChain[0].getEncoded();
assertThat(Arrays.equals(serverCert, peerCertificate), is(true));
}
private void drainServerToFeedClient(Map.Entry<JnaQuicheConnection, JnaQuicheConnection> entry, int expectedSize) throws IOException