Skip to content

Commit 8617f00

Browse files
committed
More peer review fixes
1 parent c495f1c commit 8617f00

3 files changed

Lines changed: 102 additions & 19 deletions

File tree

wrapper/CSharp/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ Run with system-wide install:
7777
mono wolfcrypttest.exe
7878
```
7979

80-
Run with local-only install (from the wolfSSL root directory):
80+
Run with local-only install. The compile step above produced
81+
`wolfcrypttest.exe` inside `wrapper/CSharp/`; this run command is invoked
82+
from the wolfSSL project root so the relative paths line up:
8183

8284
```
8385
LD_LIBRARY_PATH=./install/lib mono wrapper/CSharp/wolfcrypttest.exe

wrapper/CSharp/wolfCrypt-Test/wolfCrypt-Test.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,22 @@ private static void hpke_test(wolfcrypt.HpkeKem kem,
10191019
}
10201020
Console.WriteLine("Mismatched AAD test PASSED (correctly rejected).");
10211021

1022+
/* Null info/aad round-trip - exercises the null-marshaling path through P/Invoke */
1023+
Console.WriteLine("Testing HpkeSealBase/OpenBase with null info and aad...");
1024+
byte[] encNull = wolfcrypt.HpkeSealBase(hpke, receiverKey,
1025+
null, null, plaintext, kem);
1026+
if (encNull == null)
1027+
{
1028+
throw new Exception("HpkeSealBase with null info/aad failed");
1029+
}
1030+
byte[] decNull = wolfcrypt.HpkeOpenBase(hpke, receiverKey,
1031+
encNull, null, null, kem);
1032+
if (decNull == null || !wolfcrypt.ByteArrayVerify(plaintext, decNull))
1033+
{
1034+
throw new Exception("HpkeOpenBase with null info/aad: round-trip failed");
1035+
}
1036+
Console.WriteLine("Null info/aad round-trip test PASSED.");
1037+
10221038
/* Negative test: invalid ptLen should fail */
10231039
Console.WriteLine("Testing HpkeOpenBase with invalid ptLen...");
10241040
badResult = wolfcrypt.HpkeOpenBase(hpke, receiverKey,

wrapper/CSharp/wolfSSL_CSharp/wolfCrypt.cs

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121

2222
using System;
23+
using System.Collections.Concurrent;
2324
using System.Runtime.InteropServices;
2425
using System.Security.Cryptography;
2526
using System.Text;
@@ -361,6 +362,10 @@ public class wolfcrypt
361362
private extern static int wc_curve25519_make_key(IntPtr rng, int keysize, IntPtr key);
362363
[DllImport(wolfssl_dll)]
363364
private extern static int wc_curve25519_shared_secret(IntPtr privateKey, IntPtr publicKey, byte[] outSharedSecret, ref int outlen);
365+
/* Only available when wolfSSL is built with WOLFSSL_CURVE25519_BLINDING.
366+
* Calls are wrapped in try/catch to tolerate builds without it. */
367+
[DllImport(wolfssl_dll)]
368+
private extern static int wc_curve25519_set_rng(IntPtr key, IntPtr rng);
364369

365370
/* ASN.1 DER format */
366371
[DllImport(wolfssl_dll)]
@@ -400,6 +405,10 @@ public class wolfcrypt
400405
private extern static int wc_curve25519_make_key(IntPtr rng, int keysize, IntPtr key);
401406
[DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)]
402407
private extern static int wc_curve25519_shared_secret(IntPtr privateKey, IntPtr publicKey, byte[] outSharedSecret, ref int outlen);
408+
/* Only available when wolfSSL is built with WOLFSSL_CURVE25519_BLINDING.
409+
* Calls are wrapped in try/catch to tolerate builds without it. */
410+
[DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)]
411+
private extern static int wc_curve25519_set_rng(IntPtr key, IntPtr rng);
403412

404413
/* ASN.1 DER format */
405414
[DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)]
@@ -3257,13 +3266,31 @@ public enum HpkeAead : ushort {
32573266
}
32583267

32593268
/* HPKE Nt (GCM tag length) */
3260-
private static readonly int HPKE_Nt = 16;
3269+
private const int HPKE_Nt = 16;
3270+
3271+
/* HPKE max encoded public-key length (matches HPKE_Npk_MAX in hpke.h) */
3272+
private const int HPKE_Npk_MAX = 133;
32613273

