Skip to content

Commit 1c3be65

Browse files
committed
Interim fix for MLKEM/MLDSA seed only private key ASN1 encoding. This will be replaced when formal support is available from OpenSSL for seed only encoding.
Added Tests
1 parent d58ac49 commit 1c3be65

File tree

18 files changed

+576
-48
lines changed

18 files changed

+576
-48
lines changed

interface/ffi/asn1_ni_ffi.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include <assert.h>
99
#include <stdlib.h>
10+
#include <string.h>
11+
1012
#include "../util/asn1_util.h"
1113
#include "../util/bc_err_codes.h"
1214
#include "types.h"
@@ -48,9 +50,11 @@ int32_t ASN1_encodePublicKey(asn1_ctx *asn1_ctx, key_spec *key_spec) {
4850
return (int32_t) buf_len;
4951
}
5052

51-
int32_t ASN1_encodePrivateKey(asn1_ctx *asn1_ctx, key_spec *key_spec) {
53+
int32_t ASN1_encodePrivateKey(asn1_ctx *asn1_ctx, key_spec *key_spec, const char *option_string,
54+
size_t option_string_len) {
5255
assert(asn1_ctx != NULL);
5356

57+
5458
if (key_spec == NULL) {
5559
return JO_KEY_IS_NULL;
5660
}
@@ -59,8 +63,21 @@ int32_t ASN1_encodePrivateKey(asn1_ctx *asn1_ctx, key_spec *key_spec) {
5963
return JO_KEY_SPEC_HAS_NULL_KEY;
6064
}
6165

66+
int encoding_option = PRIVATE_KEY_DEFAULT_ENCODING;
67+
68+
if (option_string != NULL) {
69+
if (strncmp(PRIVATE_KEY_DEFAULT_ENCODING_OPTION, option_string, option_string_len) == 0) {
70+
encoding_option = PRIVATE_KEY_DEFAULT_ENCODING;
71+
} else if (strncmp(PRIVATE_KEY_SEED_ONLY_ENCODING_OPTION, option_string, option_string_len) == 0) {
72+
encoding_option = PRIVATE_KEY_SEED_ONLY_ENCODING;
73+
} else {
74+
return JO_INVALID_KEY_ENCODING_OPTION;
75+
}
76+
}
77+
78+
6279
size_t buf_len = 0;
63-
if (!asn1_writer_encode_private_key(asn1_ctx, key_spec, &buf_len)) {
80+
if (!asn1_writer_encode_private_key(asn1_ctx, key_spec, &buf_len, encoding_option)) {
6481
return JO_OPENSSL_ERROR;
6582
}
6683

interface/jni/asn1_ni_jni.c

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,33 +89,64 @@ JNIEXPORT jint JNICALL Java_org_openssl_jostle_util_asn1_Asn1NiJNI_encodePublicK
8989
* Signature: (JJ)I
9090
*/
9191
JNIEXPORT jint JNICALL Java_org_openssl_jostle_util_asn1_Asn1NiJNI_encodePrivateKey
92-
(JNIEnv *env, jobject jo, jlong asn1_ref, jlong key_ref) {
92+
(JNIEnv *env, jobject jo, jlong asn1_ref, jlong key_ref, jstring _option) {
9393
UNUSED(env);
9494
UNUSED(jo);
9595

9696
asn1_ctx *ctx = (asn1_ctx *) asn1_ref;
9797
assert(ctx != NULL);
9898

9999
key_spec *key = (key_spec *) key_ref;
100+
size_t ret_code = 0;
101+
const char *option_string = NULL;
102+
int encoding_option = PRIVATE_KEY_DEFAULT_ENCODING;
103+
100104

101105
if (key == NULL) {
102-
return JO_KEY_IS_NULL;
106+
ret_code = JO_KEY_IS_NULL;
107+
goto exit;
103108
}
104109

105110
if (key->key == NULL) {
106-
return JO_KEY_SPEC_HAS_NULL_KEY;
111+
ret_code = JO_KEY_SPEC_HAS_NULL_KEY;
112+
goto exit;
107113
}
108114

109-
size_t buf_len = 0;
110-
if (!asn1_writer_encode_private_key(ctx, key, &buf_len)) {
111-
return JO_OPENSSL_ERROR;
115+
116+
if (_option != NULL) {
117+
option_string = (*env)->GetStringUTFChars(env, _option, NULL);
118+
if (OPS_FAILED_ACCESS_1 option_string == NULL) {
119+
ret_code = JO_FAILED_ACCESS_ENCODING_OPTION;
120+
goto exit;
121+
}
122+
123+
if (strcmp(PRIVATE_KEY_DEFAULT_ENCODING_OPTION, option_string) == 0) {
124+
encoding_option = PRIVATE_KEY_DEFAULT_ENCODING;
125+
} else if (strcmp(PRIVATE_KEY_SEED_ONLY_ENCODING_OPTION, option_string) == 0) {
126+
encoding_option = PRIVATE_KEY_SEED_ONLY_ENCODING;
127+
} else {
128+
ret_code = JO_INVALID_KEY_ENCODING_OPTION;
129+
goto exit;
130+
}
112131
}
113132

114-
if (OPS_INT32_OVERFLOW_1 buf_len > INT_MAX) {
115-
return JO_OUTPUT_SIZE_INT_OVERFLOW;
133+
134+
if (!asn1_writer_encode_private_key(ctx, key, &ret_code, encoding_option)) {
135+
ret_code = JO_OPENSSL_ERROR;
136+
goto exit;
116137
}
117138

118-
return (jint) buf_len;
139+
if (OPS_INT32_OVERFLOW_1 ret_code > INT_MAX) {
140+
ret_code = JO_OUTPUT_SIZE_INT_OVERFLOW;
141+
}
142+
143+
144+
exit:
145+
if (option_string != NULL) {
146+
(*env)->ReleaseStringUTFChars(env, _option, option_string);
147+
}
148+
149+
return (jint) ret_code;
119150
}
120151

121152
/*

interface/util/asn1_util.c

Lines changed: 166 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include <openssl/bio.h>
1414
#include <openssl/crypto.h>
1515
#include <openssl/x509.h>
16-
16+
#include <openssl/core_names.h>
1717
#include "bc_err_codes.h"
1818
#include "key_spec.h"
1919
#include "ops.h"
@@ -50,7 +50,6 @@ int32_t asn1_writer_get_content(asn1_ctx *ctx, uint8_t *output, size_t *written,
5050
*written = BIO_get_mem_data(ctx->buffer, &buffer);
5151

5252
if (output != NULL) {
53-
5453
if (output_len != *written) {
5554
return JO_OUTPUT_OUT_OF_RANGE;
5655
}
@@ -77,13 +76,175 @@ int32_t asn1_writer_encode_public_key(asn1_ctx *ctx, key_spec *key_spec, size_t
7776
return 1;
7877
}
7978

80-
int32_t asn1_writer_encode_private_key(asn1_ctx *ctx, key_spec *key_spec, size_t *buf_len) {
79+
static uint8_t mldsa44[] = {
80+
48, 52, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101,
81+
3, 4, 3, 17, 4, 34, -128, 32, 0, 0, 0, 24, -114,
82+
-53, -113, 95, -119, -16, 39, 21, 113, 55, -26, 114,
83+
-74, -30, -20, -39, 107, 62, 87, 20, 41, 34, 90, -18,
84+
-39, -100, -91, -10
85+
}; // Seed at byte 22 for 32
86+
87+
static uint8_t mldsa65[] = {
88+
48, 52, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101,
89+
3, 4, 3, 18, 4, 34, -128, 32, 8, -114, 106, -34, 33,
90+
-98, 52, 82, -49, 38, 83, 64, 94, -40, -4, 34, -102,
91+
114, -44, 79, 63, -108, 20, 90, 127, 120, -99, -103,
92+
-60, -128, -41, 87
93+
};
94+
95+
static uint8_t mldsa87[] = {
96+
48, 52, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101,
97+
3, 4, 3, 19, 4, 34, -128, 32, -53, 1, -80, -43, -69,
98+
-53, -31, -23, 13, 107, 112, 73, -105, -65, 120, 123,
99+
87, 81, 58, 107, 18, 65, -62, -8, 92, 36, 126, -97,
100+
-4, 64, 93, -86
101+
};
102+
103+
104+
static uint8_t mlkem512[] = {
105+
48, 84, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101, 3,
106+
4, 4, 1, 4, 66, -128, 64, -21, -50, 86, -11, -120, -111,
107+
60, 38, 98, 62, 7, 9, 64, 49, -67, -88, -37, 65, 3, -70,
108+
-119, -109, -22, 120, -69, 95, 89, 72, -79, -53, -100,
109+
-112, -103, 49, -118, 107, -10, 52, -4, 35, -69, 17, 67,
110+
58, -24, -123, -77, -70, 74, 125, -83, -33, -126, 34, 32,
111+
24, 28, -99, -53, 31, 23, 97, -59, -111
112+
};
113+
114+
static uint8_t mlkem768[] = {
115+
48, 84, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101, 3,
116+
4, 4, 2, 4, 66, -128, 64, -70, -57, -85, 79, -37, -43,
117+
102, 66, 26, -118, -59, 37, 104, 86, -89, -108, 45, -25,
118+
13, 61, -80, 10, -55, 30, 47, -57, 117, -72, -44, -66,
119+
61, 88, -128, 21, -88, 9, -1, 26, -103, 33, -26, 72, -32,
120+
-33, -72, 26, -101, 64, 28, -21, 94, 113, 22, -70, -20,
121+
-78, 23, 40, 16, 12, 60, 6, -109, -89
122+
}; // Seed at byte 22 for 64
123+
124+
125+
static uint8_t mlkem1024[] = {
126+
48, 84, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101, 3, 4,
127+
4, 3, 4, 66, -128, 64, -26, -73, 52, -19, 111, 116, 81, 6,
128+
72, -100, 120, -75, -90, -118, 44, -100, 59, 41, -35, 81,
129+
-109, -121, -42, -37, -102, 11, -9, 3, -50, -59, 30, -15,
130+
9, 46, -71, 106, -45, 2, -126, 64, 108, 114, -67, -91, -68,
131+
38, -77, 97, -56, 126, -8, -43, 127, -30, 2, -60, 95, -74,
132+
125, -101, 88, 35, -17, 89
133+
};
134+
135+
136+
int32_t asn1_writer_encode_private_key(asn1_ctx *ctx, key_spec *key_spec, size_t *buf_len, int encoding_option) {
81137
assert(ctx != NULL);
82138
assert(key_spec != NULL);
83139
assert(key_spec->key != NULL);
84140

85-
if (!i2d_PrivateKey_bio(ctx->buffer, key_spec->key)) {
86-
return 0;
141+
switch (encoding_option) {
142+
case PRIVATE_KEY_DEFAULT_ENCODING:
143+
if (!i2d_PrivateKey_bio(ctx->buffer, key_spec->key)) {
144+
return 0;
145+
}
146+
break;
147+
case PRIVATE_KEY_SEED_ONLY_ENCODING:
148+
149+
// NB hack until official support in OpenSSL
150+
// This is not intended to be robust implementation and will be replaced
151+
152+
if (EVP_PKEY_is_a(key_spec->key, "ML-DSA-44")) {
153+
uint8_t b[sizeof(mldsa44)];
154+
memcpy(b, mldsa44, sizeof(mldsa44));
155+
156+
if (
157+
1 != EVP_PKEY_get_octet_string_param(
158+
key_spec->key,
159+
OSSL_PKEY_PARAM_ML_DSA_SEED, b + 22, 32, NULL)) {
160+
return 0;
161+
}
162+
163+
if (BIO_write(ctx->buffer, b, sizeof(mldsa44)) < 0) {
164+
return 0;
165+
}
166+
OPENSSL_cleanse(b, sizeof(mldsa44));
167+
} else if (EVP_PKEY_is_a(key_spec->key, "ML-DSA-65")) {
168+
uint8_t b[sizeof(mldsa65)];
169+
memcpy(b, mldsa65, sizeof(mldsa65));
170+
171+
if (
172+
1 != EVP_PKEY_get_octet_string_param(
173+
key_spec->key,
174+
OSSL_PKEY_PARAM_ML_DSA_SEED, b + 22, 32, NULL)) {
175+
return 0;
176+
}
177+
178+
if (BIO_write(ctx->buffer, b, sizeof(mldsa65)) < 0) {
179+
return 0;
180+
}
181+
OPENSSL_cleanse(b, sizeof(mldsa65));
182+
} else if (EVP_PKEY_is_a(key_spec->key, "ML-DSA-87")) {
183+
uint8_t b[sizeof(mldsa87)];
184+
memcpy(b, mldsa87, sizeof(mldsa87));
185+
186+
if (
187+
1 != EVP_PKEY_get_octet_string_param(
188+
key_spec->key,
189+
OSSL_PKEY_PARAM_ML_DSA_SEED, b + 22, 32, NULL)) {
190+
return 0;
191+
}
192+
193+
if (BIO_write(ctx->buffer, b, sizeof(mldsa87)) < 0) {
194+
return 0;
195+
}
196+
OPENSSL_cleanse(b, sizeof(mldsa87));
197+
} else if (EVP_PKEY_is_a(key_spec->key, "ML-KEM-512")) {
198+
uint8_t b[sizeof(mlkem512)];
199+
memcpy(b, mlkem512, sizeof(mlkem512));
200+
201+
if (
202+
1 != EVP_PKEY_get_octet_string_param(
203+
key_spec->key,
204+
OSSL_PKEY_PARAM_ML_DSA_SEED, b + 22, 64, NULL)) {
205+
return JO_OPENSSL_ERROR;
206+
}
207+
208+
if (BIO_write(ctx->buffer, b, sizeof(mlkem512)) < 0) {
209+
return 0;
210+
}
211+
OPENSSL_cleanse(b, sizeof(mlkem512));
212+
} else if (EVP_PKEY_is_a(key_spec->key, "ML-KEM-768")) {
213+
uint8_t b[sizeof(mlkem768)];
214+
memcpy(b, mlkem768, sizeof(mlkem768));
215+
216+
if (
217+
1 != EVP_PKEY_get_octet_string_param(
218+
key_spec->key,
219+
OSSL_PKEY_PARAM_ML_DSA_SEED, b + 22, 64, NULL)) {
220+
return 0;
221+
}
222+
223+
if (BIO_write(ctx->buffer, b, sizeof(mlkem768)) < 0) {
224+
return 0;
225+
}
226+
OPENSSL_cleanse(b, sizeof(mlkem768));
227+
} else if (EVP_PKEY_is_a(key_spec->key, "ML-KEM-1024")) {
228+
uint8_t b[sizeof(mlkem1024)];
229+
memcpy(b, mlkem1024, sizeof(mlkem1024));
230+
231+
if (
232+
1 != EVP_PKEY_get_octet_string_param(
233+
key_spec->key,
234+
OSSL_PKEY_PARAM_ML_DSA_SEED, b + 22, 64, NULL)) {
235+
return 0;
236+
}
237+
238+
if (BIO_write(ctx->buffer, b, sizeof(mlkem1024)) < 0) {
239+
return 0;
240+
}
241+
OPENSSL_cleanse(b, sizeof(mlkem1024));
242+
} else {
243+
return 0;
244+
}
245+
break;
246+
default:
247+
return 0;
87248
}
88249

89250

interface/util/asn1_util.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111

1212
#include "key_spec.h"
1313

14+
#define PRIVATE_KEY_DEFAULT_ENCODING 0
15+
#define PRIVATE_KEY_SEED_ONLY_ENCODING 1
16+
17+
#define PRIVATE_KEY_DEFAULT_ENCODING_OPTION "default"
18+
#define PRIVATE_KEY_SEED_ONLY_ENCODING_OPTION "seed_only"
1419

1520
typedef struct asn1_ctx {
1621
BIO *buffer;
@@ -47,10 +52,11 @@ int32_t asn1_writer_encode_public_key(asn1_ctx *ctx, key_spec *key_spec, size_t
4752
* @param ctx the ctx
4853
* @param key_spec the key spec
4954
* @param buf_len receiver for the length of data in the buffer
55+
* @param encoding_option
5056
*
5157
* @return 1 = success, 0 = failure
5258
*/
53-
int32_t asn1_writer_encode_private_key(asn1_ctx *ctx, key_spec *key_spec, size_t *buf_len);
59+
int32_t asn1_writer_encode_private_key(asn1_ctx *ctx, key_spec *key_spec, size_t *buf_len, int encoding_option);
5460

5561

5662
/**

interface/util/bc_err_codes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@
110110
#define JO_KDF_PBE_ITER_NEGATIVE -83
111111
#define JO_KDF_PBE_UNKNOWN_DIGEST -84
112112

113+
#define JO_INVALID_KEY_ENCODING_OPTION -85
114+
#define JO_FAILED_ACCESS_ENCODING_OPTION -86
113115

114116

115117
#endif //BC_OSSL_ERR_CODES_H

jostle/src/main/java/org/openssl/jostle/jcajce/provider/ErrorCode.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ public enum ErrorCode
102102
JO_KDF_PBE_ITER_NEGATIVE(-83),
103103
JO_KDF_PBE_UNKNOWN_DIGEST(-84),
104104

105+
JO_INVALID_KEY_ENCODING_OPTION(-85),
106+
JO_FAILED_ACCESS_ENCODING_OPTION(-86),
105107

106108
JO_UNKNOWN(Integer.MIN_VALUE);
107109

jostle/src/main/java/org/openssl/jostle/jcajce/provider/mldsa/JOMLDSAPrivateKey.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
import org.openssl.jostle.jcajce.spec.OSSLKeyType;
2020
import org.openssl.jostle.jcajce.spec.PKEYKeySpec;
2121
import org.openssl.jostle.util.asn1.ASNEncoder;
22+
import org.openssl.jostle.util.asn1.PrivateKeyOptions;
2223

23-
class JOMLDSAPrivateKey extends AsymmetricKeyImpl implements MLDSAPrivateKey,OSSLKey
24+
class JOMLDSAPrivateKey extends AsymmetricKeyImpl implements MLDSAPrivateKey, OSSLKey
2425
{
2526
final boolean seedOnly;
2627

@@ -51,7 +52,11 @@ public String getFormat()
5152
@Override
5253
public byte[] getEncoded()
5354
{
54-
return ASNEncoder.asPrivateKeyInfo(spec);
55+
if (seedOnly)
56+
{
57+
return ASNEncoder.asPrivateKeyInfo(spec, PrivateKeyOptions.SEED_ONLY);
58+
}
59+
return ASNEncoder.asPrivateKeyInfo(spec, PrivateKeyOptions.DEFAULT);
5560
}
5661

5762
public byte[] getSeed()
@@ -88,7 +93,7 @@ public MLDSAPrivateKey getPrivateKey(boolean preferSeedOnly)
8893
new PKEYKeySpec(
8994
NISelector.MLDSAServiceNI.handleErrors(
9095
NISelector.MLDSAServiceNI.generateKeyPair(type.getKsType(), seed, seed.length)
91-
), type)
96+
), type), preferSeedOnly
9297
);
9398
}
9499
}

0 commit comments

Comments
 (0)