Skip to content

Commit 3c6386b

Browse files
authored
Implement proper one-shot key agreement with public key bytes
1 parent 020b966 commit 3c6386b

4 files changed

Lines changed: 120 additions & 29 deletions

File tree

src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X25519.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,34 @@ internal static void X25519DeriveSecret(
152152
throw new CryptographicException();
153153
}
154154
}
155+
156+
[LibraryImport(Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_X25519DeriveSecretWithSubjectPublicKeyInfo")]
157+
private static partial int X25519DeriveSecretWithSubjectPublicKeyInfoNative(
158+
SafeX25519PrivateKeyHandle privateKey,
159+
ReadOnlySpan<byte> subjectPublicKeyInfo,
160+
int subjectPublicKeyInfoLength,
161+
Span<byte> destination,
162+
int destinationLength);
163+
164+
internal static void X25519DeriveSecretWithSubjectPublicKeyInfo(
165+
SafeX25519PrivateKeyHandle privateKey,
166+
ReadOnlySpan<byte> subjectPublicKeyInfo,
167+
Span<byte> destination)
168+
{
169+
const int Success = 1;
170+
171+
int result = X25519DeriveSecretWithSubjectPublicKeyInfoNative(
172+
privateKey,
173+
subjectPublicKeyInfo,
174+
subjectPublicKeyInfo.Length,
175+
destination,
176+
destination.Length);
177+
178+
if (result != Success)
179+
{
180+
throw new CryptographicException();
181+
}
182+
}
155183
}
156184
}
157185

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X25519DiffieHellmanImplementation.Android.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,24 @@ protected override void DeriveRawSecretAgreementCore(ReadOnlySpan<byte> otherPar
6161
Debug.Assert(destination.Length == SecretAgreementSizeInBytes);
6262
ThrowIfPrivateNeeded();
6363

64-
using (SafeX25519PublicKeyHandle importedPublicKey = ImportPublicKeyAsHandle(otherPartyPublicKey))
64+
unsafe
6565
{
66-
DeriveRawSecretAgreementCore(_privateKey, importedPublicKey, destination);
66+
Span<byte> spki = stackalloc byte[SpkiSizeInBytes];
67+
68+
bool encoded = TryWriteSubjectPublicKeyInfo(
69+
spki,
70+
otherPartyPublicKey,
71+
static (source, buffer) => source.CopyTo(buffer),
72+
out int written);
73+
74+
// SPKI encoding is either right or wrong, there isn't "optional" things that can be written down. So it
75+
// should be precisely sized.
76+
if (!encoded || written != SpkiSizeInBytes)
77+
{
78+
throw new CryptographicException();
79+
}
80+
81+
Interop.AndroidCrypto.X25519DeriveSecretWithSubjectPublicKeyInfo(_privateKey, spki, destination);
6782
}
6883
}
6984

src/native/libs/System.Security.Cryptography.Native.Android/pal_x25519.c

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <string.h>
88

9+
static jobject ImportSubjectPublicKeyInfo(JNIEnv* env, const uint8_t* buffer, int32_t bufferLength);
910
static int32_t ExportEncodedKey(jobject key, uint8_t* buffer, int32_t bufferLength, int32_t* bytesWritten);
1011

1112
int32_t AndroidCryptoNative_X25519IsSupported(void)
@@ -129,34 +130,9 @@ jobject AndroidCryptoNative_X25519ImportSubjectPublicKeyInfo(const uint8_t* buff
129130
abort_if_negative_integer_argument(bufferLength);
130131

131132
JNIEnv* env = GetJNIEnv();
132-
jobject ret = NULL;
133-
134-
INIT_LOCALS(loc, algorithmName, keyFactory, spkiBytes, keySpec, publicKey);
135-
136-
loc[algorithmName] = make_java_string(env, "XDH");
137-
loc[keyFactory] = (*env)->CallStaticObjectMethod(env, g_KeyFactoryClass, g_KeyFactoryGetInstanceMethod, loc[algorithmName]);
138-
ON_EXCEPTION_PRINT_AND_GOTO(cleanup);
139-
140-
loc[spkiBytes] = make_java_byte_array(env, bufferLength);
141-
(*env)->SetByteArrayRegion(env, loc[spkiBytes], 0, bufferLength, (const jbyte*)buffer);
142-
ON_EXCEPTION_PRINT_AND_GOTO(cleanup);
143-
144-
loc[keySpec] = (*env)->NewObject(env, g_X509EncodedKeySpecClass, g_X509EncodedKeySpecCtor, loc[spkiBytes]);
145-
ON_EXCEPTION_PRINT_AND_GOTO(cleanup);
146-
147-
loc[publicKey] = (*env)->CallObjectMethod(env, loc[keyFactory], g_KeyFactoryGenPublicMethod, loc[keySpec]);
148-
ON_EXCEPTION_PRINT_AND_GOTO(cleanup);
149-
150-
if (loc[publicKey] == NULL)
151-
{
152-
goto cleanup;
153-
}
154-
155-
ret = ToGRef(env, loc[publicKey]);
156-
loc[publicKey] = NULL;
133+
jobject publicKey = ImportSubjectPublicKeyInfo(env, buffer, bufferLength);
134+
jobject ret = ToGRef(env, publicKey);
157135

158-
cleanup:
159-
RELEASE_LOCALS(loc, env);
160136
return ret;
161137
}
162138

