confium 0.3.0 → 0.3.2

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -0
  3. data/Cargo.lock +106 -5
  4. data/Rakefile +22 -9
  5. data/confium.gemspec +28 -28
  6. data/ext/confium_native/Cargo.toml +1 -0
  7. data/ext/confium_native/extconf.rb +4 -4
  8. data/ext/confium_native/src/attributes.rs +1 -1
  9. data/ext/confium_native/src/audit.rs +1 -1
  10. data/ext/confium_native/src/composite.rs +1 -1
  11. data/ext/confium_native/src/deployment.rs +1 -1
  12. data/ext/confium_native/src/ers.rs +1 -1
  13. data/ext/confium_native/src/openpgp.rs +1 -1
  14. data/ext/confium_native/src/path.rs +1 -1
  15. data/ext/confium_native/src/pki.rs +1 -1
  16. data/ext/confium_native/src/tc.rs +2 -2
  17. data/ext/confium_native/src/transparency.rs +5 -3
  18. data/ext/confium_native/src/util.rs +5 -0
  19. data/lib/confium/audit.rb +5 -5
  20. data/lib/confium/cfm.rb +2 -1
  21. data/lib/confium/digest.rb +4 -2
  22. data/lib/confium/errors/coerce.rb +1 -3
  23. data/lib/confium/errors/crypto_error.rb +8 -6
  24. data/lib/confium/errors/index_error.rb +9 -7
  25. data/lib/confium/errors/not_found_error.rb +9 -7
  26. data/lib/confium/errors/parse_error.rb +9 -7
  27. data/lib/confium/errors/policy_violation_error.rb +9 -7
  28. data/lib/confium/errors/threshold_error.rb +9 -7
  29. data/lib/confium/errors/unresolved_signer_error.rb +8 -6
  30. data/lib/confium/errors/validation_error.rb +10 -8
  31. data/lib/confium/errors/verification_error.rb +9 -7
  32. data/lib/confium/errors.rb +1 -1
  33. data/lib/confium/ffi.rb +4 -4
  34. data/lib/confium/lib.rb +18 -19
  35. data/lib/confium/pki/certificate_builder.rb +3 -3
  36. data/lib/confium/pki/cms/signed_data_builder.rb +10 -10
  37. data/lib/confium/pki/cms.rb +1 -1
  38. data/lib/confium/pki/cnml.rb +8 -8
  39. data/lib/confium/pki.rb +1 -1
  40. data/lib/confium/policy.rb +13 -13
  41. data/lib/confium/secure_bytes.rb +91 -89
  42. data/lib/confium/tc/coordinator.rb +7 -6
  43. data/lib/confium/tc/session.rb +5 -3
  44. data/lib/confium/tc/session_stub.rb +2 -2
  45. data/lib/confium/tc/share_file.rb +13 -13
  46. data/lib/confium/tc.rb +2 -2
  47. data/lib/confium/version.rb +1 -1
  48. data/lib/confium.rb +32 -20
  49. metadata +8 -5
@@ -14,111 +14,113 @@
14
14
  #
15
15
  # After #clear, #bytes raises Confium::ClearedError.
16
16
 
17
- class Confium::SecureBytes
18
- # Raised when #bytes is called after #clear.
19
- class ClearedError < Confium::Error
20
- def initialize(message = "SecureBytes already cleared")
21
- super(message, details: {})
17
+ module Confium
18
+ class SecureBytes
19
+ # Raised when #bytes is called after #clear.
20
+ class ClearedError < Confium::Error
21
+ def initialize(message = 'SecureBytes already cleared')
22
+ super(message, details: {})
23
+ end
22
24
  end
23
- end
24
25
 
25
- # Create a SecureBytes wrapping a copy of the given String.
26
- # The original String's contents are NOT modified; callers should
27
- # zeroize the original separately if needed.
28
- # @param raw [String] binary String (any encoding; bytes are copied)
29
- # @return [Confium::SecureBytes]
30
- def self.wrap(raw)
31
- new(raw)
32
- end
26
+ # Create a SecureBytes wrapping a copy of the given String.
27
+ # The original String's contents are NOT modified; callers should
28
+ # zeroize the original separately if needed.
29
+ # @param raw [String] binary String (any encoding; bytes are copied)
30
+ # @return [Confium::SecureBytes]
31
+ def self.wrap(raw)
32
+ new(raw)
33
+ end
33
34
 
