confium 0.3.2 → 0.3.4

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.
@@ -3,6 +3,11 @@
3
3
  //! DRY consolidation: a single `bytes_from_value` + size cap + string
4
4
  //! conversion + typed-error helper shared by every subsystem module
5
5
  //! (composite, pki, tc, transparency, deployment, attributes).
6
+ //!
7
+ //! This module wraps magnus's deprecated `exception::*` constructors
8
+ //! so the rest of the crate never touches them; their replacements
9
+ //! need a `Ruby` handle, which the failure fallbacks here lack.
10
+ #![allow(deprecated)]
6
11
 
7
12
  use magnus::prelude::*;
8
13
  use magnus::{exception, Error, RHash, RString, Ruby, TryConvert, Value};
@@ -14,6 +19,28 @@ use magnus::{exception, Error, RHash, RString, Ruby, TryConvert, Value};
14
19
  /// prevent trivial memory-exhaustion attacks.
15
20
  pub const MAX_INPUT_SIZE: usize = 1 << 20;
16
21
 
22
+ /// Build a plain `RuntimeError`. Centralizes every raise site so the
23
+ /// deprecated `magnus::exception::runtime_error()` fallback lives in
24
+ /// exactly one place (its replacement needs a `Ruby` handle, which is
25
+ /// unavailable when `Ruby::get()` itself fails).
26
+ pub fn runtime(msg: impl Into<String>) -> Error {
27
+ let msg: String = msg.into();
28
+ match Ruby::get() {
29
+ Ok(ruby) => Error::new(ruby.exception_runtime_error(), msg),
30
+ Err(_) => Error::new(exception::runtime_error(), msg),
31
+ }
32
+ }
33
+
34
+ /// Build an `ArgumentError` — for bad argument shapes/ranges, the
35
+ /// class callers should see before any Confium semantics apply.
36
+ pub fn arg_error(msg: impl Into<String>) -> Error {
37
+ let msg: String = msg.into();
38
+ match Ruby::get() {
39
+ Ok(ruby) => Error::new(ruby.exception_arg_error(), msg),
40
+ Err(_) => Error::new(exception::arg_error(), msg),
41
+ }
42
+ }
43
+
17
44
  /// Convert a Ruby value to bytes. Accepts a binary `String` (any
18
45
  /// encoding) or an `Array<Integer>`. Enforces a 1 MiB size cap.
