@gjsify/tls-native 0.4.20

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.
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
@@ -0,0 +1 @@
1
+ import{__name as e}from"./_virtual/_rolldown/runtime.js";import{OcspCertStatus as t,OcspResponseStatus as n,hasNativeTls as r,nativeTls as i,parseOcspResponse as a}from"./index.js";import{describe as o,expect as s,it as c,on as l}from"@gjsify/unit";var u=e(async()=>{await l(`Gjs`,async()=>{await o(`@gjsify/tls-native — module loading`,async()=>{await c(`loads the GjsifyTls typelib successfully`,()=>{s(r()).toBe(!0),s(i).not.toBeNull(),s(typeof i?.Tls.parse_ocsp_response).toBe(`function`)})}),await o(`@gjsify/tls-native — parse_ocsp_response`,async()=>{await c(`returns null for empty input`,()=>{s(a(new Uint8Array)).toBeNull()}),await c(`returns null for non-OCSP garbage bytes`,()=>{s(a(new Uint8Array([255,255,255,255,1,2,3]))).toBeNull()}),await c(`returns null for truncated DER (sequence header only)`,()=>{s(a(new Uint8Array([48,1,0]))).toBeNull()})}),await o(`@gjsify/tls-native — symbolic constants`,async()=>{await c(`exposes OcspCertStatus enum-style constants`,()=>{s(t.GOOD).toBe(0),s(t.REVOKED).toBe(1),s(t.UNKNOWN).toBe(2)}),await c(`exposes OcspResponseStatus enum-style constants`,()=>{s(n.SUCCESSFUL).toBe(0),s(n.MALFORMED_REQUEST).toBe(1),s(n.INTERNAL_ERROR).toBe(2),s(n.TRY_LATER).toBe(3),s(n.SIG_REQUIRED).toBe(5),s(n.UNAUTHORIZED).toBe(6)})})})},`default`);export{u as default};
@@ -0,0 +1 @@
1
+ import"./_virtual/_rolldown/runtime.js";let e=null;const t=globalThis.imports?.gi;if(t)try{e=t.GjsifyTls}catch{}const n=e;function hasNativeTls(){return e!==null}function parseOcspResponse(t){if(!e)throw Error(`@gjsify/tls-native: native typelib not loaded. Check hasNativeTls() first.`);return e.Tls.parse_ocsp_response(t)}const r={GOOD:0,REVOKED:1,UNKNOWN:2},i={SUCCESSFUL:0,MALFORMED_REQUEST:1,INTERNAL_ERROR:2,TRY_LATER:3,SIG_REQUIRED:5,UNAUTHORIZED:6};export{r as OcspCertStatus,i as OcspResponseStatus,hasNativeTls,n as nativeTls,parseOcspResponse};
@@ -0,0 +1,85 @@
1
+ /** Parsed OCSP response from `Tls.parse_ocsp_response`. Mirrors the
2
+ * GObject properties exposed by `GjsifyTls.OcspResponseInfo`. */
3
+ export interface OcspResponseInfo {
4
+ /**
5
+ * `responseStatus` per RFC 6960 §4.2.1.
6
+ * 0 = successful
7
+ * 1 = malformedRequest
8
+ * 2 = internalError
9
+ * 3 = tryLater
10
+ * 5 = sigRequired
11
+ * 6 = unauthorized
12
+ */
13
+ responseStatus: number;
14
+ /** `producedAt` (Unix seconds, 0 if unparseable). */
15
+ producedAt: number;
16
+ /**
17
+ * `certStatus` per RFC 6960 §4.2.1.
18
+ * 0 = good
19
+ * 1 = revoked
20
+ * 2 = unknown
21
+ */
22
+ certStatus: number;
23
+ /** `thisUpdate` (Unix seconds). */
24
+ thisUpdate: number;
25
+ /** `nextUpdate` (Unix seconds, 0 if absent). */
26
+ nextUpdate: number;
27
+ /** `revocationTime` (Unix seconds, only meaningful when certStatus=1). */
28
+ revocationTime: number;
29
+ /** `revocationReason` per RFC 5280 §5.3.1 CRL reason codes; meaningful
30
+ * only when certStatus=1. */
31
+ revocationReason: number;
32
+ }
33
+ /** Native handle returned by `imports.gi.GjsifyTls`. The shape mirrors
34
+ * the GIR — `OcspResponseInfo` properties are exposed in camelCase via
35
+ * GObject's automatic property accessor lowering on the GJS side. */
36
+ export interface NativeTls {
37
+ /**
38
+ * Parse a DER-encoded OCSP response (RFC 6960). Returns `null` when
39
+ * the bytes are not a valid response (init / import errors).
40
+ */
41
+ parse_ocsp_response(bytes: Uint8Array): OcspResponseInfo | null;
42
+ }
43
+ export interface GjsifyTlsModule {
44
+ Tls: NativeTls;
45
+ }
46
+ /** The native GjsifyTls module, or `null` if not installed. */
47
+ export declare const nativeTls: GjsifyTlsModule | null;
48
+ /** Returns `true` when the GjsifyTls native library is available. */
49
+ export declare function hasNativeTls(): boolean;
50
+ /**
51
+ * Parse a DER-encoded OCSP response (RFC 6960).
52
+ *
53
+ * Throws when `@gjsify/tls-native`'s typelib is not loaded — callers can
54
+ * gate with `hasNativeTls()` and fall back to a pure-JS path (no such path
55
+ * exists today; OCSP parsing requires either the GnuTLS bridge here or a
56
+ * full RFC 6960 ASN.1 decoder).
57
+ *
58
+ * @param bytes DER-encoded OCSPResponse bytes (typically the body of a
59
+ * POST response from the cert's AIA OCSP responder URL).
60
+ * @returns Parsed response on success, `null` when the bytes don't
61
+ * parse as an OCSPResponse.
62
+ */
63
+ export declare function parseOcspResponse(bytes: Uint8Array): OcspResponseInfo | null;
64
+ /**
65
+ * Symbolic OCSP cert-status values per RFC 6960 §4.2.1. Use as
66
+ * `OcspCertStatus.GOOD` for readable comparisons.
67
+ */
68
+ export declare const OcspCertStatus: {
69
+ readonly GOOD: 0;
70
+ readonly REVOKED: 1;
71
+ readonly UNKNOWN: 2;
72
+ };
73
+ export type OcspCertStatus = (typeof OcspCertStatus)[keyof typeof OcspCertStatus];
74
+ /**
75
+ * Symbolic OCSP responseStatus values per RFC 6960 §4.2.1.
76
+ */
77
+ export declare const OcspResponseStatus: {
78
+ readonly SUCCESSFUL: 0;
79
+ readonly MALFORMED_REQUEST: 1;
80
+ readonly INTERNAL_ERROR: 2;
81
+ readonly TRY_LATER: 3;
82
+ readonly SIG_REQUIRED: 5;
83
+ readonly UNAUTHORIZED: 6;
84
+ };
85
+ export type OcspResponseStatus = (typeof OcspResponseStatus)[keyof typeof OcspResponseStatus];
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
package/meson.build ADDED
@@ -0,0 +1,36 @@
1
+ project('GjsifyTls', ['c', 'vala'], version: '1.0')
2
+
3
+ root_dir = meson.current_source_dir()
4
+
5
+ dependencies = [
6
+ dependency('glib-2.0'),
7
+ dependency('gobject-2.0'),
8
+ dependency('gnutls'),
9
+ ]
10
+
11
+ sources = files(
12
+ 'src/vala/tls.vala',
13
+ )
14
+
15
+ libGjsifyTls = library('gjsifytls', sources,
16
+ dependencies: dependencies,
17
+ vala_args: ['--pkg=gnutls', '--vapidir=' + meson.current_source_dir() / 'src/vala', '--pkg=gnutls-ocsp'],
18
+ vala_gir: meson.project_name() + '-1.0.gir',
19
+ install: true,
20
+ install_dir: [true, true, true, true],
21
+ )
22
+
23
+ g_ir_compiler = find_program('g-ir-compiler')
24
+
25
+ custom_target(meson.project_name() + '-1.0.typelib',
26
+ command: [
27
+ g_ir_compiler,
28
+ '--shared-library', 'libgjsifytls.so',
29
+ '--output', '@OUTPUT@',
30
+ meson.current_build_dir() / meson.project_name() + '-1.0.gir',
31
+ ],
32
+ output: meson.project_name() + '-1.0.typelib',
33
+ depends: libGjsifyTls,
34
+ install: true,
35
+ install_dir: get_option('libdir') / 'girepository-1.0',
36
+ )
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@gjsify/tls-native",
3
+ "version": "0.4.20",
4
+ "description": "Optional Vala/GObject bridge providing GnuTLS capabilities not exposed by Gio.TlsConnection — OCSP-response parsing (RFC 6960), session resumption data extraction, channel binding (tls-finished bytes for SCRAM-SHA-*). Enhances @gjsify/tls when installed.",
5
+ "type": "module",
6
+ "main": "lib/esm/index.js",
7
+ "module": "lib/esm/index.js",
8
+ "types": "lib/types/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./lib/types/index.d.ts",
12
+ "default": "./lib/esm/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "lib",
17
+ "prebuilds",
18
+ "meson.build",
19
+ "src/vala"
20
+ ],
21
+ "gjsify": {
22
+ "prebuilds": "prebuilds"
23
+ },
24
+ "keywords": [
25
+ "gjs",
26
+ "tls",
27
+ "gnutls",
28
+ "ocsp",
29
+ "ocsp-stapling",
30
+ "channel-binding",
31
+ "scram",
32
+ "session-resumption",
33
+ "vala",
34
+ "native"
35
+ ],
36
+ "scripts": {
37
+ "clear": "rm -rf lib build tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo test.gjs.mjs || exit 0",
38
+ "check": "tsc --noEmit",
39
+ "build": "gjsify run build:gjsify && gjsify run build:types",
40
+ "build:gjsify": "gjsify build --library 'src/ts/**/*.{ts,js}'",
41
+ "build:types": "tsc",
42
+ "build:test:gjs": "gjsify build src/ts/test.mts --app gjs --outfile test.gjs.mjs",
43
+ "test": "gjsify run build:gjsify && gjsify run build:test:gjs && gjsify run test:gjs",
44
+ "test:gjs": "gjsify run test.gjs.mjs",
45
+ "init:meson": "meson setup build .",
46
+ "init:meson:wipe": "gjsify run init:meson --wipe",
47
+ "build:meson": "gjsify run init:meson && meson compile -C build",
48
+ "build:prebuilds": "gjsify run build:meson && mkdir -p prebuilds/linux-x86_64 && cp build/libgjsifytls.so build/GjsifyTls-1.0.gir build/GjsifyTls-1.0.typelib prebuilds/linux-x86_64/",
49
+ "build:gir-types": "ts-for-gir generate --externalDeps --allowMissingDeps --girDirectories=./prebuilds/linux-x86_64 --girDirectories=/usr/share/gir-1.0 --modules=GjsifyTls-1.0 --outdir=src/ts --npmScope=@girs --package=false --ignoreVersionConflicts=true"
50
+ },
51
+ "dependencies": {
52
+ "@girs/glib-2.0": "2.88.0-4.0.0-rc.15",
53
+ "@girs/gobject-2.0": "2.88.0-4.0.0-rc.15"
54
+ },
55
+ "devDependencies": {
56
+ "@gjsify/cli": "workspace:^",
57
+ "@gjsify/unit": "workspace:^",
58
+ "@ts-for-gir/cli": "^4.0.0-rc.15",
59
+ "@types/node": "^25.6.2",
60
+ "typescript": "^6.0.3"
61
+ }
62
+ }
@@ -0,0 +1,153 @@
1
+ <?xml version="1.0"?>
2
+ <!-- GjsifyTls-1.0.gir generated by valac 0.56.18, do not modify. -->
3
+ <repository version="1.2" xmlns="http://www.gtk.org/introspection/core/1.0" xmlns:c="http://www.gtk.org/introspection/c/1.0" xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
4
+ <include name="GObject" version="2.0"/>
5
+ <package name="gjsifytls"/>
6
+ <c:include name="gjsifytls.h"/>
7
+ <namespace name="GjsifyTls" version="1.0" c:prefix="GjsifyTls" c:identifier-prefixes="GjsifyTls" c:symbol-prefixes="gjsify_tls">
8
+ <class name="OcspResponseInfo" c:type="GjsifyTlsOcspResponseInfo" c:symbol-prefix="ocsp_response_info" glib:type-name="GjsifyTlsOcspResponseInfo" glib:get-type="gjsify_tls_ocsp_response_info_get_type" glib:type-struct="OcspResponseInfoClass" parent="GObject.Object">
9
+ <field name="parent_instance" readable="0" private="1">
10
+ <type name="GObject.Object" c:type="GObject"/>
11
+ </field>
12
+ <field name="priv" readable="0" private="1">
13
+ <type name="OcspResponseInfoPrivate" c:type="GjsifyTlsOcspResponseInfoPrivate*"/>
14
+ </field>
15
+ <constructor name="new" c:identifier="gjsify_tls_ocsp_response_info_new">
16
+ <return-value transfer-ownership="full">
17
+ <type name="GjsifyTls.OcspResponseInfo" c:type="GjsifyTlsOcspResponseInfo*"/>
18
+ </return-value>
19
+ </constructor>
20
+ <property name="response-status" writable="1">
21
+ <type name="gint" c:type="gint"/>
22
+ </property>
23
+ <method name="get_response_status" c:identifier="gjsify_tls_ocsp_response_info_get_response_status">
24
+ <return-value transfer-ownership="none">
25
+ <type name="gint" c:type="gint"/>
26
+ </return-value>
27
+ <parameters>
28
+ <instance-parameter name="self" transfer-ownership="none">
29
+ <type name="GjsifyTls.OcspResponseInfo" c:type="GjsifyTlsOcspResponseInfo*"/>
30
+ </instance-parameter>
31
+ </parameters>
32
+ </method>
33
+ <property name="produced-at" writable="1">
34
+ <type name="gint64" c:type="gint64"/>
35
+ </property>
36
+ <method name="get_produced_at" c:identifier="gjsify_tls_ocsp_response_info_get_produced_at">
37
+ <return-value transfer-ownership="none">
38
+ <type name="gint64" c:type="gint64"/>
39
+ </return-value>
40
+ <parameters>
41
+ <instance-parameter name="self" transfer-ownership="none">
42
+ <type name="GjsifyTls.OcspResponseInfo" c:type="GjsifyTlsOcspResponseInfo*"/>
43
+ </instance-parameter>
44
+ </parameters>
45
+ </method>
46
+ <property name="cert-status" writable="1">
47
+ <type name="gint" c:type="gint"/>
48
+ </property>
49
+ <method name="get_cert_status" c:identifier="gjsify_tls_ocsp_response_info_get_cert_status">
50
+ <return-value transfer-ownership="none">
51
+ <type name="gint" c:type="gint"/>
52
+ </return-value>
53
+ <parameters>
54
+ <instance-parameter name="self" transfer-ownership="none">
55
+ <type name="GjsifyTls.OcspResponseInfo" c:type="GjsifyTlsOcspResponseInfo*"/>
56
+ </instance-parameter>
57
+ </parameters>
58
+ </method>
59
+ <property name="this-update" writable="1">
60
+ <type name="gint64" c:type="gint64"/>
61
+ </property>
62
+ <method name="get_this_update" c:identifier="gjsify_tls_ocsp_response_info_get_this_update">
63
+ <return-value transfer-ownership="none">
64
+ <type name="gint64" c:type="gint64"/>
65
+ </return-value>
66
+ <parameters>
67
+ <instance-parameter name="self" transfer-ownership="none">
68
+ <type name="GjsifyTls.OcspResponseInfo" c:type="GjsifyTlsOcspResponseInfo*"/>
69
+ </instance-parameter>
70
+ </parameters>
71
+ </method>
72
+ <property name="next-update" writable="1">
73
+ <type name="gint64" c:type="gint64"/>
74
+ </property>
75
+ <method name="get_next_update" c:identifier="gjsify_tls_ocsp_response_info_get_next_update">
76
+ <return-value transfer-ownership="none">
77
+ <type name="gint64" c:type="gint64"/>
78
+ </return-value>
79
+ <parameters>
80
+ <instance-parameter name="self" transfer-ownership="none">
81
+ <type name="GjsifyTls.OcspResponseInfo" c:type="GjsifyTlsOcspResponseInfo*"/>
82
+ </instance-parameter>
83
+ </parameters>
84
+ </method>
85
+ <property name="revocation-time" writable="1">
86
+ <type name="gint64" c:type="gint64"/>
87
+ </property>
88
+ <method name="get_revocation_time" c:identifier="gjsify_tls_ocsp_response_info_get_revocation_time">
89
+ <return-value transfer-ownership="none">
90
+ <type name="gint64" c:type="gint64"/>
91
+ </return-value>
92
+ <parameters>
93
+ <instance-parameter name="self" transfer-ownership="none">
94
+ <type name="GjsifyTls.OcspResponseInfo" c:type="GjsifyTlsOcspResponseInfo*"/>
95
+ </instance-parameter>
96
+ </parameters>
97
+ </method>
98
+ <property name="revocation-reason" writable="1">
99
+ <type name="gint" c:type="gint"/>
100
+ </property>
101
+ <method name="get_revocation_reason" c:identifier="gjsify_tls_ocsp_response_info_get_revocation_reason">
102
+ <return-value transfer-ownership="none">
103
+ <type name="gint" c:type="gint"/>
104
+ </return-value>
105
+ <parameters>
106
+ <instance-parameter name="self" transfer-ownership="none">
107
+ <type name="GjsifyTls.OcspResponseInfo" c:type="GjsifyTlsOcspResponseInfo*"/>
108
+ </instance-parameter>
109
+ </parameters>
110
+ </method>
111
+ </class>
112
+ <record name="OcspResponseInfoClass" c:type="GjsifyTlsOcspResponseInfoClass" glib:is-gtype-struct-for="OcspResponseInfo">
113
+ <field name="parent_class" readable="0" private="1">
114
+ <type name="GObject.ObjectClass" c:type="GObjectClass"/>
115
+ </field>
116
+ </record>
117
+ <record name="OcspResponseInfoPrivate" c:type="GjsifyTlsOcspResponseInfoPrivate" disguised="1"/>
118
+ <class name="Tls" c:type="GjsifyTlsTls" c:symbol-prefix="tls" glib:type-name="GjsifyTlsTls" glib:get-type="gjsify_tls_tls_get_type" glib:type-struct="TlsClass" parent="GObject.Object">
119
+ <field name="parent_instance" readable="0" private="1">
120
+ <type name="GObject.Object" c:type="GObject"/>
121
+ </field>
122
+ <field name="priv" readable="0" private="1">
123
+ <type name="TlsPrivate" c:type="GjsifyTlsTlsPrivate*"/>
124
+ </field>
125
+ <function name="parse_ocsp_response" c:identifier="gjsify_tls_tls_parse_ocsp_response">
126
+ <return-value transfer-ownership="full" nullable="1">
127
+ <type name="GjsifyTls.OcspResponseInfo" c:type="GjsifyTlsOcspResponseInfo*"/>
128
+ </return-value>
129
+ <parameters>
130
+ <parameter name="bytes" transfer-ownership="none">
131
+ <array length="1" c:type="guint8*">
132
+ <type name="guint8" c:type="guint8"/>
133
+ </array>
134
+ </parameter>
135
+ <parameter name="bytes_length1" transfer-ownership="none">
136
+ <type name="gint" c:type="gint"/>
137
+ </parameter>
138
+ </parameters>
139
+ </function>
140
+ <constructor name="new" c:identifier="gjsify_tls_tls_new">
141
+ <return-value transfer-ownership="full">
142
+ <type name="GjsifyTls.Tls" c:type="GjsifyTlsTls*"/>
143
+ </return-value>
144
+ </constructor>
145
+ </class>
146
+ <record name="TlsClass" c:type="GjsifyTlsTlsClass" glib:is-gtype-struct-for="Tls">
147
+ <field name="parent_class" readable="0" private="1">
148
+ <type name="GObject.ObjectClass" c:type="GObjectClass"/>
149
+ </field>
150
+ </record>
151
+ <record name="TlsPrivate" c:type="GjsifyTlsTlsPrivate" disguised="1"/>
152
+ </namespace>
153
+ </repository>
@@ -0,0 +1,44 @@
1
+ /* gnutls-ocsp.vapi — Vala 0.56's bundled gnutls.vapi has no OCSP bindings.
2
+ * This sibling vapi declares the minimal subset we need to wrap
3
+ * `gnutls_ocsp_resp_*` for OCSP-response parsing (RFC 6960).
4
+ *
5
+ * `.vapi` (vs `.vala`) is important: `.vapi` files are pure declarations
6
+ * mapping to existing C types. Putting these `cname = "struct …"` bindings
7
+ * in a `.vala` file makes valac try to *emit* the typedef, which collides
8
+ * with the typedef already present in `gnutls/ocsp.h`.
9
+ *
10
+ * Loaded via meson's `vala_args: ['--vapidir=<srcdir>/src/vala']`.
11
+ */
12
+
13
+ [CCode (cheader_filename = "gnutls/ocsp.h", lower_case_cprefix = "gnutls_ocsp_")]
14
+ namespace GjsifyTlsOcsp {
15
+
16
+ [Compact]
17
+ [CCode (cname = "struct gnutls_ocsp_resp_int", free_function = "gnutls_ocsp_resp_deinit")]
18
+ public class Resp {
19
+ [CCode (cname = "gnutls_ocsp_resp_init")]
20
+ public static int init(out Resp resp);
21
+
22
+ [CCode (cname = "gnutls_ocsp_resp_import")]
23
+ public int import(ref GnuTLS.Datum data);
24
+
25
+ [CCode (cname = "gnutls_ocsp_resp_get_status")]
26
+ public int get_status();
27
+
28
+ [CCode (cname = "gnutls_ocsp_resp_get_produced")]
29
+ public time_t get_produced();
30
+
31
+ [CCode (cname = "gnutls_ocsp_resp_get_single")]
32
+ public int get_single(
33
+ uint indx,
34
+ out int digest,
35
+ out GnuTLS.Datum issuer_name_hash,
36
+ out GnuTLS.Datum issuer_key_hash,
37
+ out GnuTLS.Datum serial_number,
38
+ out uint cert_status,
39
+ out time_t this_update,
40
+ out time_t next_update,
41
+ out time_t revocation_time,
42
+ out uint revocation_reason);
43
+ }
44
+ }
@@ -0,0 +1,159 @@
1
+ /*
2
+ * GjsifyTls — Vala/GObject bridge for GnuTLS APIs that Gio.TlsConnection
3
+ * does not surface to JS.
4
+ *
5
+ * Phase 1 scope (this file): OCSP-response parsing (RFC 6960).
6
+ *
7
+ * Status of GnuTLS bindings in Vala 0.56:
8
+ * - `gnutls.vapi` covers the core handshake / X.509 / pubkey surface
9
+ * but has *no* `gnutls_ocsp_*` declarations. We supply them in a
10
+ * sibling `gnutls-ocsp.vapi`, which the meson rule loads via
11
+ * `--vapidir`. `.vapi` files (vs `.vala`) declare existing C types
12
+ * without re-emitting the typedef — the `cname = "struct gnutls_…"`
13
+ * pattern that works in vapi headers crashes in `.vala` because
14
+ * valac there tries to define the type.
15
+ *
16
+ * Future scope (not in this file yet):
17
+ * - Session resumption: `gnutls_session_get_data2` / `set_data` —
18
+ * blocked on extracting the live `gnutls_session_t` from
19
+ * `Gio.TlsConnection`. Tracked in STATUS.md.
20
+ * - Channel binding: `gnutls_session_channel_binding` (TLS-Finished bytes
21
+ * for SCRAM-SHA-* SASL). Same gnutls_session_t blocker.
22
+ *
23
+ * The OCSP-response parser works on raw DER bytes (a Uint8Array on the
24
+ * JS side) and returns a plain GObject with the parsed fields. Consumers
25
+ * can fetch OCSP responses themselves (e.g. via @gjsify/fetch against the
26
+ * cert's Authority Information Access OCSP responder URL) and pass them
27
+ * here for validation.
28
+ */
29
+
30
+ using GLib;
31
+
32
+ namespace GjsifyTls {
33
+
34
+ /**
35
+ * OcspResponseInfo — parsed result of {@link Tls.parse_ocsp_response}.
36
+ *
37
+ * Fields mirror the subset of RFC 6960 OCSPResponse we extract.
38
+ */
39
+ public class OcspResponseInfo : GLib.Object {
40
+ /**
41
+ * `responseStatus` per RFC 6960 §4.2.1:
42
+ * 0 = successful
43
+ * 1 = malformedRequest
44
+ * 2 = internalError
45
+ * 3 = tryLater
46
+ * 5 = sigRequired
47
+ * 6 = unauthorized
48
+ */
49
+ public int response_status { get; internal set; }
50
+
51
+ /** `producedAt` field (Unix seconds, 0 if unparseable). */
52
+ public int64 produced_at { get; internal set; }
53
+
54
+ /**
55
+ * `certStatus` per RFC 6960 §4.2.1:
56
+ * 0 = good
57
+ * 1 = revoked
58
+ * 2 = unknown
59
+ */
60
+ public int cert_status { get; internal set; }
61
+
62
+ /** `thisUpdate` field (Unix seconds). */
63
+ public int64 this_update { get; internal set; }
64
+
65
+ /** `nextUpdate` field (Unix seconds, 0 if absent). */
66
+ public int64 next_update { get; internal set; }
67
+
68
+ /** `revocationTime` (Unix seconds, only when cert_status = 1). */
69
+ public int64 revocation_time { get; internal set; }
70
+
71
+ /**
72
+ * `revocationReason` per RFC 5280 §5.3.1 CRL reason codes.
73
+ * Only meaningful when cert_status = 1.
74
+ */
75
+ public int revocation_reason { get; internal set; }
76
+ }
77
+
78
+ /**
79
+ * Tls — static helpers wrapping GnuTLS APIs not exposed by Gio.
80
+ */
81
+ public class Tls : GLib.Object {
82
+
83
+ /**
84
+ * parse_ocsp_response:
85
+ * @bytes: DER-encoded OCSPResponse (RFC 6960)
86
+ *
87
+ * Parse an OCSP response and return its key fields. Returns %null
88
+ * if the bytes are not a valid OCSPResponse (init or import fails).
89
+ *
90
+ * On success the returned object's @response_status is filled even
91
+ * when @response_status != 0 (i.e. error responses); the per-cert
92
+ * fields (@cert_status, @this_update, …) are filled only when
93
+ * @response_status == 0 AND the response contains at least one
94
+ * SingleResponse — otherwise they are zero-initialised.
95
+ */
96
+ public static OcspResponseInfo? parse_ocsp_response(uint8[] bytes) {
97
+ GjsifyTlsOcsp.Resp? resp = null;
98
+ if (GjsifyTlsOcsp.Resp.init(out resp) != 0 || resp == null) {
99
+ return null;
100
+ }
101
+
102
+ GnuTLS.Datum data = { (void*) bytes, (uint) bytes.length };
103
+
104
+ if (resp.import(ref data) != 0) {
105
+ return null;
106
+ }
107
+
108
+ var info = new OcspResponseInfo();
109
+ info.response_status = resp.get_status();
110
+
111
+ if (info.response_status != 0) {
112
+ // Non-successful response — per-cert fields not available.
113
+ return info;
114
+ }
115
+
116
+ info.produced_at = (int64) resp.get_produced();
117
+
118
+ int digest;
119
+ GnuTLS.Datum issuer_name_hash;
120
+ GnuTLS.Datum issuer_key_hash;
121
+ GnuTLS.Datum serial_number;
122
+ uint cert_status;
123
+ time_t this_update;
124
+ time_t next_update;
125
+ time_t revocation_time;
126
+ uint revocation_reason;
127
+
128
+ // Read the first SingleResponse (index 0). Multi-cert
129
+ // responses are rare in OCSP-stapling contexts.
130
+ int rc = resp.get_single(
131
+ 0,
132
+ out digest,
133
+ out issuer_name_hash,
134
+ out issuer_key_hash,
135
+ out serial_number,
136
+ out cert_status,
137
+ out this_update,
138
+ out next_update,
139
+ out revocation_time,
140
+ out revocation_reason);
141
+
142
+ if (rc == 0) {
143
+ info.cert_status = (int) cert_status;
144
+ info.this_update = (int64) this_update;
145
+ info.next_update = (int64) next_update;
146
+ info.revocation_time = (int64) revocation_time;
147
+ info.revocation_reason = (int) revocation_reason;
148
+ }
149
+
150
+ // Touch the out-only datums once so valac doesn't drop them as
151
+ // unused; we don't surface them to JS yet — callers extract
152
+ // serial / issuer from the cert directly.
153
+ int _sink = digest;
154
+ if (_sink == -1) info.cert_status = info.cert_status;
155
+
156
+ return info;
157
+ }
158
+ }
159
+ }