Skip to content

Commit 641301c

Browse files
authored
CBL-6902: External Keys to work with client cert (#2254)
* CBL-6902: External Keys to work with client cert - Override ExternalPrivateKey::isPrivateKeyDataAvailable to return false. - For external keys, we do not have key data. Unlike the KeyData, which is passed in a fleece dictionary inside C4ReplicatorParameters, we have to pass C4KeyPair by object pointer. - We added "C4KeyPair*" to C4ReplicatorParameters. This is the counterpart of kC4ReplicatorAuthClientCertKey when PrivateKeyData is not available (mutually exclusive). - It is an EE only option.
1 parent c18fcbd commit 641301c

File tree

7 files changed

+51
-13
lines changed

7 files changed

+51
-13
lines changed

C/include/c4ReplicatorTypes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ typedef struct C4ReplicatorParameters {
203203
const C4SocketFactory* C4NULLABLE socketFactory; ///< Custom C4SocketFactory, if not NULL
204204
C4ReplicationCollection* collections;
205205
size_t collectionCount;
206+
#ifdef COUCHBASE_ENTERPRISE
207+
C4KeyPair* C4NULLABLE externalKey;
208+
#endif
206209
} C4ReplicatorParameters;
207210

208211
#pragma mark - CONSTANTS:

Crypto/PublicKey.hh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ namespace litecore::crypto {
115115

116116
/** A key-pair stored externally; subclasses must implement the crypto operations. */
117117
class ExternalPrivateKey : public PrivateKey {
118+
public:
119+
virtual bool isPrivateKeyDataAvailable() override { return false; }
120+
118121
protected:
119122
explicit ExternalPrivateKey(unsigned keySizeInBits);
120123

Networking/WebSockets/BuiltInWebSocket.cc

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ using namespace litecore::websocket;
2929

3030
void C4RegisterBuiltInWebSocket() {
3131
// NOLINTBEGIN(performance-unnecessary-value-param)
32-
C4SocketImpl::registerInternalFactory(
33-
[](websocket::URL url, fleece::alloc_slice options, std::shared_ptr<DBAccess> database) -> WebSocketImpl* {
34-
return new BuiltInWebSocket(url, C4SocketImpl::convertParams(options), database);
35-
});
32+
C4SocketImpl::registerInternalFactory([](websocket::URL url, fleece::alloc_slice options,
33+
std::shared_ptr<DBAccess> database,
34+
C4KeyPair* externalKey) -> WebSocketImpl* {
35+
return new BuiltInWebSocket(url, C4SocketImpl::convertParams(options, externalKey), database);
36+
});
3637
// NOLINTEND(performance-unnecessary-value-param)
3738
}
3839

@@ -252,6 +253,13 @@ namespace litecore::websocket {
252253
"Missing TLS client cert in C4Replicator config"_sl));
253254
return false;
254255
}
256+
#ifdef COUCHBASE_ENTERPRISE
257+
if ( parameters().externalKey ) {
258+
Retained<crypto::Cert> cert = make_retained<crypto::Cert>(certData);
259+
_tlsContext->setIdentity(new crypto::Identity(cert, parameters().externalKey->getPrivateKey()));
260+
return true;
261+
}
262+
#endif
255263
if ( slice keyData = auth[kC4ReplicatorAuthClientCertKey].asData(); keyData ) {
256264
_tlsContext->setIdentity(certData, keyData);
257265
return true;

Networking/WebSockets/WebSocketImpl.hh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "WebSocketInterface.hh"
1515
#include "Logging.hh"
1616
#include "Stopwatch.hh"
17+
#include "c4Certificate.hh"
1718
#include "fleece/Expert.hh" // for AllocedDict
1819
#include <atomic>
1920
#include <chrono>
@@ -45,6 +46,9 @@ namespace litecore::websocket {
4546
int heartbeatSecs; ///< WebSocket heartbeat interval in seconds (default if 0)
4647
fleece::alloc_slice networkInterface; ///< Network interface
4748
fleece::AllocedDict options; ///< Other options
49+
#ifdef COUCHBASE_ENTERPRISE
50+
Retained<C4KeyPair> externalKey; ///< Client cert uses external key..
51+
#endif
4852
};
4953

5054
WebSocketImpl(const URL& url, Role role, bool framing, Parameters);

Replicator/c4RemoteReplicator.hh

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ namespace litecore {
5454
_customSocketFactory = *params.socketFactory;
5555
_socketFactory = &_customSocketFactory;
5656
}
57+
#if COUCHBASE_ENTERPRISE
58+
_socketExternalKey = params.externalKey;
59+
#endif
5760
}
5861

5962
void start(bool reset) noexcept override {
@@ -117,8 +120,14 @@ namespace litecore {
117120
_c4db_setDatabaseTag(dbOpenedAgain, DatabaseTag_C4RemoteReplicator);
118121
auto dbAccess =
119122
make_shared<DBAccess>(dbOpenedAgain, _options->properties["disable_blob_support"_sl].asBool());
120-
auto webSocket = CreateWebSocket(_url, socketOptions(), dbAccess, _socketFactory);
121-
_replicator = new Replicator(dbAccess, webSocket, *this, _options);
123+
auto webSocket = CreateWebSocket(_url, socketOptions(), dbAccess, _socketFactory, nullptr
124+
#ifdef COUCHBASE_ENTERPRISE
125+
,
126+
_socketExternalKey);
127+
#else
128+
);
129+
#endif
130+
_replicator = new Replicator(dbAccess, webSocket, *this, _options);
122131

123132
// Yes this line is disgusting, but the memory addresses that the logger logs
124133
// are not the _actual_ addresses of the object, but rather the pointer to
@@ -220,6 +229,13 @@ namespace litecore {
220229
private:
221230
alloc_slice const _url;
222231
const C4SocketFactory* _socketFactory{nullptr};
232+
// _socketExternalKey comes from C4ReplicatorParameters::externalKey. It belongs to
233+
// kC4ReplicatorOptionAuthentication, but it's not present in the corresponding dictionary.
234+
// It's mutually exclusive with kC4ReplicatorAuthClientCertKey, which provides the option
235+
// by key-data.
236+
#if COUCHBASE_ENTERPRISE
237+
Retained<C4KeyPair> _socketExternalKey;
238+
#endif
223239
C4SocketFactory _customSocketFactory{}; // Storage for *_socketFactory if non-null
224240
litecore::actor::Timer _retryTimer;
225241
unsigned _retryCount{0};

Replicator/c4Socket+Internal.hh

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ namespace litecore::repl {
2222
// Main factory function to create a WebSocket.
2323
fleece::Retained<websocket::WebSocket> CreateWebSocket(const websocket::URL&, const fleece::alloc_slice& options,
2424
std::shared_ptr<DBAccess>, const C4SocketFactory*,
25-
void* nativeHandle = nullptr);
25+
void* nativeHandle = nullptr,
26+
C4KeyPair* externalKey = nullptr);
2627

2728
// Returns the WebSocket object associated with a C4Socket
2829
websocket::WebSocket* WebSocketFrom(C4Socket* c4sock);
@@ -34,11 +35,11 @@ namespace litecore::repl {
3435
public:
3536
static const C4SocketFactory& registeredFactory();
3637

37-
using InternalFactory = websocket::WebSocketImpl* (*)(websocket::URL, fleece::alloc_slice options,
38-
std::shared_ptr<DBAccess>);
38+
using InternalFactory = websocket::WebSocketImpl* (*)(websocket::URL, fleece::alloc_slice options,
39+
std::shared_ptr<DBAccess>, C4KeyPair* externalKey);
3940
static void registerInternalFactory(InternalFactory);
4041

41-
static Parameters convertParams(fleece::slice c4SocketOptions);
42+
static Parameters convertParams(fleece::slice c4SocketOptions, C4KeyPair* externalKey = nullptr);
4243

4344
C4SocketImpl(const websocket::URL&, websocket::Role, const fleece::alloc_slice& options, const C4SocketFactory*,
4445
void* nativeHandle = nullptr);

Replicator/c4Socket.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,15 @@ namespace litecore::repl {
6262

6363
Retained<WebSocket> CreateWebSocket(const websocket::URL& url, const alloc_slice& options,
6464
shared_ptr<DBAccess> database, const C4SocketFactory* factory,
65-
void* nativeHandle) {
65+
void* nativeHandle, C4KeyPair* externalKey) {
6666
if ( !factory ) factory = sRegisteredFactory;
6767

6868
if ( factory ) {
6969
auto ret = new C4SocketImpl(url, Role::Client, options, factory, nativeHandle);
7070
return ret;
7171
} else if ( sRegisteredInternalFactory ) {
7272
Assert(!nativeHandle);
73-
return sRegisteredInternalFactory(url, options, std::move(database));
73+
return sRegisteredInternalFactory(url, options, std::move(database), externalKey);
7474
} else {
7575
throw std::logic_error("No default C4SocketFactory registered; call c4socket_registerFactory())");
7676
}
@@ -80,12 +80,15 @@ namespace litecore::repl {
8080
return f ? *f : C4SocketImpl::registeredFactory();
8181
}
8282

83-
WebSocketImpl::Parameters C4SocketImpl::convertParams(slice c4SocketOptions) {
83+
WebSocketImpl::Parameters C4SocketImpl::convertParams(slice c4SocketOptions, C4KeyPair* externalKey) {
8484
WebSocketImpl::Parameters params = {};
8585
params.options = AllocedDict(c4SocketOptions);
8686
params.webSocketProtocols = params.options[kC4SocketOptionWSProtocols].asString();
8787
params.heartbeatSecs = (int)params.options[kC4ReplicatorHeartbeatInterval].asInt();
8888
params.networkInterface = params.options[kC4SocketOptionNetworkInterface].asString();
89+
#ifdef COUCHBASE_ENTERPRISE
90+
params.externalKey = externalKey;
91+
#endif
8992
return params;
9093
}
9194

0 commit comments

Comments
 (0)