/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* 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
* <https://www.gnu.org/licenses/>.
*/
#import "OFStream.h"
#import "OFRunLoop.h"
#import "OFX509Certificate.h"
#import "OFX509CertificatePrivateKey.h"
OF_ASSUME_NONNULL_BEGIN
/** @file */
@class OFArray OF_GENERIC(ObjectType);
@class OFTLSStream;
/**
* @brief An enum representing an error of an OFTLSStream.
*/
typedef enum {
/** @brief An unknown error. */
OFTLSStreamErrorCodeUnknown,
/** @brief Initialization of the TLS context failed. */
OFTLSStreamErrorCodeInitializationFailed,
/** @brief Failed to verify certificate. */
OFTLSStreamErrorCodeCertificateVerificationFailed,
/** @brief The certificate has an untrusted or unknown issuer. */
OFTLSStreamErrorCodeCertificateIssuerUntrusted,
/** @brief The certificate is for a different name. */
OFTLSStreamErrorCodeCertificateNameMismatch,
/** @brief The certificate has expired or is not yet valid. */
OFTLSStreamErrorCodeCertificatedExpired,
/** @brief The certificate has been revoked. */
OFTLSStreamErrorCodeCertificateRevoked
} OFTLSStreamErrorCode;
/**
* @protocol OFTLSStreamDelegate OFTLSStream.h ObjFW/ObjFW.h
*
* A delegate for OFTLSStream.
*/
@protocol OFTLSStreamDelegate <OFStreamDelegate>
@optional
/**
* @brief A method which is called when a TLS stream performed the client
* handshake.
*
* @param stream The TLS stream which performed the handshake
* @param host The host for which the handshake was performed
* @param exception An exception that occurred during the handshake, or nil on
* success
*/
- (void)stream: (OFTLSStream *)stream
didPerformClientHandshakeWithHost: (OFString *)host
exception: (nullable id)exception;
/**
* @brief A method which is called when a TLS stream performed the server
* handshake.
*
* @param stream The TLS stream which performed the handshake
* @param exception An exception that occurred during the handshake, or nil on
* success
*/
- (void)streamDidPerformServerHandshake: (OFTLSStream *)stream
exception: (nullable id)exception;
@end
/**
* @class OFTLSStream OFTLSStream.h ObjFW/ObjFW.h
*
* @brief A class that provides Transport Layer Security on top of a stream.
*
* This class is a class cluster and returns a suitable OFTLSStream subclass,
* if available.
*
* Subclasses need to override @ref lowlevelReadIntoBuffer:length:,
* @ref lowlevelWriteBuffer:length:,
* @ref lowlevelHasDataInReadBuffer and
* @ref asyncPerformClientHandshakeWithHost:runLoopMode:.
*
* In order to get access to the underlying stream, @ref underlyingStream can
* be used.
*/
@interface OFTLSStream: OFStream <OFReadyForReadingObserving,
OFReadyForWritingObserving>
{
OFStream <OFReadyForReadingObserving, OFReadyForWritingObserving>
*_underlyingStream;
bool _verifiesCertificates;
OFArray OF_GENERIC(OFX509Certificate *) *_Nullable _certificateChain;
OFX509CertificatePrivateKey *_Nullable _privateKey;
OF_RESERVE_IVARS(OFTLSStream, 2)
}
/**
* @brief The underlying stream.
*/
@property (readonly, nonatomic) OFStream <OFReadyForReadingObserving,
OFReadyForWritingObserving> *underlyingStream;
/**
* @brief The delegate for asynchronous operations on the stream.
*
* @note The delegate is retained for as long as asynchronous operations are
* still ongoing.
*/
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
id <OFTLSStreamDelegate> delegate;
/**
* @brief Whether certificates are verified. Default is true.
*/
@property (nonatomic) bool verifiesCertificates;
/**
* @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.
*
* @param stream The stream to use as underlying stream. Must not be closed
* before the TLS stream is closed.
* @return A new, autoreleased TLS stream
*/
+ (instancetype)streamWithStream: (OFStream <OFReadyForReadingObserving,
OFReadyForWritingObserving> *)stream;
/**
* @brief Initializes the TLS stream with the specified stream as its
* underlying stream.
*
* @note The delegate of the specified stream will be changed to the TLS
* stream. You must not change this before the TLS session is completed.
*
* @param stream The stream to use as underlying stream. Must not be closed
* before the TLS stream is closed.
* @return An initialized TLS stream
*/
- (instancetype)initWithStream: (OFStream <OFReadyForReadingObserving,
OFReadyForWritingObserving> *)stream
OF_DESIGNATED_INITIALIZER;
/**
* @brief Asynchronously performs the TLS client handshake for the specified
* host and calls the delegate afterwards.
*
* @param host The host to perform the handshake with
* @throw OFTLSHandshakeFailedException The TLS handshake failed
* @throw OFAlreadyOpenException The handshake was already performed
*/
- (void)asyncPerformClientHandshakeWithHost: (OFString *)host;
/**
* @brief Asynchronously performs the TLS client handshake for the specified
* host and calls the delegate afterwards.
*
* @param host The host to perform the handshake with
* @param runLoopMode The run loop mode in which to perform the async handshake
* @throw OFTLSHandshakeFailedException The TLS handshake failed
* @throw OFAlreadyOpenException The handshake was already performed
*/
- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
runLoopMode: (OFRunLoopMode)runLoopMode;
/**
* @brief Performs the TLS client handshake for the specified host.
*
* @param host The host to perform the handshake with
* @throw OFTLSHandshakeFailedException The TLS handshake failed
* @throw OFAlreadyOpenException The handshake was already performed
*/
- (void)performClientHandshakeWithHost: (OFString *)host;
/**
* @brief Asynchronously performs the TLS server handshake and calls the
* delegate afterwards.
*
* @throw OFTLSHandshakeFailedException The TLS handshake failed
* @throw OFAlreadyOpenException The handshake was already performed
*/
- (void)asyncPerformServerHandshake;
/**
* @brief Asynchronously performs the TLS server handshake and calls the
* delegate afterwards.
*
* @param runLoopMode The run loop mode in which to perform the async handshake
*
* @throw OFTLSHandshakeFailedException The TLS handshake failed
* @throw OFAlreadyOpenException The handshake was already performed
*/
- (void)asyncPerformServerHandshakeWithRunLoopMode: (OFRunLoopMode)runLoopMode;
/**
* @brief Performs the TLS server handshake.
*
* @throw OFTLSHandshakeFailedException The TLS handshake failed
* @throw OFAlreadyOpenException The handshake was already performed
*/
- (void)performServerHandshake;
@end
#ifdef __cplusplus
extern "C" {
#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 to use one that ObjFW
* does not know about.
*/
extern Class OFTLSStreamImplementation;
/**
* @brief Returns a string description for the TLS stream error code.
*
* @param errorCode The error code to return the description for
* @return A string description for the TLS stream error code
*/
extern OFString *OFTLSStreamErrorCodeDescription(
OFTLSStreamErrorCode errorCode);
#ifdef __cplusplus
}
#endif
OF_ASSUME_NONNULL_END