34
- # @api private
35
- def initialize(raw)
36
- @buffer = raw.dup.force_encoding(Encoding::ASCII_8BIT)
37
- @cleared = false
38
- # Register finalizer to zeroize if the object is GC'd without
39
- # an explicit #clear call.
40
- ObjectSpace.define_finalizer(self, finalizer_proc)
41
- end
35
+ # @api private
36
+ def initialize(raw)
37
+ @buffer = raw.dup.force_encoding(Encoding::ASCII_8BIT)
38
+ @cleared = false
39
+ # Register finalizer to zeroize if the object is GC'd without
40
+ # an explicit #clear call.
41
+ ObjectSpace.define_finalizer(self, finalizer_proc)
42
+ end
42
43
 
43
- # Non-destructive read of the wrapped bytes.
44
- # @return [String] binary String (ASCII-8BIT encoding)
45
- # @raise [ClearedError] if #clear was already called
46
- def bytes
47
- raise ClearedError if @cleared
44
+ # Non-destructive read of the wrapped bytes.
45
+ # @return [String] binary String (ASCII-8BIT encoding)
46
+ # @raise [ClearedError] if #clear was already called
47
+ def bytes
48
+ raise ClearedError if @cleared
48
49
 
49
- @buffer.dup
50
- end
50
+ @buffer.dup
51
+ end
51
52
 
52
- # Destructive read: returns a copy, then zeroizes the original.
53
- # @return [String] binary String
54
- # @raise [ClearedError] if #clear was already called
55
- def bytes!
56
- raise ClearedError if @cleared
53
+ # Destructive read: returns a copy, then zeroizes the original.
54
+ # @return [String] binary String
55
+ # @raise [ClearedError] if #clear was already called
56
+ def bytes!
57
+ raise ClearedError if @cleared
57
58
 
58
- copy = @buffer.dup
59
- clear
60
- copy
61
- end
59
+ copy = @buffer.dup
60
+ clear
61
+ copy
62
+ end
62
63
 
63
- # Number of bytes. Returns 0 after #clear.
64
- # @return [Integer]
65
- def length
66
- @cleared ? 0 : @buffer.bytesize
67
- end
64
+ # Number of bytes. Returns 0 after #clear.
65
+ # @return [Integer]
66
+ def length
67
+ @cleared ? 0 : @buffer.bytesize
68
+ end
68
69
 
69
- alias size length
70
+ alias size length
70
71
 
71
- # Whether the buffer has been cleared.
72
- # @return [Boolean]
73
- def cleared?
74
- @cleared
75
- end
72
+ # Whether the buffer has been cleared.
73
+ # @return [Boolean]
74
+ def cleared?
75
+ @cleared
76
+ end
76
77
 
77
- # Zeroize the buffer immediately. Idempotent.
78
- # @return [self]
79
- def clear
80
- return self if @cleared
78
+ # Zeroize the buffer immediately. Idempotent.
79
+ # @return [self]
80
+ def clear
81
+ return self if @cleared
81
82
 
82
- # Overwrite every byte with 0x00 in place.
83
- @buffer.replace("\x00" * @buffer.bytesize)
84
- @buffer = nil
85
- @cleared = true
86
- self
87
- end
83
+ # Overwrite every byte with 0x00 in place.
84
+ @buffer.replace("\x00" * @buffer.bytesize)
85
+ @buffer = nil
86
+ @cleared = true
87
+ self
88
+ end
88
89
 
89
- # String representation for debugging. Does NOT expose the raw bytes.
90
- # @return [String]
91
- def inspect
92
- if @cleared
93
- "#<Confium::SecureBytes:0x#{object_id.to_s(16)} CLEARED>"
94
- else
95
- "#<Confium::SecureBytes:0x#{object_id.to_s(16)} #{length} bytes>"
90
+ # String representation for debugging. Does NOT expose the raw bytes.
91
+ # @return [String]
92
+ def inspect
93
+ if @cleared
94
+ "#<Confium::SecureBytes:0x#{object_id.to_s(16)} CLEARED>"
95
+ else
96
+ "#<Confium::SecureBytes:0x#{object_id.to_s(16)} #{length} bytes>"
97
+ end
96
98
  end
97
- end
98
99
 
99
- private
100
+ private
100
101
 
101
- # Finalizer proc that zeroizes the buffer if GC collects this
102
- # object without an explicit #clear. Uses object_id to find the
103
- # buffer — but since the buffer is an instance variable that may
104
- # already be collected, this is a best-effort path. Explicit #clear
105
- # is the recommended path.
106
- # @return [Proc]
107
- def finalizer_proc
108
- method(:finalize)
109
- end
102
+ # Finalizer proc that zeroizes the buffer if GC collects this
103
+ # object without an explicit #clear. Uses object_id to find the
104
+ # buffer — but since the buffer is an instance variable that may
105
+ # already be collected, this is a best-effort path. Explicit #clear
106
+ # is the recommended path.
107
+ # @return [Proc]
108
+ def finalizer_proc
109
+ method(:finalize)
110
+ end
110
111
 