@@ -259,6 +235,38 @@ int32_t AndroidCryptoNative_X25519DeriveSecret(
259235
return ret;
260236
}
261237

238+
int32_t AndroidCryptoNative_X25519DeriveSecretWithSubjectPublicKeyInfo(
239+
jobject privateKey,
240+
const uint8_t* buffer,
241+
int32_t bufferLength,
242+
uint8_t* destination,
243+
int32_t destinationLength)
244+
{
245+
abort_if_invalid_pointer_argument(privateKey);
246+
abort_if_invalid_pointer_argument(buffer);
247+
abort_if_invalid_pointer_argument(destination);
248+
abort_if_negative_integer_argument(bufferLength);
249+
abort_if_negative_integer_argument(destinationLength);
250+
251+
JNIEnv* env = GetJNIEnv();
252+
int32_t ret = FAIL;
253+
254+
INIT_LOCALS(loc, publicKey);
255+
256+
loc[publicKey] = ImportSubjectPublicKeyInfo(env, buffer, bufferLength);
257+
258+
if (loc[publicKey] == NULL)
259+
{
260+
goto cleanup;
261+
}
262+
263+
ret = AndroidCryptoNative_X25519DeriveSecret(privateKey, loc[publicKey], destination, destinationLength);
264+
265+
cleanup:
266+
RELEASE_LOCALS(loc, env);
267+
return ret;
268+
}
269+
262270
static int32_t ExportEncodedKey(jobject key, uint8_t* buffer, int32_t bufferLength, int32_t* bytesWritten)
263271
{
264272
abort_if_invalid_pointer_argument(key);
@@ -301,3 +309,36 @@ static int32_t ExportEncodedKey(jobject key, uint8_t* buffer, int32_t bufferLeng
301309
RELEASE_LOCALS(loc, env);
302310
return ret;
303311
}
312+
313+
static jobject ImportSubjectPublicKeyInfo(JNIEnv* env, const uint8_t* buffer, int32_t bufferLength)
314+
{
315+
jobject ret = NULL;
316+
317+
INIT_LOCALS(loc, algorithmName, keyFactory, spkiBytes, keySpec, publicKey);
318+
319+
loc[algorithmName] = make_java_string(env, "XDH");
320+
loc[keyFactory] = (*env)->CallStaticObjectMethod(env, g_KeyFactoryClass, g_KeyFactoryGetInstanceMethod, loc[algorithmName]);
321+
ON_EXCEPTION_PRINT_AND_GOTO(cleanup);
322+
323+
loc[spkiBytes] = make_java_byte_array(env, bufferLength);
324+
(*env)->SetByteArrayRegion(env, loc[spkiBytes], 0, bufferLength, (const jbyte*)buffer);
325+
ON_EXCEPTION_PRINT_AND_GOTO(cleanup);
326+
327+
loc[keySpec] = (*env)->NewObject(env, g_X509EncodedKeySpecClass, g_X509EncodedKeySpecCtor, loc[spkiBytes]);
328+
ON_EXCEPTION_PRINT_AND_GOTO(cleanup);
329+
330+
loc[publicKey] = (*env)->CallObjectMethod(env, loc[keyFactory], g_KeyFactoryGenPublicMethod, loc[keySpec]);
331+
ON_EXCEPTION_PRINT_AND_GOTO(cleanup);
332+
333+
if (loc[publicKey] == NULL)
334+
{
335+
goto cleanup;
336+
}
337+
338+
ret = loc[publicKey];
339+
loc[publicKey] = NULL;
340+
341+
cleanup:
342+
RELEASE_LOCALS(loc, env);
343+
return ret;
344+
}

src/native/libs/System.Security.Cryptography.Native.Android/pal_x25519.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,10 @@ PALEXPORT int32_t AndroidCryptoNative_X25519DeriveSecret(
3333
jobject publicKey,
3434
uint8_t* destination,
3535
int32_t destinationLength);
36+
37+
PALEXPORT int32_t AndroidCryptoNative_X25519DeriveSecretWithSubjectPublicKeyInfo(
38+
jobject privateKey,
39+
const uint8_t* buffer,
40+
int32_t bufferLength,
41+
uint8_t* destination,
42+
int32_t destinationLength);

0 commit comments

Comments
 (0)