noxtls 0.1.3

TLS/DTLS protocol and connection state machine for the noxtls Rust stack.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
// Copyright (c) 2019-2026, Argenox Technologies LLC
// All rights reserved.
//
// SPDX-License-Identifier: GPL-2.0-only OR LicenseRef-Argenox-Commercial-License
//
// This file is part of the NoxTLS Library.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by the
// Free Software Foundation; version 2 of the License.
//
// Alternatively, this file may be used under the terms of a commercial
// license from Argenox Technologies LLC.
//
// See `noxtls/LICENSE` and `noxtls/LICENSE.md` in this repository for full details.
// CONTACT: info@argenox.com

use crate::internal_alloc::Vec;
use noxtls_core::{Error, Result};
use noxtls_crypto::{
    rsaes_oaep_sha256_decrypt, rsaes_pkcs1_v15_decrypt, rsassa_pss_sha256_sign, rsassa_sha256_sign,
    x25519_shared_secret, RsaPrivateKey, X25519PrivateKey, X25519PublicKey,
};

/// Opaque external key handle used by providers to locate key material.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ExternalKeyHandle {
    id: Vec<u8>,
}

/// Names supported signing operations for external/provider-backed keys.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum KeySignAlgorithm {
    RsaPkcs1Sha256,
    RsaPssSha256,
}

/// Names supported decryption operations for external/provider-backed keys.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum KeyDecryptAlgorithm {
    RsaPkcs1v15,
    RsaOaepSha256,
}

/// Names supported key-derivation operations for external/provider-backed keys.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum KeyDeriveAlgorithm {
    X25519,
}

/// Carries one provider sign operation request.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct KeySignRequest<'a> {
    pub handle: &'a ExternalKeyHandle,
    pub algorithm: KeySignAlgorithm,
    pub message: &'a [u8],
    pub salt: Option<&'a [u8]>,
}

/// Carries one provider decrypt operation request.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct KeyDecryptRequest<'a> {
    pub handle: &'a ExternalKeyHandle,
    pub algorithm: KeyDecryptAlgorithm,
    pub ciphertext: &'a [u8],
    pub label: Option<&'a [u8]>,
}

/// Carries one provider key-derivation operation request.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct KeyDeriveRequest<'a> {
    pub handle: &'a ExternalKeyHandle,
    pub algorithm: KeyDeriveAlgorithm,
    pub peer_public_key: &'a [u8],
}