111
- # Called by the GC finalizer.
112
- def finalize(_id)
113
- # Best-effort: the buffer may already be collected by the time
114
- # the finalizer runs. If @buffer still exists, zeroize it.
115
- # This is a closure over the instance — MRI guarantees the
116
- # finalizer runs after the object is unreachable but before
117
- # the buffer's memory is reused.
118
- return if @cleared
119
-
120
- @buffer&.replace("\x00" * @buffer.bytesize)
121
- @buffer = nil
122
- @cleared = true
112
+ # Called by the GC finalizer.
113
+ def finalize(_id)
114
+ # Best-effort: the buffer may already be collected by the time
115
+ # the finalizer runs. If @buffer still exists, zeroize it.
116
+ # This is a closure over the instance — MRI guarantees the
117
+ # finalizer runs after the object is unreachable but before
118
+ # the buffer's memory is reused.
119
+ return if @cleared
120
+
121
+ @buffer&.replace("\x00" * @buffer.bytesize)
122
+ @buffer = nil
123
+ @cleared = true
124
+ end
123
125
  end
124
126
  end
@@ -25,7 +25,7 @@ module Confium
25
25
  @sessions = {}
26
26
  end
27
27
 
28
- def create_session(message:, threshold:, unlock_window: 14400)
28
+ def create_session(message:, threshold:, unlock_window: 14_400)
29
29
  session_id = "session-#{@sessions.length + 1}"
30
30
  @sessions[session_id] = {
31
31
  message: message,
@@ -33,7 +33,7 @@ module Confium
33
33
  unlock_window: unlock_window,
34
34
  state: :pending,
35
35
  commitments: [],
36
- shares: [],
36
+ shares: []
37
37
  }
38
38
  session_id
39
39
  end
@@ -45,9 +45,9 @@ module Confium
45
45
  def submit_commitment(session_id, signer_id, commitment_bytes)
46
46
  session = @sessions[session_id] or raise "Unknown session: #{session_id}"
47
47
  session[:commitments] << { signer_id: signer_id, bytes: commitment_bytes }
48
- if session[:commitments].length >= session[:threshold]
49
- session[:state] = :commitments_collected
50
- end
48
+ return unless session[:commitments].length >= session[:threshold]
49
+
50
+ session[:state] = :commitments_collected
51
51
  end
52
52
 
53
53
  def submit_share(session_id, signer_id, share_bytes)
@@ -57,7 +57,8 @@ module Confium
57
57
 
58
58
  def aggregate(session_id)
59
59
  session = @sessions[session_id] or raise "Unknown session: #{session_id}"
60
- raise "Threshold not met" if session[:shares].length < session[:threshold]
60
+ raise 'Threshold not met' if session[:shares].length < session[:threshold]
61
+
61
62
  session[:state] = :completed
62
63
  # In real implementation, calls FFI to aggregate shares
63
64
  session[:shares].map { |s| s[:bytes] }.join
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "confium/lib"
3
+ require 'confium/lib'
4
4
 
5
5
  # Confium::TC::Session wraps a threshold cryptography session.
6
6
  #
@@ -32,7 +32,8 @@ module Confium
32
32
  end
33
33
 
34
34
  def set_local_share(share_bytes)
35
- raise ArgumentError, "share must be a String" unless share_bytes.is_a?(String)
35
+ raise ArgumentError, 'share must be a String' unless share_bytes.is_a?(String)
36
+
36
37
  @local_share = share_bytes
37
38
  end
38
39
 
@@ -41,7 +42,8 @@ module Confium
41
42
  end
42
43
 
43
44
  def result
44
- raise "Session not complete" unless complete?
45
+ raise 'Session not complete' unless complete?
46
+
45
47
  @result
46
48
  end
47
49
  end
@@ -31,12 +31,12 @@ module Confium
31
31
  # call confium_tc::session::Session::round_step.
32
32
  def round_step(_incoming_messages)
33
33
  @round += 1
34
- @complete = @round >= 3 # FROST is 3 rounds
34
+ @complete = @round >= 3 # FROST is 3 rounds
35
35
  { outgoing: [], complete: @complete }
36
36
  end
37
37
 
38
38
  def result