32623274
/* Hpke struct is ~80 bytes on 64-bit (see hpke.h). Allocate 512 bytes
32633275
* (6x headroom) to accommodate platform alignment and future growth.
32643276
* If the native struct ever exceeds this, wc_HpkeInit will write OOB —
32653277
* keep in sync with hpke.h if the struct grows significantly. */
3266-
private static readonly int HPKE_STRUCT_SZ = 512;
3278+
private const int HPKE_STRUCT_SZ = 512;
3279+
3280+
/* Per-Hpke-context state owned by the C# wrapper.
3281+
* The RNG must outlive any keypair created with this context: when
3282+
* wolfSSL is built with WOLFSSL_CURVE25519_BLINDING, wc_curve25519_make_key
3283+
* stores the rng pointer inside the keypair (via wc_curve25519_set_rng)
3284+
* and re-uses it for blinding during shared-secret operations. If the
3285+
* wrapper freed the rng after key generation, that pointer would dangle
3286+
* and the next seal/open would fail with RNG_FAILURE_E (-199). */
3287+
private struct HpkeContextState
3288+
{
3289+
public IntPtr rng;
3290+
public HpkeKem kem;
3291+
}
3292+
private static readonly ConcurrentDictionary<IntPtr, HpkeContextState> hpkeContexts =
3293+
new ConcurrentDictionary<IntPtr, HpkeContextState>();
32673294