19
46
  pub fn bytes_from_value(v: Value) -> Result<Vec<u8>, Error> {
@@ -30,10 +57,7 @@ pub fn bytes_from_value(v: Value) -> Result<Vec<u8>, Error> {
30
57
  arr.into_iter()
31
58
  .map(|i| {
32
59
  if !(0..=255).contains(&i) {
33
- Err(Error::new(
34
- exception::arg_error(),
35
- format!("byte out of range 0..255: {i}"),
36
- ))
60
+ Err(arg_error(format!("byte out of range 0..255: {i}")))
37
61
  } else {
38
62
  Ok(i as u8)
39
63
  }
@@ -46,18 +70,15 @@ pub fn bytes_from_value(v: Value) -> Result<Vec<u8>, Error> {
46
70
  /// memory-exhaustion attacks.
47
71
  pub fn enforce_size(len: usize) -> Result<(), Error> {
48
72
  if len > MAX_INPUT_SIZE {
49
- return Err(Error::new(
50
- exception::arg_error(),
51
- format!("input size {0} exceeds max {MAX_INPUT_SIZE}", len),
52
- ));
73
+ return Err(arg_error(format!("input size {0} exceeds max {MAX_INPUT_SIZE}", len)));
53
74
  }
54
75
  Ok(())
55
76
  }
56
77
 
57
78
  /// Build a Ruby binary `String` from a byte slice. Avoids the UTF-8
58
- /// round-trip in `RString::buf_new` + `cat` for already-binary input.
59
- pub fn bytes_to_rstring(_ruby: &Ruby, bytes: &[u8]) -> RString {
60
- let s = RString::buf_new(0);
79
+ /// round-trip for already-binary input.
80
+ pub fn bytes_to_rstring(ruby: &Ruby, bytes: &[u8]) -> RString {
81
+ let s = ruby.str_buf_new(0);
61
82
  s.cat(bytes);
62
83
  s
63
84
  }
@@ -148,6 +169,19 @@ pub fn parse_error(msg: impl Into<String>, operation: &str, format: Option<&str>
148
169
  confium_error(msg, "ParseError", d)
149
170
  }
150
171
 
172
+ #[allow(dead_code)]
173
+ pub fn index_error(msg: impl Into<String>, operation: &str, index: Option<u64>) -> Error {
174
+ let ruby = match Ruby::get() {
175
+ Ok(r) => r,
176
+ Err(_) => return Error::new(exception::runtime_error(), msg.into()),
177
+ };
178
+ let d = new_details(&ruby);
179
+ let _ = d.aset("operation", operation);
180
+ let _ = d.aset("component", "Confium");
181
+ if let Some(i) = index { let _ = d.aset("index", i); }
182
+ confium_error(msg, "IndexError", d)
183
+ }
184
+
151
185
  #[allow(dead_code)]
152
186
  pub fn validation_error(msg: impl Into<String>, operation: &str, param: &str, expected: &str, actual: &str) -> Error {
153
187
  let ruby = match Ruby::get() {
data/lib/confium/audit.rb CHANGED
@@ -68,6 +68,7 @@ module Confium
68
68
  attr_reader :records
69
69
 
70
70
  def initialize
71
+ # @type ivar @records: Array[untyped]
71
72
  @records = []
72
73
  end
73
74
 
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Confium
6
+ module Composite
7
+ # JSON transport for composite signatures. The native extension
8
+ # defines Signature with algorithms/component_count/verify over
9
+ # component Hashes (algorithm, public_key, signature) whose
10
+ # binary fields cannot ride JSON directly, so the wire format
11
+ # hex-encodes them:
12
+ #
13
+ # json = Confium::Composite::Signature.components_to_json(components)
14
+ # sig = Confium::Composite::Signature.from_json(json)
15
+ # sig.verify(message)
16
+ class Signature
17
+ BINARY_FIELDS = %w[public_key signature].freeze
18
+
19
+ def self.components_to_json(components)
20
+ JSON.generate(components.map do |component|
21
+ component.transform_values do |value|
22
+ value.encoding == Encoding::ASCII_8BIT ? value.unpack1('H*') : value
23
+ end
24
+ end)
25
+ end
26
+
27
+ # Accepts either a bare JSON array of component Hashes or a
28
+ # {"components": [...]} envelope, with public_key/signature
29
+ # hex-encoded on the wire. Takes a JSON string or an
30
+ # already-parsed Array/Hash.
31
+ def self.from_json(json)
32
+ data = json.is_a?(String) ? JSON.parse(json) : json
33
+ components = data.is_a?(Hash) ? data['components'] : data
34
+ unless components.is_a?(Array) && !components.empty?
35
+ raise ArgumentError, 'expected a non-empty "components" array'
36
+ end
37
+
38
+ new(components.map { |c| decode_binary_fields(c) })
39
+ end
40
+
41
+ def self.decode_binary_fields(component)
42
+ BINARY_FIELDS.each_with_object(component.dup) do |field, decoded|
43
+ value = component.fetch(field)
44
+ decoded[field] = [value].pack('H*') if value.is_a?(String)
45
+ end
46
+ end
47
+ private_class_method :decode_binary_fields
48
+ end
49
+ end
50
+ end
@@ -21,6 +21,7 @@
21
21
  # Confium::Crypto.lookup(:hash) # => Confium::Digest
22
22
  module Confium
23
23
  module Crypto
24
+ # @type ivar @interfaces: Hash[Symbol, untyped]
24
25
  @interfaces = {}
25
26
 
26
27
  class << self
@@ -33,6 +33,7 @@ module Confium
33
33
 
34
34
  def initialize
35
35
  @content = nil
36
+ # @type ivar @signers: Array[Hash[Symbol, untyped]]
36
37
  @signers = []
37
38
  end
38
39
 
@@ -81,7 +82,9 @@ module Confium
81
82
  result.fetch('signature')
82
83
  when :ecdsa_p256
83
84
  result = Confium::TC::FrostP256.sign(private_key, payload)
84
- result.fetch('signature')
85
+ # CMS carries ECDSA signatures DER-encoded (DSA-Sig-Value);
86
+ # FrostP256.sign returns { "der" => ..., "fixed" => ... }.
87
+ result.fetch('der')
85
88
  else
86
89
  raise ArgumentError, "unsupported algorithm: #{algorithm.inspect}"
87
90
  end
data/lib/confium/pki.rb CHANGED
@@ -9,5 +9,7 @@
9
9
  module Confium
10
10
  module PKI
11
11
  autoload :CMS, 'confium/pki/cms'
12
+ autoload :CertificateBuilder, 'confium/pki/certificate_builder'
13
+ autoload :CNML, 'confium/pki/cnml'
12
14
  end
13
15
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Confium::Transparency namespace file.
4
+ #
5
+ # The Transparency module itself is defined by the native Rust
6
+ # extension via magnus at require time. This file registers
7
+ # pure-Ruby autoloads for the Transparency submodules.
8
+ module Confium
9
+ module Transparency
10
+ autoload :OTS, 'confium/transparency/ots'
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Confium
4
- VERSION = '0.3.2'
4
+ VERSION = '0.3.4'
5
5
  end
data/lib/confium.rb CHANGED
@@ -18,8 +18,7 @@ begin
18
18
  # 3.1 and 3.2 get exact-minor builds while 3.3 covers 3.3+).
19
19
  # Source builds install the extension flat, without a version
20
20
  # directory.
21
- major, minor = RUBY_VERSION.split('.').first(2).map(&:to_i)
22
- window = major > 3 || minor >= 3 ? '3.3' : "#{major}.#{minor}"
21
+ window = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.3') ? '3.3' : RUBY_VERSION[/\A\d+\.\d+/]
23
22
  begin
24
23
  require_relative "confium_native/#{window}/confium_native"
25
24
  rescue LoadError
@@ -56,9 +55,21 @@ module Confium
56
55
  autoload :PolicyViolationError, 'confium/errors/policy_violation_error'
57
56
  autoload :SecureBytes, 'confium/secure_bytes'
58
57
  autoload :Policy, 'confium/policy'
59
- autoload :PKI, 'confium/pki'
58
+ # PKI is native-defined, so an autoload would never fire; the
59
+ # namespace file is eager-required and registers the pure-Ruby
60
+ # submodules (CMS, CertificateBuilder, CNML) as autoloads.
61
+ require_relative 'confium/pki'
60
62
  end
61
63
 
64
+ # Eager-load the Composite Signature JSON companion. The native
65
+ # extension defines Confium::Composite; this file reopens the class
66
+ # to add from_json transport.
67
+ require_relative 'confium/composite'
68
+
69
+ # Eager-load the Transparency namespace for the OTS autoload (the
70
+ # module itself is native-defined).
71
+ require_relative 'confium/transparency'
72
+
62
73
  # Eager-load the Audit Ruby companion. The native extension registers
63
74
  # `Confium::Audit` as a Ruby module with the `record`/`sink=`/`sink`
64
75
  # methods; the companion file defines the Sink class hierarchy on top
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: confium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Open
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-22 00:00:00.000000000 Z
11
+ date: 2026-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rb_sys
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: 1.3.0
55
- - !ruby/object:Gem::Dependency
56
- name: rake-compiler-dock
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '1.3'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1.3'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rspec
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -144,6 +130,7 @@ files:
144
130
  - lib/confium.rb
145
131
  - lib/confium/audit.rb
146
132
  - lib/confium/cfm.rb
133
+ - lib/confium/composite.rb
147
134
  - lib/confium/crypto.rb
148
135
  - lib/confium/digest.rb
149
136
  - lib/confium/errors.rb
@@ -172,6 +159,7 @@ files:
172
159
  - lib/confium/tc/session.rb
173
160
  - lib/confium/tc/session_stub.rb
174
161
  - lib/confium/tc/share_file.rb
162
+ - lib/confium/transparency.rb
175
163
  - lib/confium/transparency/ots.rb
176
164
  - lib/confium/version.rb
177
165
  homepage: https://www.confium.org