39
- nil # Real implementation returns the signature/secret bytes.
39
+ nil # Real implementation returns the signature/secret bytes.
40
40
  end
41
41
  end
42
42
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
- require "fileutils"
3
+ require 'json'
4
+ require 'fileutils'
5
5
 
6
6
  module Confium
7
7
  module TC
@@ -46,11 +46,11 @@ module Confium
46
46
  def self.from_json(json)
47
47
  d = JSON.parse(json)
48
48
  new(
49
- scheme: d.fetch("scheme"),
50
- threshold: d.fetch("threshold"),
51
- party_count: d.fetch("party_count"),
52
- public_key: [d.fetch("public_key")].pack("H*"),
53
- shares: d.fetch("shares").map { |h| [h].pack("H*") },
49
+ scheme: d.fetch('scheme'),
50
+ threshold: d.fetch('threshold'),
51
+ party_count: d.fetch('party_count'),
52
+ public_key: [d.fetch('public_key')].pack('H*'),
53
+ shares: d.fetch('shares').map { |h| [h].pack('H*') }
54
54
  )
55
55
  end
56
56
 
@@ -67,8 +67,8 @@ module Confium
67
67
  scheme: scheme,
68
68
  threshold: threshold,
69
69
  party_count: party_count,
70
- public_key: public_key.unpack1("H*"),
71
- shares: shares.map { |s| s.unpack1("H*") },
70
+ public_key: public_key.unpack1('H*'),
71
+ shares: shares.map { |s| s.unpack1('H*') }
72
72
  )
73
73
  end
74
74
 
@@ -76,10 +76,10 @@ module Confium
76
76
  def self.from_keygen(scheme_name, keygen_result)
77
77
  new(
78
78
  scheme: scheme_name,
79
- threshold: nil, # not carried by the keygen Hash; caller knows
80
- party_count: keygen_result["shares"].length,
81
- public_key: keygen_result["public_key"],
82
- shares: keygen_result["shares"],
79
+ threshold: nil, # not carried by the keygen Hash; caller knows
80
+ party_count: keygen_result['shares'].length,
81
+ public_key: keygen_result['public_key'],
82
+ shares: keygen_result['shares']
83
83
  )
84
84
  end
85
85
  end
data/lib/confium/tc.rb CHANGED
@@ -11,7 +11,7 @@
11
11
  # for the full interface specification.
12
12
  module Confium
13
13
  module TC
14
- autoload :Session, "confium/tc/session"
15
- autoload :Coordinator, "confium/tc/coordinator"
14
+ autoload :Session, 'confium/tc/session'
15
+ autoload :Coordinator, 'confium/tc/coordinator'
16
16
  end
17
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Confium
4
- VERSION = "0.3.0"
4
+ VERSION = '0.3.2'
5
5
  end
data/lib/confium.rb CHANGED
@@ -9,51 +9,63 @@
9
9
  # Pure-Ruby companions (the error hierarchy, Enumerable mixins, etc.)
10
10
  # are loaded lazily via autoload — see lib/confium/<name>.rb.
11
11
 
12
- require_relative "confium/version"
12
+ require_relative 'confium/version'
13
13
 
14
14
  begin
15
- require_relative "confium_native/confium_native"
15
+ # Pre-built platform gems ship one extension per C-ABI window
16
+ # (Ruby 3.2 broke the 3.1 ABI — object shapes — and rb-sys
17
+ # references the VM pointer libruby stopped exporting in 3.3, so
18
+ # 3.1 and 3.2 get exact-minor builds while 3.3 covers 3.3+).
19
+ # Source builds install the extension flat, without a version
20
+ # directory.
21
+ major, minor = RUBY_VERSION.split('.').first(2).map(&:to_i)
22
+ window = major > 3 || minor >= 3 ? '3.3' : "#{major}.#{minor}"
23
+ begin
24
+ require_relative "confium_native/#{window}/confium_native"
25
+ rescue LoadError
26
+ require_relative 'confium_native/confium_native'
27
+ end
16
28
  rescue LoadError => e
17
- warn "confium: native extension not built — run `bundle exec rake compile`"
29
+ warn 'confium: native extension not built — run `bundle exec rake compile`'
18
30
  raise e
19
31
  end
20
32
 
21
33
  # Register autoloads on the native-defined PKI::CMS module so Ruby
22
34
  # companions like SignedDataBuilder load on first reference. Eager-
23
35
  # required because the module already exists at this point.
24
- require_relative "confium/pki/cms"
36
+ require_relative 'confium/pki/cms'
25
37
 
