Skip to content

Commit

Permalink
more peer test (#191)
Browse files Browse the repository at this point in the history
* update

* update netaddr test

* update peer test

* update peer test

* update test

* update peer
  • Loading branch information
MacOMNI authored Oct 23, 2024
1 parent 6b754db commit 86e32b5
Show file tree
Hide file tree
Showing 9 changed files with 341 additions and 23 deletions.
83 changes: 83 additions & 0 deletions Networking/Sources/CHelpers/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,89 @@

#include "helpers.h"

int parse_certificate(
const unsigned char *data,
size_t length,
unsigned char **public_key,
size_t *public_key_len,
char **alt_name,
char **error_message)
{
int ret = -1;
BIO *bio = NULL;
X509 *cert = NULL;
STACK_OF(GENERAL_NAME) *alt_names = NULL;

// Create BIO from certificate data
bio = BIO_new_mem_buf(data, (int)length);
if (!bio) {
*error_message = "Failed to create BIO.";
goto cleanup;
}

// Parse the X509 certificate from the BIO
cert = d2i_X509_bio(bio, NULL);
if (!cert) {
*error_message = "Failed to parse X509 certificate.";
goto cleanup;
}

// Extract the public key from the certificate
EVP_PKEY *pkey = X509_get_pubkey(cert);
if (!pkey) {
*error_message = "Failed to get public key from certificate.";
goto cleanup;
}

// Get the raw public key
if (EVP_PKEY_get_raw_public_key(pkey, NULL, public_key_len) <= 0) {
*error_message = "Failed to get public key length.";
goto cleanup;
}
*public_key = (unsigned char *)malloc(*public_key_len);
if (!*public_key) {
*error_message = "Failed to allocate memory for public key.";
goto cleanup;
}

if (EVP_PKEY_get_raw_public_key(pkey, *public_key, public_key_len) <= 0) {
*error_message = "Failed to extract public key.";
goto cleanup;
}

// Extract the alternative name from the certificate (if present)
alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
if (alt_names) {
for (int i = 0; i < sk_GENERAL_NAME_num(alt_names); i++) {
GENERAL_NAME *gen_name = sk_GENERAL_NAME_value(alt_names, i);
if (gen_name->type == GEN_DNS) {
ASN1_STRING *name = gen_name->d.dNSName;
*alt_name = strdup((char *)ASN1_STRING_get0_data(name));
break;
}
}
sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
}

if (!*alt_name) {
*error_message = "No alternative name found.";
goto cleanup;
}

ret = EXIT_SUCCESS; // Success

cleanup:
if (ret != 0 && *public_key) {
free(*public_key);
*public_key = NULL;
}
BIO_free(bio);
EVP_PKEY_free(pkey);
X509_free(cert);

return ret;
}

// Function to parse certificate and extract public key and alternative name
int parse_pkcs12_certificate(
const unsigned char *data,
Expand Down
10 changes: 9 additions & 1 deletion Networking/Sources/CHelpers/helpers.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
#include <arpa/inet.h>
#include <stddef.h>

int parse_certificate(
const unsigned char *data,
size_t length,
unsigned char **public_key,
size_t *public_key_len,
char **alt_name,
char **error_message);

int parse_pkcs12_certificate(
const unsigned char *data,
size_t length,
Expand All @@ -21,4 +29,4 @@ char *get_error_string(int error);
static inline uint16_t helper_ntohs(in_port_t netport)
{
return ntohs(netport);
}
}
4 changes: 2 additions & 2 deletions Networking/Sources/MsQuicSwift/NetAddr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private func parseQuicAddr(_ addr: QUIC_ADDR) -> (String, UInt16, Bool)? {
private func parseIpv4Addr(_ address: String) -> (String, UInt16)? {
let ipv4Pattern =
#"((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"#
let ipv4WithPortPattern = #"(\#(ipv4Pattern)):(\d{1,5})"#
let ipv4WithPortPattern = #"(\#(ipv4Pattern)):(\d{1,5})(?=\s*$|\s+)"#

let regex = try? NSRegularExpression(pattern: ipv4WithPortPattern, options: [])
let range = NSRange(location: 0, length: address.utf16.count)
Expand Down Expand Up @@ -142,7 +142,7 @@ private func parseIpv6Addr(_ address: String) -> (String, UInt16)? {
"|(?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::",
")",
].reduce("", +)
let ipv6WithPortPattern = #"\[(\#(ipv6Pattern))\]:(\d{1,5})"#
let ipv6WithPortPattern = #"\[(\#(ipv6Pattern))\]:(\d{1,5})(?=\s*$|\s+)"#

let regex = try? NSRegularExpression(pattern: ipv6WithPortPattern, options: [])
let range = NSRange(location: 0, length: address.utf16.count)
Expand Down
45 changes: 34 additions & 11 deletions Networking/Sources/Networking/PKCS12.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,45 @@ enum CryptoError: Error {
case parseFailed(String)
}

public func parseCertificate(data: Data) throws -> (publicKey: Data, alternativeName: String) {
public enum CertificateType {
case x509
case p12
}

public func parseCertificate(data: Data, type: CertificateType) throws -> (
publicKey: Data, alternativeName: String
) {
var publicKeyPointer: UnsafeMutablePointer<UInt8>!
var publicKeyLen = 0
var altNamePointer: UnsafeMutablePointer<Int8>!
var errorMessage: UnsafeMutablePointer<Int8>?
defer { free(altNamePointer) }
let result = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
parse_pkcs12_certificate(
bytes.baseAddress!.assumingMemoryBound(to: UInt8.self),
data.count,
&publicKeyPointer,
&publicKeyLen,
&altNamePointer,
&errorMessage
)
}

let result: Int32 =
switch type {
case .x509:
data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
parse_certificate(
bytes.baseAddress!.assumingMemoryBound(to: UInt8.self),
data.count,
&publicKeyPointer,
&publicKeyLen,
&altNamePointer,
&errorMessage
)
}
case .p12:
data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
parse_pkcs12_certificate(
bytes.baseAddress!.assumingMemoryBound(to: UInt8.self),
data.count,
&publicKeyPointer,
&publicKeyLen,
&altNamePointer,
&errorMessage
)
}
}

guard result == 0 else {
throw CryptoError.parseFailed(String(cString: errorMessage!))
Expand Down
4 changes: 2 additions & 2 deletions Networking/Sources/Networking/Peer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ private struct PeerEventHandler<Handler: StreamHandler>: QuicEventHandler {
return .code(.requiredCert)
}
do {
let (publicKey, alternativeName) = try parseCertificate(data: certificate)
let (publicKey, alternativeName) = try parseCertificate(data: certificate, type: .x509)
logger.debug(
"Certificate parsed",
metadata: ["publicKey": "\(publicKey.toHexString())", "alternativeName": "\(alternativeName)"]
Expand Down Expand Up @@ -385,7 +385,7 @@ public final class MockPeerEventHandler: QuicEventHandler {
return .code(.requiredCert)
}
do {
let (publicKey, alternativeName) = try parseCertificate(data: certificate)
let (publicKey, alternativeName) = try parseCertificate(data: certificate, type: .x509)
if alternativeName != generateSubjectAlternativeName(pubkey: publicKey) {
return .code(.badCert)
}
Expand Down
12 changes: 9 additions & 3 deletions Networking/Tests/MsQuicSwiftTests/NetAddrTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,15 @@ struct NetAddrTests {

@Test
func parseInvalidFormat() async throws {
let address = "abcd:::"
let netAddr = NetAddr(address: address)
#expect(netAddr == nil)
let address1 = "abcd:::"
let netAddr1 = NetAddr(address: address1)
#expect(netAddr1 == nil)
let address2 = "127.0.0.1:12,awef"
let netAddr2 = NetAddr(address: address2)
#expect(netAddr2 == nil)
let address3 = "[2001:db8:85a3::8a2e:370:7334]:8080,8081,8082"
let netAddr3 = NetAddr(address: address3)
#expect(netAddr3 == nil)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Utils

@testable import Networking

final class PeerTests {
final class MockPeerEventTests {
let registration: QuicRegistration
let certData: Data
let badCertData: Data
Expand Down Expand Up @@ -98,6 +98,66 @@ final class PeerTests {
)
}

@Test
func connected() async throws {
let serverHandler = MockPeerEventHandler()
let clientHandler = MockPeerEventHandler()
let privateKey1 = try Ed25519.SecretKey(from: Data32())
let cert = try generateSelfSignedCertificate(privateKey: privateKey1)
let serverConfiguration = try QuicConfiguration(
registration: registration,
pkcs12: certData,
alpns: [Data("testalpn".utf8)],
client: false,
settings: QuicSettings.defaultSettings
)

let listener = try QuicListener(
handler: serverHandler,
registration: registration,
configuration: serverConfiguration,
listenAddress: NetAddr(ipAddress: "127.0.0.1", port: 0)!,
alpns: [Data("testalpn".utf8)]
)

let listenAddress = try listener.listenAddress()
// Client setup with certificate
let clientConfiguration = try QuicConfiguration(
registration: registration,
pkcs12: cert,
alpns: [Data("testalpn".utf8)],
client: true,
settings: QuicSettings.defaultSettings
)

let clientConnection = try QuicConnection(
handler: clientHandler,
registration: registration,
configuration: clientConfiguration
)

// Attempt to connect
try clientConnection.connect(to: listenAddress)
let stream1 = try clientConnection.createStream()
try stream1.send(data: Data("test data 1".utf8))

try? await Task.sleep(for: .milliseconds(100))
let (_, info) = serverHandler.events.value.compactMap {
switch $0 {
case let .newConnection(_, connection, info):
(connection, info) as (QuicConnection, ConnectionInfo)?
default:
nil
}
}.first!
let (ipAddress2, _) = info.remoteAddress.getAddressAndPort()

#expect(info.negotiatedAlpn == Data("testalpn".utf8))
#expect(info.serverName == "127.0.0.1")
#expect(info.localAddress == listenAddress)
#expect(ipAddress2 == "127.0.0.1")
}

@Test
func rejectsConDueToBadClientCert() async throws {
let serverHandler = MockPeerEventHandler()
Expand Down
6 changes: 3 additions & 3 deletions Networking/Tests/NetworkingTests/PKCS12Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import Utils
struct PKCS12Tests {
@Test func invalidParseCertificate() async throws {
#expect(throws: CryptoError.self) {
_ = try parseCertificate(data: Data("wrong cert data".utf8))
_ = try parseCertificate(data: Data("wrong cert data".utf8), type: .p12)
}
}

@Test func vailidParseCertificate() async throws {
@Test func vailidParseP12Certificate() async throws {
let privateKey = try Ed25519.SecretKey(from: Data32())
let cert = try generateSelfSignedCertificate(privateKey: privateKey)
let (publicKey, alternativeName) = try parseCertificate(data: cert)
let (publicKey, alternativeName) = try parseCertificate(data: cert, type: .p12)
#expect(alternativeName == generateSubjectAlternativeName(publicKey: privateKey.publicKey))
#expect(Data32(publicKey) == privateKey.publicKey.data)
}
Expand Down
Loading

0 comments on commit 86e32b5

Please sign in to comment.