Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more peer test #191

Merged
merged 8 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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