26
38
  # The native extension defines Confium::OpenPGP with _native_armor /
27
39
  # _native_dearmor. This file adds the idiomatic Ruby wrappers with
28
40
  # default args. Eager-required for the same reason as PKI::CMS.
29
- require_relative "confium/openpgp"
41
+ require_relative 'confium/openpgp'
30
42
 
31
43
  module Confium
32
44
  # Error hierarchy autoloads. Each subclass lives in its own file so
33
45
  # callers can `autoload :FooError, "confium/errors/foo"` and avoid
34
46
  # loading the whole hierarchy if they only rescue one type.
35
- autoload :Error, "confium/errors"
36
- autoload :ParseError, "confium/errors/parse_error"
37
- autoload :ValidationError, "confium/errors/validation_error"
38
- autoload :VerificationError, "confium/errors/verification_error"
39
- autoload :ThresholdError, "confium/errors/threshold_error"
40
- autoload :CryptoError, "confium/errors/crypto_error"
41
- autoload :NotFoundError, "confium/errors/not_found_error"
42
- autoload :IndexError, "confium/errors/index_error"
43
- autoload :UnresolvedSignerError,"confium/errors/unresolved_signer_error"
44
- autoload :PolicyViolationError, "confium/errors/policy_violation_error"
45
- autoload :SecureBytes, "confium/secure_bytes"
46
- autoload :Policy, "confium/policy"
47
- autoload :PKI, "confium/pki"
47
+ autoload :Error, 'confium/errors'
48
+ autoload :ParseError, 'confium/errors/parse_error'
49
+ autoload :ValidationError, 'confium/errors/validation_error'
50
+ autoload :VerificationError, 'confium/errors/verification_error'
51
+ autoload :ThresholdError, 'confium/errors/threshold_error'
52
+ autoload :CryptoError, 'confium/errors/crypto_error'
53
+ autoload :NotFoundError, 'confium/errors/not_found_error'
54
+ autoload :IndexError, 'confium/errors/index_error'
55
+ autoload :UnresolvedSignerError, 'confium/errors/unresolved_signer_error'
56
+ autoload :PolicyViolationError, 'confium/errors/policy_violation_error'
57
+ autoload :SecureBytes, 'confium/secure_bytes'
58
+ autoload :Policy, 'confium/policy'
59
+ autoload :PKI, 'confium/pki'
48
60
  end
49
61
 
50
62
  # Eager-load the Audit Ruby companion. The native extension registers
51
63
  # `Confium::Audit` as a Ruby module with the `record`/`sink=`/`sink`
52
64
  # methods; the companion file defines the Sink class hierarchy on top
53
65
  # of that module.
54
- require_relative "confium/audit"
66
+ require_relative 'confium/audit'
55
67
 
56
68
  # Eager-load the TC ShareFile Ruby companion. The native extension
57
69
  # defines `Confium::TC` as a Ruby module; this file adds the
58
70
  # `ShareFile` class for filesystem-backed share persistence.
59
- require_relative "confium/tc/share_file"
71
+ require_relative 'confium/tc/share_file'
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: confium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Open
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-08-22 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: rb_sys
@@ -43,14 +44,14 @@ dependencies:
43
44
  requirements:
44
45
  - - "~>"
45
46
  - !ruby/object:Gem::Version
46
- version: 1.2.0
47
+ version: 1.3.0
47
48
  type: :development
48
49
  prerelease: false
49
50
  version_requirements: !ruby/object:Gem::Requirement
50
51
  requirements:
51
52
  - - "~>"
52
53
  - !ruby/object:Gem::Version
53
- version: 1.2.0
54
+ version: 1.3.0
54
55
  - !ruby/object:Gem::Dependency
55
56
  name: rake-compiler-dock
56
57
  requirement: !ruby/object:Gem::Requirement
@@ -182,6 +183,7 @@ metadata:
182
183
  homepage_uri: https://www.confium.org
183
184
  source_code_uri: https://github.com/confium/confium-ruby
184
185
  rubygems_mfa_required: 'true'
186
+ post_install_message:
185
187
  rdoc_options: []
186
188
  require_paths:
187
189
  - lib
@@ -196,7 +198,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
196
198
  - !ruby/object:Gem::Version
197
199
  version: '0'
198
200
  requirements: []
199
- rubygems_version: 4.0.16
201
+ rubygems_version: 3.5.22
202
+ signing_key:
200
203
  specification_version: 4
201
204
  summary: Ruby bindings for the Confium multi-stakeholder threshold cryptography framework.
202
205
  test_files: []