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; }