Index: src/Makefile
==================================================================
--- src/Makefile
+++ src/Makefile
@@ -151,10 +151,11 @@
OFTLSStream.m \
OFTXTDNSResourceRecord.m \
OFUDPSocket.m \
OFURIDNSResourceRecord.m \
OFX509Certificate.m \
+ OFX509CertificatePrivateKey.m \
${USE_SRCS_APPLETALK} \
${USE_SRCS_IPX} \
${USE_SRCS_SCTP} \
${USE_SRCS_UNIX_SOCKETS}
SRCS_APPLETALK = OFDDPSocket.m
Index: src/OFTLSStream.h
==================================================================
--- src/OFTLSStream.h
+++ src/OFTLSStream.h
@@ -17,18 +17,19 @@
* .
*/
#import "OFStream.h"
#import "OFRunLoop.h"
+#import "OFX509Certificate.h"
+#import "OFX509CertificatePrivateKey.h"
OF_ASSUME_NONNULL_BEGIN
/** @file */
@class OFArray OF_GENERIC(ObjectType);
@class OFTLSStream;
-@class OFX509Certificate;
/**
* @brief An enum representing an error of an OFTLSStream.
*/
typedef enum {
@@ -100,12 +101,14 @@
OFReadyForWritingObserving>
{
OFStream
*_underlyingStream;
bool _verifiesCertificates;
- OFArray OF_GENERIC(OFX509Certificate *) *_Nullable _certificateChain;
- OF_RESERVE_IVARS(OFTLSStream, 3)
+ OFArray OF_GENERIC(OF_KINDOF(OFX509Certificate *)) *_Nullable
+ _certificateChain;
+ OF_KINDOF(OFX509CertificatePrivateKey *) _Nullable _privateKey;
+ OF_RESERVE_IVARS(OFTLSStream, 2)
}
/**
* @brief The underlying stream.
*/
@@ -130,10 +133,16 @@
* @brief The certificate chain to use.
*/
@property OF_NULLABLE_PROPERTY (copy, nonatomic)
OFArray OF_GENERIC(OFX509Certificate *) *certificateChain;
+/**
+ * @brief The private key to use.
+ */
+@property OF_NULLABLE_PROPERTY (retain, nonatomic)
+ OFX509CertificatePrivateKey *privateKey;
+
- (instancetype)init OF_UNAVAILABLE;
/**
* @brief Creates a new TLS stream with the specified stream as its underlying
* stream.
@@ -225,12 +234,12 @@
#endif
/**
* @brief The implementation for OFTLSStream to use.
*
* This can be set to a class that is always used for OFTLSStream. This is
- * useful to either force a specific implementation or use one that ObjFW does
- * not know about.
+ * useful to either force a specific implementation or to use one that ObjFW
+ * does not know about.
*/
extern Class OFTLSStreamImplementation;
/**
* @brief Returns a string description for the TLS stream error code.
Index: src/OFTLSStream.m
==================================================================
--- src/OFTLSStream.m
+++ src/OFTLSStream.m
@@ -95,10 +95,11 @@
@implementation OFTLSStream
@synthesize underlyingStream = _underlyingStream;
@dynamic delegate;
@synthesize verifiesCertificates = _verifiesCertificates;
+@synthesize privateKey = _privateKey;
+ (instancetype)alloc
{
if (self == [OFTLSStream class]) {
if (OFTLSStreamImplementation != Nil)
Index: src/OFX509Certificate.h
==================================================================
--- src/OFX509Certificate.h
+++ src/OFX509Certificate.h
@@ -19,17 +19,48 @@
#import "OFObject.h"
OF_ASSUME_NONNULL_BEGIN
+@class OFArray OF_GENERIC(ObjectType);
+@class OFIRI;
+
/**
* @class OFX509Certificate OFX509Certificate.h ObjFW/ObjFW.h
*
* @brief An X.509 certificate.
*/
@interface OFX509Certificate: OFObject
{
OF_RESERVE_IVARS(OFX509Certificate, 4)
}
+
+/**
+ * @brief Returns a certificate chain from the specified IRI.
+ *
+ * @param IRI The IRI to retrieve the certificate chain from
+ *
+ * @throw OFOpenItemFailedException Opening the item failed
+ * @throw OFUnsupportedProtocolException The specified IRI is not supported
+ * @throw OFReadFailedException Reading the item failed
+ * @throw OFInvalidFormatException The format of the item is invalid
+ */
++ (OFArray OF_GENERIC(OFX509Certificate *) *)
+ certificateChainFromIRI: (OFIRI *)IRI;
@end
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**
+ * @brief The implementation for OFX509Certificate to use.
+ *
+ * This can be set to a class that is always used for OFX509Certificate. This
+ * is useful to either force a specific implementation or to use one that ObjFW
+ * does not know about.
+ */
+extern Class OFX509CertificateImplementation;
+#ifdef __cplusplus
+}
+#endif
OF_ASSUME_NONNULL_END
Index: src/OFX509Certificate.m
==================================================================
--- src/OFX509Certificate.m
+++ src/OFX509Certificate.m
@@ -36,6 +36,16 @@
object: self];
}
return [super alloc];
}
+
++ (OFArray OF_GENERIC(OFX509Certificate *) *)
+ certificateChainFromIRI: (OFIRI *)IRI
+{
+ if (OFX509CertificateImplementation != Nil)
+ return [OFX509CertificateImplementation
+ certificateChainFromIRI: IRI];
+
+ OF_UNRECOGNIZED_SELECTOR
+}
@end
ADDED src/OFX509CertificatePrivateKey.h
Index: src/OFX509CertificatePrivateKey.h
==================================================================
--- /dev/null
+++ src/OFX509CertificatePrivateKey.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2008-2024 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3.0 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * version 3.0 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3.0 along with this program. If not, see
+ * .
+ */
+
+#import "OFObject.h"
+
+OF_ASSUME_NONNULL_BEGIN
+
+@class OFIRI;
+
+/**
+ * @class OFX509CertificatePrivateKey \
+ * OFX509CertificatePrivateKey.h ObjFW/ObjFW.h
+ *
+ * @brief The private key for an X.509 certificate.
+ */
+@interface OFX509CertificatePrivateKey: OFObject
+{
+ OF_RESERVE_IVARS(OFX509Certificate, 4)
+}
+
+/**
+ * @brief Returns a private key from the specified IRI.
+ *
+ * @param IRI The IRI to retrieve the private key from
+ *
+ * @throw OFInitializationFailedException Initializing the private key failed
+ * @throw OFOpenItemFailedException Opening the item failed
+ * @throw OFUnsupportedProtocolException The specified IRI is not supported
+ * @throw OFReadFailedException Reading the item failed
+ * @throw OFInvalidFormatException The format of the item is invalid
+ */
++ (instancetype)privateKeyFromIRI: (OFIRI *)IRI;
+@end
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**
+ * @brief The implementation for OFX509CertificatePrivateKey to use.
+ *
+ * This can be set to a class that is always used for
+ * OFX509CertificatePrivateKey. This is useful to either force a specific
+ * implementation or to use one that ObjFW does not know about.
+ */
+extern Class OFX509CertificatePrivateKeyImplementation;
+#ifdef __cplusplus
+}
+#endif
+
+OF_ASSUME_NONNULL_END
ADDED src/OFX509CertificatePrivateKey.m
Index: src/OFX509CertificatePrivateKey.m
==================================================================
--- /dev/null
+++ src/OFX509CertificatePrivateKey.m
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2008-2024 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3.0 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * version 3.0 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3.0 along with this program. If not, see
+ * .
+ */
+
+#include "config.h"
+
+#import "OFX509CertificatePrivateKey.h"
+
+#import "OFNotImplementedException.h"
+
+Class OFX509CertificatePrivateKeyImplementation = Nil;
+
+@implementation OFX509CertificatePrivateKey
++ (instancetype)alloc
+{
+ if (self == [OFX509CertificatePrivateKey class]) {
+ if (OFX509CertificatePrivateKeyImplementation != Nil)
+ return
+ [OFX509CertificatePrivateKeyImplementation alloc];
+
+ @throw [OFNotImplementedException exceptionWithSelector: _cmd
+ object: self];
+ }
+
+ return [super alloc];
+}
+
++ (instancetype)privateKeyFromIRI: (OFIRI *)IRI
+{
+ if (OFX509CertificatePrivateKeyImplementation != Nil)
+ return [OFX509CertificatePrivateKeyImplementation
+ privateKeyFromIRI: IRI];
+
+ OF_UNRECOGNIZED_SELECTOR
+}
+@end
Index: src/ObjFW.h
==================================================================
--- src/ObjFW.h
+++ src/ObjFW.h
@@ -84,10 +84,12 @@
# import "OFDatagramSocket.h"
# import "OFSequencedPacketSocket.h"
# import "OFTCPSocket.h"
# import "OFUDPSocket.h"
# import "OFTLSStream.h"
+# import "OFX509Certificate.h"
+# import "OFX509CertificatePrivateKey.h"
# import "OFKernelEventObserver.h"
# import "OFDNSQuery.h"
# import "OFDNSResourceRecord.h"
# import "OFDNSResponse.h"
# import "OFDNSResolver.h"
Index: src/tls/Makefile
==================================================================
--- src/tls/Makefile
+++ src/tls/Makefile
@@ -13,11 +13,13 @@
SRCS = ${USE_SRCS_GNUTLS} \
${USE_SRCS_MBEDTLS} \
${USE_SRCS_OPENSSL} \
${USE_SRCS_SECURETRANSPORT}
-SRCS_GNUTLS = OFGnuTLSTLSStream.m
+SRCS_GNUTLS = OFGnuTLSTLSStream.m \
+ OFGnuTLSX509Certificate.m \
+ OFGnuTLSX509CertificatePrivateKey.m
SRCS_MBEDTLS = OFMbedTLSTLSStream.m
SRCS_OPENSSL = OFOpenSSLTLSStream.m
SRCS_SECURETRANSPORT = OFSecureTransportTLSStream.m
includesubdir = ObjFWTLS
Index: src/tls/OFGnuTLSTLSStream.h
==================================================================
--- src/tls/OFGnuTLSTLSStream.h
+++ src/tls/OFGnuTLSTLSStream.h
@@ -21,14 +21,16 @@
#include
OF_ASSUME_NONNULL_BEGIN
+OF_SUBCLASSING_RESTRICTED
@interface OFGnuTLSTLSStream: OFTLSStream
{
- bool _initialized, _handshakeDone;
+ bool _server, _handshakeDone;
gnutls_session_t _session;
+ gnutls_certificate_credentials_t _credentials;
OFString *_host;
}
@end
OF_ASSUME_NONNULL_END
Index: src/tls/OFGnuTLSTLSStream.m
==================================================================
--- src/tls/OFGnuTLSTLSStream.m
+++ src/tls/OFGnuTLSTLSStream.m
@@ -20,21 +20,23 @@
#include "config.h"
#include
#import "OFGnuTLSTLSStream.h"
+#import "OFArray.h"
#import "OFData.h"
+#import "OFGnuTLSX509Certificate.h"
+#import "OFGnuTLSX509CertificatePrivateKey.h"
#import "OFAlreadyOpenException.h"
#import "OFInitializationFailedException.h"
#import "OFNotOpenException.h"
#import "OFReadFailedException.h"
#import "OFTLSHandshakeFailedException.h"
#import "OFWriteFailedException.h"
int _ObjFWTLS_reference;
-static gnutls_certificate_credentials_t systemTrustCreds;
#ifndef GNUTLS_SAFE_PADDING_CHECK
/* Some older versions don't have it. */
# define GNUTLS_SAFE_PADDING_CHECK 0
#endif
@@ -99,21 +101,10 @@
{
if (OFTLSStreamImplementation == Nil)
OFTLSStreamImplementation = self;
}
-+ (void)initialize
-{
- if (self != [OFGnuTLSTLSStream class])
- return;
-
- if (gnutls_certificate_allocate_credentials(&systemTrustCreds) !=
- GNUTLS_E_SUCCESS ||
- gnutls_certificate_set_x509_system_trust(systemTrustCreds) < 0)
- @throw [OFInitializationFailedException exception];
-}
-
- (instancetype)initWithStream: (OFStream *)stream
{
self = [super initWithStream: stream];
@@ -127,28 +118,31 @@
return self;
}
- (void)dealloc
{
- if (_initialized)
- [self close];
+ [self close];
[_host release];
[super dealloc];
}
- (void)close
{
- if (!_initialized)
- @throw [OFNotOpenException exceptionWithObject: self];
-
if (_handshakeDone)
gnutls_bye(_session, GNUTLS_SHUT_WR);
- gnutls_deinit(_session);
- _initialized = _handshakeDone = false;
+ if (_session != NULL)
+ gnutls_deinit(_session);
+
+ if (_credentials != NULL)
+ gnutls_certificate_free_credentials(_credentials);
+
+ _session = NULL;
+ _credentials = NULL;
+ _handshakeDone = false;
[_host release];
_host = nil;
[super close];
@@ -208,55 +202,86 @@
{
return (_underlyingStream.hasDataInReadBuffer ||
gnutls_record_check_pending(_session) > 0);
}
-- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
- runLoopMode: (OFRunLoopMode)runLoopMode
+- (void)of_asyncPerformHandshakeWithHost: (OFString *)host
+ server: (bool)server
+ runLoopMode: (OFRunLoopMode)runLoopMode
{
static const OFTLSStreamErrorCode initFailedErrorCode =
OFTLSStreamErrorCodeInitializationFailed;
void *pool = objc_autoreleasePoolPush();
id exception = nil;
int status;
- if (_initialized)
+ if (_handshakeDone)
@throw [OFAlreadyOpenException exceptionWithObject: self];
- if (gnutls_init(&_session, GNUTLS_CLIENT | GNUTLS_NONBLOCK |
- GNUTLS_SAFE_PADDING_CHECK) != GNUTLS_E_SUCCESS)
+ if (gnutls_init(&_session, (server ? GNUTLS_SERVER : GNUTLS_CLIENT) |
+ GNUTLS_NONBLOCK | GNUTLS_SAFE_PADDING_CHECK) != GNUTLS_E_SUCCESS)
@throw [OFTLSHandshakeFailedException
exceptionWithStream: self
host: host
errorCode: initFailedErrorCode];
- _initialized = true;
-
gnutls_transport_set_ptr(_session, self);
gnutls_transport_set_pull_function(_session, readFunc);
gnutls_transport_set_push_function(_session, writeFunc);
if (gnutls_set_default_priority(_session) != GNUTLS_E_SUCCESS ||
- gnutls_credentials_set(_session, GNUTLS_CRD_CERTIFICATE,
- systemTrustCreds) != GNUTLS_E_SUCCESS)
+ gnutls_certificate_allocate_credentials(&_credentials) !=
+ GNUTLS_E_SUCCESS ||
+ gnutls_certificate_set_x509_system_trust(_credentials) < 0)
@throw [OFTLSHandshakeFailedException
exceptionWithStream: self
host: host
errorCode: initFailedErrorCode];
_host = [host copy];
+ _server = server;
+
+ if (!server) {
+ if (gnutls_server_name_set(_session, GNUTLS_NAME_DNS,
+ _host.UTF8String, _host.UTF8StringLength) !=
+ GNUTLS_E_SUCCESS)
+ @throw [OFTLSHandshakeFailedException
+ exceptionWithStream: self
+ host: host
+ errorCode: initFailedErrorCode];
+
+ if (_verifiesCertificates)
+ gnutls_session_set_verify_cert(_session, _host.UTF8String, 0);
+ }
+
+ if (_certificateChain != nil) {
+ OFMutableData *certs = [OFMutableData
+ dataWithItemSize: sizeof(gnutls_x509_crt_t)
+ capacity: _certificateChain.count];
+
+ for (OFGnuTLSX509Certificate *cert in _certificateChain) {
+ gnutls_x509_crt_t gnuTLSCert =
+ cert.of_gnuTLSCertificate;
+ [certs addItem: &gnuTLSCert];
+ }
+
+ if (gnutls_certificate_set_x509_key(_credentials,
+ (gnutls_x509_crt_t *)certs.items, (unsigned int)certs.count,
+ [_privateKey of_gnuTLSPrivateKey]) < 0)
+ @throw [OFTLSHandshakeFailedException
+ exceptionWithStream: self
+ host: host
+ errorCode: initFailedErrorCode];
+ }
- if (gnutls_server_name_set(_session, GNUTLS_NAME_DNS,
- _host.UTF8String, _host.UTF8StringLength) != GNUTLS_E_SUCCESS)
+ if (gnutls_credentials_set(_session, GNUTLS_CRD_CERTIFICATE,
+ _credentials) != GNUTLS_E_SUCCESS)
@throw [OFTLSHandshakeFailedException
exceptionWithStream: self
host: host
errorCode: initFailedErrorCode];
- if (_verifiesCertificates)
- gnutls_session_set_verify_cert(_session, _host.UTF8String, 0);
-
status = gnutls_handshake(_session);
if (status == GNUTLS_E_INTERRUPTED || status == GNUTLS_E_AGAIN) {
if (gnutls_record_get_direction(_session) == 1)
[_underlyingStream asyncWriteData: [OFData data]
@@ -285,18 +310,40 @@
exceptionWithStream: self
host: host
errorCode: errorCode];
}
- if ([_delegate respondsToSelector:
- @selector(stream:didPerformClientHandshakeWithHost:exception:)])
- [_delegate stream: self
- didPerformClientHandshakeWithHost: host
- exception: exception];
+ if (server) {
+ if ([_delegate respondsToSelector: @selector(
+ streamDidPerformServerHandshake:exception:)])
+ [_delegate streamDidPerformServerHandshake: self
+ exception: exception];
+ } else {
+ if ([_delegate respondsToSelector: @selector(stream:
+ didPerformClientHandshakeWithHost:exception:)])
+ [_delegate stream: self
+ didPerformClientHandshakeWithHost: host
+ exception: exception];
+ }
objc_autoreleasePoolPop(pool);
}
+
+- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
+ runLoopMode: (OFRunLoopMode)runLoopMode
+{
+ [self of_asyncPerformHandshakeWithHost: host
+ server: false
+ runLoopMode: runLoopMode];
+}
+
+- (void)asyncPerformServerHandshakeWithRunLoopMode: (OFRunLoopMode)runLoopMode
+{
+ [self of_asyncPerformHandshakeWithHost: nil
+ server: true
+ runLoopMode: runLoopMode];
+}
- (bool)stream: (OFStream *)stream
didReadIntoBuffer: (void *)buffer
length: (size_t)length
exception: (id)exception
@@ -323,15 +370,22 @@
exceptionWithStream: self
host: _host
errorCode: OFTLSStreamErrorCodeUnknown];
}
- if ([_delegate respondsToSelector:
- @selector(stream:didPerformClientHandshakeWithHost:exception:)])
- [_delegate stream: self
- didPerformClientHandshakeWithHost: _host
- exception: exception];
+ if (_server) {
+ if ([_delegate respondsToSelector: @selector(
+ streamDidPerformServerHandshake:exception:)])
+ [_delegate streamDidPerformServerHandshake: self
+ exception: exception];
+ } else {
+ if ([_delegate respondsToSelector: @selector(stream:
+ didPerformClientHandshakeWithHost:exception:)])
+ [_delegate stream: self
+ didPerformClientHandshakeWithHost: _host
+ exception: exception];
+ }
[_delegate release];
return false;
}
@@ -366,16 +420,23 @@
exceptionWithStream: self
host: _host
errorCode: OFTLSStreamErrorCodeUnknown];
}
- if ([_delegate respondsToSelector:
- @selector(stream:didPerformClientHandshakeWithHost:exception:)])
- [_delegate stream: self
- didPerformClientHandshakeWithHost: _host
- exception: exception];
+ if (_server) {
+ if ([_delegate respondsToSelector: @selector(
+ streamDidPerformServerHandshake:exception:)])
+ [_delegate streamDidPerformServerHandshake: self
+ exception: exception];
+ } else {
+ if ([_delegate respondsToSelector: @selector(stream:
+ didPerformClientHandshakeWithHost:exception:)])
+ [_delegate stream: self
+ didPerformClientHandshakeWithHost: _host
+ exception: exception];
+ }
[_delegate release];
return nil;
}
@end
ADDED src/tls/OFGnuTLSX509Certificate.h
Index: src/tls/OFGnuTLSX509Certificate.h
==================================================================
--- /dev/null
+++ src/tls/OFGnuTLSX509Certificate.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2008-2024 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3.0 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * version 3.0 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3.0 along with this program. If not, see
+ * .
+ */
+
+#import "OFX509Certificate.h"
+
+#include
+
+OF_ASSUME_NONNULL_BEGIN
+
+OF_SUBCLASSING_RESTRICTED
+@interface OFGnuTLSX509Certificate: OFX509Certificate
+{
+ gnutls_x509_crt_t _certificate;
+}
+
+@property (readonly, nonatomic) gnutls_x509_crt_t of_gnuTLSCertificate;
+
+- (instancetype)of_initWithGnuTLSCertificate: (gnutls_x509_crt_t)certificate;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/tls/OFGnuTLSX509Certificate.m
Index: src/tls/OFGnuTLSX509Certificate.m
==================================================================
--- /dev/null
+++ src/tls/OFGnuTLSX509Certificate.m
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2008-2024 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3.0 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * version 3.0 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3.0 along with this program. If not, see
+ * .
+ */
+
+#include "config.h"
+
+#import "OFGnuTLSX509Certificate.h"
+#import "OFArray.h"
+#import "OFData.h"
+
+#import "OFInvalidFormatException.h"
+#import "OFOutOfRangeException.h"
+
+@implementation OFGnuTLSX509Certificate
+@synthesize of_gnuTLSCertificate = _certificate;
+
++ (void)load
+{
+ if (OFX509CertificateImplementation == Nil)
+ OFX509CertificateImplementation = self;
+}
+
++ (OFArray OF_GENERIC(OFX509Certificate *) *)
+ certificateChainFromIRI: (OFIRI *)IRI
+{
+ OFMutableArray *chain = [OFMutableArray array];
+ void *pool = objc_autoreleasePoolPush();
+ OFData *data = [OFData dataWithContentsOfIRI: IRI];
+ gnutls_datum_t datum;
+ gnutls_x509_crt_t *certs;
+ unsigned int i, size;
+
+ if (data.count * data.itemSize > UINT_MAX)
+ @throw [OFOutOfRangeException exception];
+
+ datum.data = (unsigned char *)data.items;
+ datum.size = (unsigned int)(data.count * data.itemSize);
+
+ if (gnutls_x509_crt_list_import2(&certs, &size, &datum,
+ GNUTLS_X509_FMT_PEM, 0) != GNUTLS_E_SUCCESS)
+ @throw [OFInvalidFormatException exception];
+
+ for (i = 0; i < size; i++) {
+ OFGnuTLSX509Certificate *certificate;
+
+ @try {
+ certificate = [[self alloc]
+ of_initWithGnuTLSCertificate: certs[i]];
+ } @catch (id e) {
+ gnutls_x509_crt_deinit(certs[i]);
+ gnutls_free(certs);
+ @throw e;
+ }
+
+ @try {
+ [chain addObject: certificate];
+ } @catch (id e) {
+ gnutls_free(certs);
+ @throw e;
+ } @finally {
+ [certificate release];
+ }
+ }
+
+ gnutls_free(certs);
+
+ objc_autoreleasePoolPop(pool);
+
+ return chain;
+}
+
+- (instancetype)of_initWithGnuTLSCertificate: (gnutls_x509_crt_t)certificate
+{
+ self = [super init];
+
+ _certificate = certificate;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ gnutls_x509_crt_deinit(_certificate);
+
+ [super dealloc];
+}
+@end
ADDED src/tls/OFGnuTLSX509CertificatePrivateKey.h
Index: src/tls/OFGnuTLSX509CertificatePrivateKey.h
==================================================================
--- /dev/null
+++ src/tls/OFGnuTLSX509CertificatePrivateKey.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2008-2024 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3.0 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * version 3.0 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3.0 along with this program. If not, see
+ * .
+ */
+
+#import "OFX509CertificatePrivateKey.h"
+
+#include
+
+OF_ASSUME_NONNULL_BEGIN
+
+OF_SUBCLASSING_RESTRICTED
+@interface OFGnuTLSX509CertificatePrivateKey: OFX509CertificatePrivateKey
+{
+ gnutls_x509_privkey_t _privateKey;
+}
+
+@property (readonly, nonatomic) gnutls_x509_privkey_t of_gnuTLSPrivateKey;
+
+- (instancetype)of_initWithGnuTLSPrivateKey: (gnutls_x509_privkey_t)privateKey;
+@end
+
+OF_ASSUME_NONNULL_END
ADDED src/tls/OFGnuTLSX509CertificatePrivateKey.m
Index: src/tls/OFGnuTLSX509CertificatePrivateKey.m
==================================================================
--- /dev/null
+++ src/tls/OFGnuTLSX509CertificatePrivateKey.m
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2008-2024 Jonathan Schleifer
+ *
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3.0 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * version 3.0 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3.0 along with this program. If not, see
+ * .
+ */
+
+#include "config.h"
+
+#import "OFGnuTLSX509CertificatePrivateKey.h"
+#import "OFData.h"
+
+#import "OFInitializationFailedException.h"
+#import "OFInvalidFormatException.h"
+#import "OFOutOfRangeException.h"
+
+@implementation OFGnuTLSX509CertificatePrivateKey
+@synthesize of_gnuTLSPrivateKey = _privateKey;
+
++ (void)load
+{
+ if (OFX509CertificatePrivateKeyImplementation == Nil)
+ OFX509CertificatePrivateKeyImplementation = self;
+}
+
++ (instancetype)privateKeyFromIRI: (OFIRI *)IRI
+{
+ void *pool = objc_autoreleasePoolPush();
+ OFData *data = [OFData dataWithContentsOfIRI: IRI];
+ gnutls_datum_t datum;
+ gnutls_x509_privkey_t key;
+ OFGnuTLSX509CertificatePrivateKey *privateKey;
+
+ if (data.count * data.itemSize > UINT_MAX)
+ @throw [OFOutOfRangeException exception];
+
+ datum.data = (unsigned char *)data.items;
+ datum.size = (unsigned int)(data.count * data.itemSize);
+
+ if (gnutls_x509_privkey_init(&key) != GNUTLS_E_SUCCESS)
+ @throw [OFInitializationFailedException
+ exceptionWithClass: self];
+
+ if (gnutls_x509_privkey_import(key, &datum,
+ GNUTLS_X509_FMT_PEM) != GNUTLS_E_SUCCESS) {
+ gnutls_x509_privkey_deinit(key);
+ @throw [OFInvalidFormatException exception];
+ }
+
+ @try {
+ privateKey = [[self alloc] of_initWithGnuTLSPrivateKey: key];
+ } @catch (id e) {
+ gnutls_x509_privkey_deinit(key);
+ @throw e;
+ }
+
+ objc_autoreleasePoolPop(pool);
+
+ return [privateKey autorelease];
+}
+
+- (instancetype)of_initWithGnuTLSPrivateKey: (gnutls_x509_privkey_t)privateKey
+{
+ self = [super init];
+
+ _privateKey = privateKey;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ gnutls_x509_privkey_deinit(_privateKey);
+
+ [super dealloc];
+}
+@end
Index: src/tls/OFMbedTLSTLSStream.h
==================================================================
--- src/tls/OFMbedTLSTLSStream.h
+++ src/tls/OFMbedTLSTLSStream.h
@@ -21,10 +21,11 @@
#include
OF_ASSUME_NONNULL_BEGIN
+OF_SUBCLASSING_RESTRICTED
@interface OFMbedTLSTLSStream: OFTLSStream
{
bool _initialized, _handshakeDone;
mbedtls_ssl_config _config;
mbedtls_ssl_context _SSL;
Index: src/tls/OFOpenSSLTLSStream.h
==================================================================
--- src/tls/OFOpenSSLTLSStream.h
+++ src/tls/OFOpenSSLTLSStream.h
@@ -24,10 +24,11 @@
OF_ASSUME_NONNULL_BEGIN
#define OFOpenSSLTLSStreamBufferSize 512
+OF_SUBCLASSING_RESTRICTED
@interface OFOpenSSLTLSStream: OFTLSStream
{
bool _handshakeDone;
SSL *_SSL;
BIO *_readBIO, *_writeBIO;
Index: src/tls/OFSecureTransportTLSStream.h
==================================================================
--- src/tls/OFSecureTransportTLSStream.h
+++ src/tls/OFSecureTransportTLSStream.h
@@ -21,10 +21,11 @@
#include
OF_ASSUME_NONNULL_BEGIN
+OF_SUBCLASSING_RESTRICTED
@interface OFSecureTransportTLSStream: OFTLSStream
{
SSLContextRef _context;
OFString *_host;
}