/// Trait boundary for external key operations backed by software, HSM, or remote KMS providers.
pub trait ExternalKeyProvider {
    /// Performs a provider-backed signing operation.
    ///
    /// # Arguments
    ///
    /// * `request` — Sign request carrying handle, algorithm, message bytes, and optional salt.
    ///
    /// # Returns
    ///
    /// Signature bytes produced by the configured provider.
    ///
    /// # Errors
    ///
    /// Returns provider or algorithm errors when key lookup, input validation, or signing fails.
    fn sign(&self, request: &KeySignRequest<'_>) -> Result<Vec<u8>>;

    /// Performs a provider-backed decryption operation.
    ///
    /// # Arguments
    ///
    /// * `request` — Decrypt request carrying handle, algorithm, ciphertext, and optional OAEP label.
    ///
    /// # Returns
    ///
    /// Plaintext bytes produced by the configured provider.
    ///
    /// # Errors
    ///
    /// Returns provider or algorithm errors when key lookup, input validation, or decrypt fails.
    fn decrypt(&self, request: &KeyDecryptRequest<'_>) -> Result<Vec<u8>>;

    /// Performs a provider-backed key-derivation operation.
    ///
    /// # Arguments
    ///
    /// * `request` — Derive request carrying handle, algorithm, and peer public key bytes.
    ///
    /// # Returns
    ///
    /// Derived shared secret bytes.
    ///
    /// # Errors
    ///
    /// Returns provider or algorithm errors when key lookup, input validation, or derivation fails.
    fn derive_shared_secret(&self, request: &KeyDeriveRequest<'_>) -> Result<Vec<u8>>;
}

/// In-tree software provider implementing the same external key-provider trait boundary.
#[derive(Debug, Clone, Default)]
pub struct SoftwareKeyProvider {
    rsa_signing_keys: Vec<(ExternalKeyHandle, RsaPrivateKey)>,
    rsa_decrypt_keys: Vec<(ExternalKeyHandle, RsaPrivateKey)>,
    x25519_keys: Vec<(ExternalKeyHandle, X25519PrivateKey)>,
}

impl ExternalKeyHandle {
    /// Creates one external key handle from raw identifier bytes.
    ///
    /// # Arguments
    ///
    /// * `id` — Opaque key identifier bytes used by provider lookup logic.
    ///
    /// # Returns
    ///
    /// `ExternalKeyHandle` wrapping `id`.
    ///
    /// # Errors
    ///
    /// Returns [`Error::InvalidLength`] when `id` is empty.
    pub fn new(id: &[u8]) -> Result<Self> {
        if id.is_empty() {
            return Err(Error::InvalidLength(
                "external key handle must not be empty",
            ));
        }
        Ok(Self { id: id.to_vec() })
    }

    /// Borrows the opaque identifier bytes for provider adapter conversions.
    ///
    /// # Arguments
    ///
    /// * `self` — External key handle whose identifier bytes are required.
    ///
    /// # Returns
    ///
    /// Borrowed opaque identifier bytes.
    ///
    /// # Panics
    ///
    /// This function does not panic.
    #[must_use]
    pub fn as_bytes(&self) -> &[u8] {
        &self.id
    }
}

impl SoftwareKeyProvider {
    /// Creates an empty software provider with no registered keys.
    ///
    /// # Returns
    ///
    /// New provider instance ready for key registration.
    ///
    /// # Panics
    ///
    /// This function does not panic.
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    /// Registers one RSA key handle for signing operations.
    ///
    /// # Arguments
    ///
    /// * `handle` — External key handle associated with `key`.
    /// * `key` — RSA private key used for sign requests.
    ///
    /// # Returns
    ///
    /// `Ok(())` when key registration succeeds.
    ///
    /// # Errors
    ///
    /// Returns [`Error::StateError`] when `handle` is already registered for signing.
    pub fn register_rsa_signing_key(
        &mut self,
        handle: ExternalKeyHandle,
        key: RsaPrivateKey,
    ) -> Result<()> {
        if self
            .rsa_signing_keys
            .iter()
            .any(|(existing, _)| existing == &handle)
        {
            return Err(Error::StateError(
                "rsa signing key handle is already registered",
            ));
        }
        self.rsa_signing_keys.push((handle, key));
        Ok(())
    }

    /// Registers one RSA key handle for decryption operations.
    ///
    /// # Arguments
    ///
    /// * `handle` — External key handle associated with `key`.
    /// * `key` — RSA private key used for decrypt requests.
    ///
    /// # Returns
    ///
    /// `Ok(())` when key registration succeeds.
    ///
    /// # Errors
    ///
    /// Returns [`Error::StateError`] when `handle` is already registered for decryption.
    pub fn register_rsa_decryption_key(
        &mut self,
        handle: ExternalKeyHandle,
        key: RsaPrivateKey,
    ) -> Result<()> {
        if self
            .rsa_decrypt_keys
            .iter()
            .any(|(existing, _)| existing == &handle)
        {
            return Err(Error::StateError(
                "rsa decryption key handle is already registered",
            ));
        }
        self.rsa_decrypt_keys.push((handle, key));
        Ok(())
    }

    /// Registers one X25519 key handle for key-derivation operations.
    ///
    /// # Arguments
    ///
    /// * `handle` — External key handle associated with `key`.
    /// * `key` — X25519 private key used for derive requests.
    ///
    /// # Returns
    ///
    /// `Ok(())` when key registration succeeds.
    ///
    /// # Errors
    ///
    /// Returns [`Error::StateError`] when `handle` is already registered for X25519 derivation.
    pub fn register_x25519_key(
        &mut self,
        handle: ExternalKeyHandle,
        key: X25519PrivateKey,
    ) -> Result<()> {
        if self
            .x25519_keys
            .iter()
            .any(|(existing, _)| existing == &handle)
        {
            return Err(Error::StateError("x25519 key handle is already registered"));
        }
        self.x25519_keys.push((handle, key));
        Ok(())
    }

    /// Looks up one registered RSA signing key by external handle.
    ///
    /// # Arguments
    ///
    /// * `handle` — External key handle from the incoming sign request.
    ///
    /// # Returns
    ///
    /// Reference to the registered RSA private signing key.
    ///
    /// # Errors
    ///
    /// Returns [`Error::StateError`] when the handle is unknown.
    fn rsa_signing_key(&self, handle: &ExternalKeyHandle) -> Result<&RsaPrivateKey> {
        self.rsa_signing_keys
            .iter()
            .find_map(|(existing, key)| (existing == handle).then_some(key))
            .ok_or(Error::StateError("unknown rsa signing key handle"))
    }

    /// Looks up one registered RSA decryption key by external handle.
    ///
    /// # Arguments
    ///
    /// * `handle` — External key handle from the incoming decrypt request.
    ///
    /// # Returns
    ///
    /// Reference to the registered RSA private decrypt key.
    ///
    /// # Errors
    ///
    /// Returns [`Error::StateError`] when the handle is unknown.
    fn rsa_decryption_key(&self, handle: &ExternalKeyHandle) -> Result<&RsaPrivateKey> {
        self.rsa_decrypt_keys
            .iter()
            .find_map(|(existing, key)| (existing == handle).then_some(key))
            .ok_or(Error::StateError("unknown rsa decryption key handle"))
    }

    /// Looks up one registered X25519 key by external handle.
    ///
    /// # Arguments
    ///
    /// * `handle` — External key handle from the incoming derive request.
    ///
    /// # Returns
    ///
    /// Clone of the registered X25519 private key.
    ///
    /// # Errors
    ///
    /// Returns [`Error::StateError`] when the handle is unknown.
    fn x25519_private_key(&self, handle: &ExternalKeyHandle) -> Result<X25519PrivateKey> {
        self.x25519_keys
            .iter()
            .find_map(|(existing, key)| (existing == handle).then_some(key.clone()))
            .ok_or(Error::StateError("unknown x25519 key handle"))
    }
}

impl ExternalKeyProvider for SoftwareKeyProvider {
    /// Performs software-provider signing using the registered key handle and selected algorithm.
    ///
    /// # Arguments
    ///
    /// * `request` — Sign request to execute.
    ///
    /// # Returns
    ///
    /// Signature bytes for the requested message.
    ///
    /// # Errors
    ///
    /// Returns key lookup failures, unsupported algorithm details, or algorithm-level signing failures.
    fn sign(&self, request: &KeySignRequest<'_>) -> Result<Vec<u8>> {
        let key = self.rsa_signing_key(request.handle)?;
        match request.algorithm {
            KeySignAlgorithm::RsaPkcs1Sha256 => rsassa_sha256_sign(key, request.message),
            KeySignAlgorithm::RsaPssSha256 => {
                let salt = request.salt.ok_or(Error::InvalidLength(
                    "rsa-pss-sha256 signing requires a salt",
                ))?;
                rsassa_pss_sha256_sign(key, request.message, salt)
            }
        }
    }

    /// Performs software-provider decryption using the registered key handle and selected algorithm.
    ///
    /// # Arguments
    ///
    /// * `request` — Decrypt request to execute.
    ///
    /// # Returns
    ///
    /// Decrypted plaintext bytes.
    ///
    /// # Errors
    ///
    /// Returns key lookup failures, or a uniform decryption-failure error for any algorithm-level decrypt failure.
    fn decrypt(&self, request: &KeyDecryptRequest<'_>) -> Result<Vec<u8>> {
        let key = self.rsa_decryption_key(request.handle)?;
        let plaintext = match request.algorithm {
            KeyDecryptAlgorithm::RsaPkcs1v15 => rsaes_pkcs1_v15_decrypt(key, request.ciphertext),
            KeyDecryptAlgorithm::RsaOaepSha256 => {
                let label = request.label.unwrap_or(&[]);
                rsaes_oaep_sha256_decrypt(key, request.ciphertext, label)
            }
        };
        plaintext.map_err(|_| Error::CryptoFailure("key provider decryption failed"))
    }

    /// Performs software-provider shared-secret derivation using the registered key handle.
    ///
    /// # Arguments
    ///
    /// * `request` — Derive request to execute.
    ///
    /// # Returns
    ///
    /// Derived shared secret bytes.
    ///
    /// # Errors
    ///
    /// Returns key lookup failures, peer key parsing failures, or algorithm-level derive failures.
    fn derive_shared_secret(&self, request: &KeyDeriveRequest<'_>) -> Result<Vec<u8>> {
        match request.algorithm {
            KeyDeriveAlgorithm::X25519 => {
                let private_key = self.x25519_private_key(request.handle)?;
                let peer_bytes: [u8; 32] = request
                    .peer_public_key
                    .try_into()
                    .map_err(|_| Error::InvalidLength("x25519 peer public key must be 32 bytes"))?;
                let peer_public = X25519PublicKey::from_bytes(peer_bytes);
                let shared = x25519_shared_secret(private_key, peer_public)?;
                Ok(shared.to_vec())
            }
        }
    }
}