pqc_asn1 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +11 -0
- data/ext/pqc_asn1/der.c +23 -18
- data/ext/pqc_asn1/extconf.rb +11 -9
- data/ext/pqc_asn1/internal.h +116 -0
- data/ext/pqc_asn1/pem.c +16 -14
- data/ext/pqc_asn1/pqc_asn1.c +12 -4
- data/ext/pqc_asn1/pqcsb.c +932 -0
- data/ext/pqc_asn1/pqcsb.h +358 -0
- data/ext/pqc_asn1/pqcsb_config.h +18 -0
- data/ext/pqc_asn1/secure_buffer.c +251 -731
- data/ext/pqc_asn1/secure_buffer.h +22 -63
- data/lib/pqc_asn1/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c30efe239594f245393a2ec72d0794c685d469d064be4da9f480343cf25435fd
|
|
4
|
+
data.tar.gz: 733d22810457f379abc602ff94fbac6c510df30e2e8f33fe0006d2f0a55af4ec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad3dcf7c1a955dd30c59c10e96708dc9aa806af18373625f4301843be5d06c488b157ea9f86b33d30d31c3c6a0c41b3e82e252862713ee8f5e0b17a2c2fcb67c
|
|
7
|
+
data.tar.gz: 767ceaedb4cfddf337a0367b2a6fa0ea3786203a57d8113904d4ef0dbcf460d1ae7fc68bae90767924768fdce6ab5d751cac701fd63a082d311aa722f2e36b90
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.1] — 2026-03-19
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **libpqcsb integration**: Replaced the 997-line inline secure buffer
|
|
15
|
+
implementation with the standalone [libpqcsb](https://github.com/msuliq/libpqcsb)
|
|
16
|
+
C library. This eliminates code duplication and improves maintainability.
|
|
17
|
+
- SecureBuffer now uses libpqcsb's opaque buffer API, guard-based access control,
|
|
18
|
+
and improved memory protection.
|
|
19
|
+
- Updated `extconf.rb` to vendor libpqcsb with pkg-config fallback.
|
|
20
|
+
- All existing SecureBuffer methods and their semantics remain unchanged.
|
|
21
|
+
- Public API is fully backward-compatible.
|
|
22
|
+
- Updated vendored libpqcasn1 to version 0.1.5 (from 0.1.3).
|
|
23
|
+
|
|
10
24
|
### Fixed
|
|
11
25
|
|
|
12
26
|
- `SecureBuffer#wipe!` now returns `self` instead of `nil`, enabling method
|
data/README.md
CHANGED
|
@@ -249,6 +249,17 @@ Error categories:
|
|
|
249
249
|
`PqcAsn1::DER::KEY_SIZES` maps each OID to `{ public:, secret: }` byte counts.
|
|
250
250
|
Used automatically when `validate: true` is passed to `build_spki` / `build_pkcs8`.
|
|
251
251
|
|
|
252
|
+
## Implementation
|
|
253
|
+
|
|
254
|
+
The gem is implemented in C with no OpenSSL dependency:
|
|
255
|
+
|
|
256
|
+
- **libpqcasn1** (v0.1.5) — Vendored pure C ASN.1/DER codec for key serialization
|
|
257
|
+
- **libpqcsb** (v0.1.0) — Vendored secure memory buffer library providing `mmap`-backed
|
|
258
|
+
allocation, guard pages, memory locking, and secure zeroing
|
|
259
|
+
|
|
260
|
+
Both libraries are vendored; the gem builds a single native extension with no external
|
|
261
|
+
C dependencies beyond POSIX APIs and the C standard library.
|
|
262
|
+
|
|
252
263
|
## Requirements
|
|
253
264
|
|
|
254
265
|
- Ruby >= 2.7.2
|
data/ext/pqc_asn1/der.c
CHANGED
|
@@ -265,7 +265,7 @@ typedef struct {
|
|
|
265
265
|
size_t written;
|
|
266
266
|
} pkcs8_build_ctx_t;
|
|
267
267
|
|
|
268
|
-
static
|
|
268
|
+
static pqcsb_status_t
|
|
269
269
|
pkcs8_build_fill(uint8_t *data, size_t len, void *ctx_ptr)
|
|
270
270
|
{
|
|
271
271
|
pkcs8_build_ctx_t *ctx = (pkcs8_build_ctx_t *)ctx_ptr;
|
|
@@ -276,6 +276,7 @@ pkcs8_build_fill(uint8_t *data, size_t len, void *ctx_ptr)
|
|
|
276
276
|
ctx->sk_ptr, ctx->sk_len,
|
|
277
277
|
ctx->pk_ptr, ctx->pk_len,
|
|
278
278
|
&ctx->written);
|
|
279
|
+
return ctx->rc == PQC_ASN1_OK ? PQCSB_OK : PQCSB_ERR_ALLOC;
|
|
279
280
|
}
|
|
280
281
|
|
|
281
282
|
/* ------------------------------------------------------------------ */
|
|
@@ -289,6 +290,7 @@ pkcs8_build_fill(uint8_t *data, size_t len, void *ctx_ptr)
|
|
|
289
290
|
typedef struct {
|
|
290
291
|
VALUE rb_oid_der;
|
|
291
292
|
pqcsb_buf_t *sk_buf;
|
|
293
|
+
pqcsb_read_guard_t guard; /* Read access guard to sk_buf */
|
|
292
294
|
VALUE result;
|
|
293
295
|
pqc_asn1_status_t rc;
|
|
294
296
|
/* Extended fields (NULL/0 when not using keywords). */
|
|
@@ -310,19 +312,19 @@ pkcs8_sb_build_body(VALUE arg)
|
|
|
310
312
|
pqc_asn1_status_t rc = pqc_asn1_pkcs8_size_ex(
|
|
311
313
|
oid_ptr, oid_len,
|
|
312
314
|
ctx->params_ptr, ctx->params_len,
|
|
313
|
-
ctx->
|
|
315
|
+
ctx->guard.len, ctx->pk_len, &total);
|
|
314
316
|
if (rc != PQC_ASN1_OK)
|
|
315
317
|
raise_status(rc, NULL);
|
|
316
318
|
|
|
317
319
|
pkcs8_build_ctx_t build_ctx = {
|
|
318
320
|
oid_ptr, oid_len,
|
|
319
321
|
ctx->params_ptr, ctx->params_len,
|
|
320
|
-
ctx->
|
|
322
|
+
ctx->guard.data, ctx->guard.len,
|
|
321
323
|
ctx->pk_ptr, ctx->pk_len,
|
|
322
324
|
PQC_ASN1_OK, 0
|
|
323
325
|
};
|
|
324
|
-
ctx->result =
|
|
325
|
-
|
|
326
|
+
ctx->result = pqcsb_rb_create_inplace(pqcsb_class(), total,
|
|
327
|
+
pkcs8_build_fill, &build_ctx);
|
|
326
328
|
ctx->rc = build_ctx.rc;
|
|
327
329
|
return Qnil;
|
|
328
330
|
}
|
|
@@ -330,7 +332,8 @@ pkcs8_sb_build_body(VALUE arg)
|
|
|
330
332
|
static VALUE
|
|
331
333
|
pkcs8_sb_ensure_end_read(VALUE arg)
|
|
332
334
|
{
|
|
333
|
-
|
|
335
|
+
pkcs8_sb_ctx_t *ctx = (pkcs8_sb_ctx_t *)arg;
|
|
336
|
+
pqcsb_end_read(&ctx->guard);
|
|
334
337
|
return Qnil;
|
|
335
338
|
}
|
|
336
339
|
|
|
@@ -385,21 +388,23 @@ rb_der_build_pkcs8(int argc, VALUE *argv, VALUE _self)
|
|
|
385
388
|
/* Accept SecureBuffer input: read directly from its mmap region
|
|
386
389
|
* without ever placing secret key material on the Ruby heap. */
|
|
387
390
|
if (rb_obj_is_kind_of(rb_sk, pqcsb_class())) {
|
|
388
|
-
pqcsb_buf_t *sk_buf;
|
|
389
|
-
|
|
390
|
-
if (sk_buf->wiped)
|
|
391
|
+
pqcsb_buf_t *sk_buf = (pqcsb_buf_t *)RTYPEDDATA_DATA(rb_sk);
|
|
392
|
+
if (pqcsb_is_wiped(sk_buf))
|
|
391
393
|
rb_raise(rb_eRuntimeError, "SecureBuffer has been wiped");
|
|
392
394
|
|
|
393
395
|
pkcs8_sb_ctx_t sb_ctx = {
|
|
394
|
-
rb_oid_der, sk_buf, Qnil, PQC_ASN1_OK,
|
|
396
|
+
rb_oid_der, sk_buf, {NULL, 0, PQCSB_OK, NULL}, Qnil, PQC_ASN1_OK,
|
|
395
397
|
params_ptr, params_len, pk_ptr, pk_len
|
|
396
398
|
};
|
|
397
|
-
pqcsb_begin_read(sk_buf);
|
|
399
|
+
sb_ctx.guard = pqcsb_begin_read(sk_buf);
|
|
400
|
+
if (sb_ctx.guard.status != PQCSB_OK)
|
|
401
|
+
rb_raise(rb_eRuntimeError, "pqcsb_begin_read failed");
|
|
402
|
+
|
|
398
403
|
rb_ensure(pkcs8_sb_build_body, (VALUE)&sb_ctx,
|
|
399
|
-
pkcs8_sb_ensure_end_read, (VALUE)
|
|
404
|
+
pkcs8_sb_ensure_end_read, (VALUE)&sb_ctx);
|
|
400
405
|
|
|
401
406
|
if (sb_ctx.rc != PQC_ASN1_OK) {
|
|
402
|
-
|
|
407
|
+
pqcsb_rb_wipe(sb_ctx.result);
|
|
403
408
|
raise_status(sb_ctx.rc, NULL);
|
|
404
409
|
}
|
|
405
410
|
return sb_ctx.result;
|
|
@@ -428,12 +433,12 @@ rb_der_build_pkcs8(int argc, VALUE *argv, VALUE _self)
|
|
|
428
433
|
pk_ptr, pk_len,
|
|
429
434
|
PQC_ASN1_OK, 0
|
|
430
435
|
};
|
|
431
|
-
VALUE result =
|
|
432
|
-
|
|
436
|
+
VALUE result = pqcsb_rb_create_inplace(pqcsb_class(), total,
|
|
437
|
+
pkcs8_build_fill, &build_ctx);
|
|
433
438
|
if (build_ctx.rc != PQC_ASN1_OK) {
|
|
434
439
|
/* Wipe the SecureBuffer so garbage content doesn't escape if
|
|
435
440
|
* the caller rescues the exception. */
|
|
436
|
-
|
|
441
|
+
pqcsb_rb_wipe(result);
|
|
437
442
|
raise_status(build_ctx.rc, NULL);
|
|
438
443
|
}
|
|
439
444
|
return result;
|
|
@@ -527,7 +532,7 @@ pkcs8_parse_use_cb(VALUE rb_bytes, VALUE data2,
|
|
|
527
532
|
? frozen_bin_str((const char *)alg_params, (long)alg_params_len)
|
|
528
533
|
: Qnil;
|
|
529
534
|
|
|
530
|
-
ctx->rb_key =
|
|
535
|
+
ctx->rb_key = pqcsb_rb_create(pqcsb_class(), sk_bytes, sk_len);
|
|
531
536
|
|
|
532
537
|
ctx->rb_pub = pub_key && pub_key_len > 0
|
|
533
538
|
? frozen_bin_str((const char *)pub_key, (long)pub_key_len)
|
|
@@ -582,7 +587,7 @@ rb_der_parse_pkcs8(UNUSED VALUE _self, VALUE rb_der)
|
|
|
582
587
|
? frozen_bin_str((const char *)alg_params, (long)alg_params_len)
|
|
583
588
|
: Qnil;
|
|
584
589
|
|
|
585
|
-
VALUE key_val =
|
|
590
|
+
VALUE key_val = pqcsb_rb_create(pqcsb_class(), sk_bytes, sk_len);
|
|
586
591
|
|
|
587
592
|
VALUE pub_val = pub_key && pub_key_len > 0
|
|
588
593
|
? frozen_bin_str((const char *)pub_key, (long)pub_key_len)
|
data/ext/pqc_asn1/extconf.rb
CHANGED
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
require "mkmf"
|
|
4
4
|
|
|
5
|
-
# Try to find
|
|
6
|
-
# If pkg-config finds
|
|
7
|
-
# vendored
|
|
8
|
-
# If not found, fall back to the vendored
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
# Try to find system-installed libraries first.
|
|
6
|
+
# If pkg-config finds them, the system headers and libraries are used and
|
|
7
|
+
# the vendored sources are excluded from compilation.
|
|
8
|
+
# If not found, fall back to the vendored copies bundled in ext/pqc_asn1/.
|
|
9
|
+
pqcsb_available = pkg_config("pqcsb")
|
|
10
|
+
pqcasn1_available = pkg_config("pqcasn1")
|
|
11
|
+
|
|
12
|
+
$srcs_pqcsb = pqcsb_available ? [] : %w[pqcsb.c]
|
|
13
|
+
$srcs_pqcasn1 = pqcasn1_available ? [] : %w[pqc_asn1.c]
|
|
14
|
+
|
|
15
|
+
$srcs = %w[pqc_asn1_ext.c error.c secure_buffer.c oid.c cursor.c der.c pem.c base64_ext.c] + $srcs_pqcasn1 + $srcs_pqcsb
|
|
14
16
|
|
|
15
17
|
# -Wpedantic generates excessive noise from Ruby's own UCRT headers on Windows
|
|
16
18
|
# (C23 attributes like [[nodiscard]], [[maybe_unused]], __VA_OPT__).
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* internal.h — private definitions shared across libpqcsb source files.
|
|
3
|
+
*
|
|
4
|
+
* This header is NOT installed and is NOT part of the public API.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#ifndef PQCSB_INTERNAL_H
|
|
8
|
+
#define PQCSB_INTERNAL_H
|
|
9
|
+
|
|
10
|
+
/* Include generated config before anything else. */
|
|
11
|
+
#include "pqcsb_config.h"
|
|
12
|
+
|
|
13
|
+
/* Enable memset_s() on Apple/BSD via C11 Annex K.
|
|
14
|
+
* Must appear before any system header that may include string.h. */
|
|
15
|
+
#ifndef __STDC_WANT_LIB_EXT1__
|
|
16
|
+
#define __STDC_WANT_LIB_EXT1__ 1
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
#include "pqcsb.h"
|
|
20
|
+
|
|
21
|
+
#include <string.h>
|
|
22
|
+
#include <stdio.h>
|
|
23
|
+
#include <stdatomic.h>
|
|
24
|
+
|
|
25
|
+
#ifndef _WIN32
|
|
26
|
+
#include <unistd.h>
|
|
27
|
+
#endif
|
|
28
|
+
|
|
29
|
+
#ifdef HAVE_SYS_MMAN_H
|
|
30
|
+
#include <sys/mman.h>
|
|
31
|
+
#endif
|
|
32
|
+
|
|
33
|
+
#ifdef HAVE_SYS_RESOURCE_H
|
|
34
|
+
#include <sys/resource.h>
|
|
35
|
+
#endif
|
|
36
|
+
|
|
37
|
+
#ifdef _WIN32
|
|
38
|
+
#include <windows.h>
|
|
39
|
+
#include <ntsecapi.h>
|
|
40
|
+
#endif
|
|
41
|
+
|
|
42
|
+
/* ------------------------------------------------------------------ */
|
|
43
|
+
/* MAP_ANONYMOUS portability */
|
|
44
|
+
/* ------------------------------------------------------------------ */
|
|
45
|
+
|
|
46
|
+
#if defined(HAVE_MMAP) && !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
|
|
47
|
+
#define MAP_ANONYMOUS MAP_ANON
|
|
48
|
+
#endif
|
|
49
|
+
|
|
50
|
+
/* ------------------------------------------------------------------ */
|
|
51
|
+
/* Platform backend selection */
|
|
52
|
+
/* ------------------------------------------------------------------ */
|
|
53
|
+
|
|
54
|
+
#if defined(HAVE_MMAP) && defined(MAP_ANONYMOUS)
|
|
55
|
+
# define PQCSB_USE_MMAP 1
|
|
56
|
+
#elif defined(_WIN32)
|
|
57
|
+
# define PQCSB_USE_VIRTUALALLOC 1
|
|
58
|
+
#elif defined(PQCSB_ALLOW_MALLOC_FALLBACK)
|
|
59
|
+
/* Weak protection: canaries only, no guard pages, no mprotect */
|
|
60
|
+
#else
|
|
61
|
+
# error "No mmap or VirtualAlloc available. Define PQCSB_ALLOW_MALLOC_FALLBACK "\
|
|
62
|
+
"to opt into the malloc fallback (no guard pages — reduced security guarantees)."
|
|
63
|
+
#endif
|
|
64
|
+
|
|
65
|
+
/* ------------------------------------------------------------------ */
|
|
66
|
+
/* Protection macros */
|
|
67
|
+
/* ------------------------------------------------------------------ */
|
|
68
|
+
|
|
69
|
+
#if defined(PQCSB_USE_MMAP) && defined(HAVE_MPROTECT)
|
|
70
|
+
# define PQCSB_SET_PROT(base, ps, dp, prot) \
|
|
71
|
+
mprotect((base) + (ps), (dp), (prot))
|
|
72
|
+
# define PQCSB_PROT_NONE PROT_NONE
|
|
73
|
+
# define PQCSB_PROT_READ PROT_READ
|
|
74
|
+
# define PQCSB_PROT_RW (PROT_READ | PROT_WRITE)
|
|
75
|
+
# define PQCSB_HAS_PROTECT 1
|
|
76
|
+
#elif defined(PQCSB_USE_VIRTUALALLOC)
|
|
77
|
+
# define PQCSB_SET_PROT(base, ps, dp, prot) do { \
|
|
78
|
+
DWORD _old; \
|
|
79
|
+
VirtualProtect((base) + (ps), (dp), (prot), &_old); \
|
|
80
|
+
} while (0)
|
|
81
|
+
# define PQCSB_PROT_NONE PAGE_NOACCESS
|
|
82
|
+
# define PQCSB_PROT_READ PAGE_READONLY
|
|
83
|
+
# define PQCSB_PROT_RW PAGE_READWRITE
|
|
84
|
+
# define PQCSB_HAS_PROTECT 1
|
|
85
|
+
#else
|
|
86
|
+
# define PQCSB_HAS_PROTECT 0
|
|
87
|
+
#endif
|
|
88
|
+
|
|
89
|
+
/* ------------------------------------------------------------------ */
|
|
90
|
+
/* Buffer struct (opaque to consumers) */
|
|
91
|
+
/* ------------------------------------------------------------------ */
|
|
92
|
+
|
|
93
|
+
struct pqcsb_buf {
|
|
94
|
+
uint8_t *data; /* pointer to user data (inside mmap or malloc'd) */
|
|
95
|
+
size_t len; /* user-requested byte length */
|
|
96
|
+
uint8_t *mmap_base; /* base of entire mmap region (NULL if malloc) */
|
|
97
|
+
size_t mmap_len; /* total mmap size including guard pages */
|
|
98
|
+
size_t data_pages; /* size of data region (between guard pages) */
|
|
99
|
+
int wiped; /* 1 if wipe() was called */
|
|
100
|
+
int locked; /* 1 if mlock succeeded on the data pages */
|
|
101
|
+
_Atomic int read_refs; /* reference count for nested begin_read/end_read */
|
|
102
|
+
uint8_t canary[PQCSB_CANARY_SIZE]; /* per-buffer random canary */
|
|
103
|
+
pqcsb_config_t config; /* runtime configuration options for this buffer */
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/* ------------------------------------------------------------------ */
|
|
107
|
+
/* Internal function declarations */
|
|
108
|
+
/* ------------------------------------------------------------------ */
|
|
109
|
+
|
|
110
|
+
/* secure_zero.c */
|
|
111
|
+
/* (pqcsb_secure_zero is public, declared in pqcsb.h) */
|
|
112
|
+
|
|
113
|
+
/* random.c */
|
|
114
|
+
/* (pqcsb_fill_random is public, declared in pqcsb.h) */
|
|
115
|
+
|
|
116
|
+
#endif /* PQCSB_INTERNAL_H */
|
data/ext/pqc_asn1/pem.c
CHANGED
|
@@ -77,18 +77,18 @@ pem_decoded_cleanup(VALUE arg)
|
|
|
77
77
|
* pem_encode_sb_ctx_t — SecureBuffer encode context.
|
|
78
78
|
*
|
|
79
79
|
* pem_encode_sb_body calls pqc_asn1_pem_encode with the SecureBuffer's
|
|
80
|
-
* data pointer (valid only inside
|
|
80
|
+
* data pointer (valid only inside the read guard).
|
|
81
81
|
* pem_encode_sb_cleanup always calls pqcsb_end_read to restore guard
|
|
82
82
|
* pages even when pqc_asn1_pem_encode raises via ruby_xmalloc. On
|
|
83
83
|
* exception paths it also frees any partially-allocated pem_buf.
|
|
84
84
|
*/
|
|
85
85
|
typedef struct {
|
|
86
|
-
|
|
87
|
-
const char
|
|
88
|
-
size_t
|
|
89
|
-
char
|
|
90
|
-
size_t
|
|
91
|
-
pqc_asn1_status_t
|
|
86
|
+
pqcsb_read_guard_t guard;
|
|
87
|
+
const char *label;
|
|
88
|
+
size_t label_len;
|
|
89
|
+
char *pem_buf;
|
|
90
|
+
size_t pem_len;
|
|
91
|
+
pqc_asn1_status_t rc;
|
|
92
92
|
} pem_encode_sb_ctx_t;
|
|
93
93
|
|
|
94
94
|
static VALUE
|
|
@@ -96,7 +96,7 @@ pem_encode_sb_body(VALUE arg)
|
|
|
96
96
|
{
|
|
97
97
|
pem_encode_sb_ctx_t *ctx = (pem_encode_sb_ctx_t *)arg;
|
|
98
98
|
ctx->rc = pqc_asn1_pem_encode(
|
|
99
|
-
ctx->
|
|
99
|
+
ctx->guard.data, ctx->guard.len,
|
|
100
100
|
ctx->label, ctx->label_len,
|
|
101
101
|
&ctx->pem_buf, &ctx->pem_len);
|
|
102
102
|
return Qnil;
|
|
@@ -107,7 +107,7 @@ pem_encode_sb_cleanup(VALUE arg)
|
|
|
107
107
|
{
|
|
108
108
|
pem_encode_sb_ctx_t *ctx = (pem_encode_sb_ctx_t *)arg;
|
|
109
109
|
/* Always restore mprotect — this is the primary purpose of rb_ensure here. */
|
|
110
|
-
pqcsb_end_read(ctx->
|
|
110
|
+
pqcsb_end_read(&ctx->guard);
|
|
111
111
|
/* On exception paths pem_buf may have been allocated inside
|
|
112
112
|
* pqc_asn1_pem_encode before ruby_xmalloc raised NoMemoryError.
|
|
113
113
|
* Zero and free it here to prevent leaking PEM-encoded key material.
|
|
@@ -263,13 +263,15 @@ rb_pem_encode(UNUSED VALUE _self, VALUE rb_der, VALUE rb_label)
|
|
|
263
263
|
if (rb_obj_is_kind_of(rb_der, pqcsb_class())) {
|
|
264
264
|
/* SecureBuffer input: use rb_ensure to restore PROT_NONE even when
|
|
265
265
|
* pqc_asn1_pem_encode raises via ruby_xmalloc under memory pressure. */
|
|
266
|
-
pqcsb_buf_t *sb;
|
|
267
|
-
|
|
268
|
-
if (sb->wiped)
|
|
266
|
+
pqcsb_buf_t *sb = (pqcsb_buf_t *)RTYPEDDATA_DATA(rb_der);
|
|
267
|
+
if (pqcsb_is_wiped(sb))
|
|
269
268
|
rb_raise(rb_eRuntimeError, "SecureBuffer has been wiped");
|
|
270
269
|
|
|
271
|
-
|
|
272
|
-
|
|
270
|
+
pqcsb_read_guard_t guard = pqcsb_begin_read(sb);
|
|
271
|
+
if (guard.status != PQCSB_OK)
|
|
272
|
+
rb_raise(rb_eRuntimeError, "pqcsb_begin_read failed");
|
|
273
|
+
|
|
274
|
+
pem_encode_sb_ctx_t ctx = {guard, label, label_len, NULL, 0, PQC_ASN1_OK};
|
|
273
275
|
rb_ensure(pem_encode_sb_body, (VALUE)&ctx,
|
|
274
276
|
pem_encode_sb_cleanup, (VALUE)&ctx);
|
|
275
277
|
rc = ctx.rc;
|
data/ext/pqc_asn1/pqc_asn1.c
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* pqc_asn1.c — DER/PEM/Base64 utilities for post-quantum key serialization.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* GENERATED FILE — do not edit directly.
|
|
5
|
+
* Edit the individual source files in src/ and run scripts/concat.sh to
|
|
6
|
+
* regenerate this file.
|
|
7
|
+
*
|
|
8
|
+
* Source files (in concatenation order):
|
|
9
|
+
* src/tlv.c — version, safe arithmetic, secure zeroing, DER TLV helpers
|
|
10
|
+
* src/builder.c — SPKI/PKCS#8 layout structs and DER builders
|
|
11
|
+
* src/parser.c — SPKI/PKCS#8 DER parsers
|
|
12
|
+
* src/base64.c — RFC 4648 Base64 encode/decode
|
|
13
|
+
* src/pem.c — RFC 7468 PEM encode/decode
|
|
14
|
+
*
|
|
15
|
+
* Version: 0.1.5
|
|
8
16
|
*
|
|
9
17
|
* Standalone C library — no external dependencies beyond the C standard library.
|
|
10
18
|
*
|