32683295
/// <summary>
32693296
/// Get the enc (encapsulated key) length for a given KEM
@@ -3272,13 +3299,15 @@ public enum HpkeAead : ushort {
32723299
/// <returns>Length in bytes</returns>
32733300
private static ushort HpkeEncLen(HpkeKem kem)
32743301
{
3302+
/* Values must match DHKEM_*_ENC_LEN macros in wolfssl/wolfcrypt/hpke.h.
3303+
* Not P/Invoked because wc_HpkeKemGetEncLen is currently WOLFSSL_LOCAL. */
32753304
switch (kem)
32763305
{
3277-
case HpkeKem.DHKEM_P256_HKDF_SHA256: return 65;
3278-
case HpkeKem.DHKEM_P384_HKDF_SHA384: return 97;
3279-
case HpkeKem.DHKEM_P521_HKDF_SHA512: return 133;
3280-
case HpkeKem.DHKEM_X25519_HKDF_SHA256: return 32;
3281-
case HpkeKem.DHKEM_X448_HKDF_SHA512: return 56;
3306+
case HpkeKem.DHKEM_P256_HKDF_SHA256: return 65; /* DHKEM_P256_ENC_LEN */
3307+
case HpkeKem.DHKEM_P384_HKDF_SHA384: return 97; /* DHKEM_P384_ENC_LEN */
3308+
case HpkeKem.DHKEM_P521_HKDF_SHA512: return 133; /* DHKEM_P521_ENC_LEN */
3309+
case HpkeKem.DHKEM_X25519_HKDF_SHA256: return 32; /* DHKEM_X25519_ENC_LEN */
3310+
case HpkeKem.DHKEM_X448_HKDF_SHA512: return 56; /* DHKEM_X448_ENC_LEN */
32823311
default: return 0;
32833312
}
32843313
}
@@ -3293,6 +3322,7 @@ private static ushort HpkeEncLen(HpkeKem kem)
32933322
public static IntPtr HpkeInit(HpkeKem kem, HpkeKdf kdf, HpkeAead aead)
32943323
{
32953324
IntPtr hpke = IntPtr.Zero;
3325+
IntPtr rng = IntPtr.Zero;
32963326

32973327
try
32983328
{
@@ -3313,10 +3343,27 @@ public static IntPtr HpkeInit(HpkeKem kem, HpkeKdf kdf, HpkeAead aead)
33133343
Marshal.FreeHGlobal(hpke);
33143344
return IntPtr.Zero;
33153345
}
3346+
3347+
/* Allocate a persistent RNG that lives as long as this context.
3348+
* Required so curve25519 keypairs (with blinding) retain a valid
3349+
* rng pointer for shared-secret operations. */
3350+
rng = RandomNew();
3351+
if (rng == IntPtr.Zero)
3352+
{
3353+
log(ERROR_LOG, "HPKE init: RNG allocation failed");
3354+
Marshal.FreeHGlobal(hpke);
3355+
return IntPtr.Zero;
3356+
}
3357+
3358+
hpkeContexts[hpke] = new HpkeContextState { rng = rng, kem = kem };
33163359
}
33173360
catch (Exception e)
33183361
{
33193362
log(ERROR_LOG, "HPKE init exception " + e.ToString());
3363+
if (rng != IntPtr.Zero)
3364+
{
3365+
RandomFree(rng);
3366+
}
33203367
if (hpke != IntPtr.Zero)
33213368
{
33223369
Marshal.FreeHGlobal(hpke);
@@ -3335,7 +3382,6 @@ public static IntPtr HpkeInit(HpkeKem kem, HpkeKdf kdf, HpkeAead aead)
33353382
public static IntPtr HpkeGenerateKeyPair(IntPtr hpke)
33363383
{
33373384
IntPtr keypair = IntPtr.Zero;
3338-
IntPtr rng = IntPtr.Zero;
33393385

33403386
try
33413387
{
@@ -3345,29 +3391,43 @@ public static IntPtr HpkeGenerateKeyPair(IntPtr hpke)
33453391
return IntPtr.Zero;
33463392
}
33473393

3348-
rng = RandomNew();
3349-
if (rng == IntPtr.Zero)
3394+
HpkeContextState state;
3395+
if (!hpkeContexts.TryGetValue(hpke, out state) || state.rng == IntPtr.Zero)
33503396
{
3351-
log(ERROR_LOG, "HPKE generate keypair: RNG init failed");
3397+
log(ERROR_LOG, "HPKE generate keypair: no RNG associated with context");
33523398
return IntPtr.Zero;
33533399
}
33543400

3355-
int ret = wc_HpkeGenerateKeyPair(hpke, ref keypair, rng);
3401+
int ret = wc_HpkeGenerateKeyPair(hpke, ref keypair, state.rng);
33563402
if (ret != 0)
33573403
{
33583404
log(ERROR_LOG, "HPKE generate keypair failed " + ret + ": " + GetError(ret));
3359-
keypair = IntPtr.Zero;
3405+
return IntPtr.Zero;
3406+
}
3407+
3408+
/* For X25519, explicitly bind the persistent rng to the keypair.
3409+
* wc_curve25519_make_key already does this internally when wolfSSL
3410+
* is built with WOLFSSL_CURVE25519_BLINDING, but the explicit call
3411+
* here documents the lifetime requirement and is defensive against
3412+
* future changes. The function only exists when blinding is built
3413+
* in, so swallow EntryPointNotFoundException for builds without it. */
3414+
if (state.kem == HpkeKem.DHKEM_X25519_HKDF_SHA256 && keypair != IntPtr.Zero)
3415+
{
3416+
try
3417+
{
3418+
wc_curve25519_set_rng(keypair, state.rng);
3419+
}
3420+
catch (EntryPointNotFoundException)
3421+
{
3422+
/* wolfSSL built without WOLFSSL_CURVE25519_BLINDING; nothing to do */
3423+
}
33603424
}
33613425
}
33623426
catch (Exception e)
33633427
{
33643428
log(ERROR_LOG, "HPKE generate keypair exception " + e.ToString());
33653429
keypair = IntPtr.Zero;
33663430
}
3367-
finally
3368-
{
3369-
if (rng != IntPtr.Zero) RandomFree(rng);
3370-
}
33713431

33723432
return keypair;
33733433
}
@@ -3388,7 +3448,7 @@ public static byte[] HpkeSerializePublicKey(IntPtr hpke, IntPtr keypair)
33883448
return null;
33893449
}
33903450

3391-
ushort outSz = 133; /* HPKE_Npk_MAX */
3451+
ushort outSz = (ushort)HPKE_Npk_MAX;
33923452
byte[] outBuf = new byte[outSz];
33933453

33943454
int ret = wc_HpkeSerializePublicKey(hpke, keypair, outBuf, ref outSz);
@@ -3466,6 +3526,11 @@ public static void HpkeFree(IntPtr hpke)
34663526
{
34673527
if (hpke != IntPtr.Zero)
34683528
{
3529+
HpkeContextState state;
3530+
if (hpkeContexts.TryRemove(hpke, out state) && state.rng != IntPtr.Zero)
3531+
{
3532+
RandomFree(state.rng);
3533+
}
34693534
Marshal.FreeHGlobal(hpke);
34703535
}
34713536
}

0 commit comments

Comments
 (0)