secp256k1-native 0.15.0 → 0.16.0
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 +22 -0
- data/README.md +16 -14
- data/lib/secp256k1/version.rb +1 -1
- data/lib/secp256k1.rb +60 -12
- data/lib/secp256k1_native.bundle +0 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e99016475f085f293dea746296947233bc59879e7e5b864da191ac9760ec665b
|
|
4
|
+
data.tar.gz: 409aac96468c5557951cb754a8bb0e9fd49e16b7526fdb31b22cb8441b2b2d85
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8fd3c3f89628724cfbad9866ba636c131350fcbf4977016d63eb4ec0c18568a037be17af63ff792981a49c74915942291384afc25989a097d31f6056f3fe4b0e
|
|
7
|
+
data.tar.gz: ac8290a66020b4ba4d5e0f4f77228bee25bf984eb517997b3178b4b51b165cf88a905c489bd429bc68a214d052bf56f412e27588ab07d434512a582c576fe47b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.16.0] - 2026-04-29
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
|
|
7
|
+
- `Point#mul` is now constant-time (Montgomery ladder) by default, matching OpenSSL behaviour. The previous variable-time wNAF implementation is available as `Point#mul_vt`
|
|
8
|
+
- `Point#mul` raises `InsecureOperationError` without the native C extension unless explicitly allowed via `SECP256K1_ALLOW_PURE_RUBY_CT=1` or `Secp256k1.allow_pure_ruby_ct!`
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `Point#mul_vt` for explicit variable-time scalar multiplication (public scalars only)
|
|
13
|
+
- `Secp256k1.native?` to check whether the C extension is loaded
|
|
14
|
+
- `Secp256k1.allow_pure_ruby_ct!` and `SECP256K1_ALLOW_PURE_RUBY_CT` env var for opting in to pure-Ruby constant-time operations
|
|
15
|
+
- Evidence-based risk assessment documentation (`docs/risks.md`)
|
|
16
|
+
- MkDocs site with GitHub Pages automation
|
|
17
|
+
- YARD-generated API reference
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- `Point#mul_ct` is now a deprecated alias for `Point#mul`
|
|
22
|
+
- Licence changed from Open BSV License to MIT
|
|
23
|
+
- Documentation reorganised into focused documents (architecture, security, performance, design rationale)
|
|
24
|
+
|
|
3
25
|
## [0.15.0] - 2026-04-27
|
|
4
26
|
|
|
5
27
|
### Added
|
data/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# secp256k1-native
|
|
2
2
|
|
|
3
|
+
> **Before using a custom cryptographic implementation, read [Evaluating the risks](https://sgbett.github.io/secp256k1-native/risks/) — it examines what the empirical evidence says about rolling your own crypto and where this gem sits in that landscape.**
|
|
4
|
+
|
|
3
5
|
Pure native C secp256k1 implementation for Ruby (no libsecp256k1 dependency).
|
|
4
6
|
|
|
5
|
-
Provides secp256k1 elliptic curve cryptography for Ruby — field arithmetic, scalar operations, Jacobian point arithmetic, and constant-time scalar multiplication — via an optional native C extension. The gem ships a pure-Ruby base layer that works out of the box on any Ruby 2.7+ platform, with the C extension
|
|
7
|
+
Provides secp256k1 elliptic curve cryptography for Ruby — field arithmetic, scalar operations, Jacobian point arithmetic, and constant-time scalar multiplication — via an optional native C extension. The gem ships a pure-Ruby base layer that works out of the box on any Ruby 2.7+ platform, with the C extension providing constant-time guarantees and ~22x acceleration when available.
|
|
6
8
|
|
|
7
9
|
Used by the [bsv-ruby-sdk](https://github.com/sgbett/bsv-ruby-sdk) and suitable for any Ruby project requiring secp256k1 operations.
|
|
8
10
|
|
|
@@ -36,14 +38,14 @@ require 'secp256k1'
|
|
|
36
38
|
# Generator point
|
|
37
39
|
g = Secp256k1::Point.generator
|
|
38
40
|
|
|
39
|
-
# Scalar multiplication (
|
|
40
|
-
scalar = 0xdeadbeef
|
|
41
|
-
point = g.mul(scalar)
|
|
42
|
-
puts point.x.to_s(16)
|
|
43
|
-
|
|
44
|
-
# Constant-time scalar multiplication (for secret scalars)
|
|
41
|
+
# Scalar multiplication (constant-time by default — safe for all scalars)
|
|
45
42
|
secret = 0xcafebabe
|
|
46
|
-
pubkey = g.
|
|
43
|
+
pubkey = g.mul(secret)
|
|
44
|
+
puts pubkey.x.to_s(16)
|
|
45
|
+
|
|
46
|
+
# Variable-time scalar multiplication (faster, for public scalars only)
|
|
47
|
+
scalar = 0xdeadbeef
|
|
48
|
+
point = g.mul_vt(scalar)
|
|
47
49
|
|
|
48
50
|
# SEC1 encoding / decoding
|
|
49
51
|
compressed = pubkey.to_octet_string(:compressed) # 33 bytes
|
|
@@ -95,12 +97,12 @@ The wNAF loop and ECDSA/Schnorr logic remain in Ruby, calling native primitives
|
|
|
95
97
|
|
|
96
98
|
### Performance
|
|
97
99
|
|
|
98
|
-
| Mode |
|
|
99
|
-
|
|
100
|
-
| Pure Ruby |
|
|
101
|
-
|
|
|
100
|
+
| Mode | Sign (ops/sec) | Verify (ops/sec) |
|
|
101
|
+
|------|---------------|-----------------|
|
|
102
|
+
| Pure Ruby | 100 | 97 |
|
|
103
|
+
| C extension | 2,302 | 1,826 |
|
|
102
104
|
|
|
103
|
-
The extension provides
|
|
105
|
+
The C extension provides ~23× speedup for signing and ~19× for verification — but performance is secondary to security. The primary purpose of the C extension is to provide **hardware-level constant-time guarantees** that Ruby's variable-width `Integer` internals cannot offer. Users handling secret key material should evaluate whether the pure-Ruby implementation is appropriate for their threat model. See [docs/performance.md](docs/performance.md) for detailed analysis.
|
|
104
106
|
|
|
105
107
|
## Building the native extension
|
|
106
108
|
|
|
@@ -116,7 +118,7 @@ bundle exec rake compile
|
|
|
116
118
|
|
|
117
119
|
The compiled bundle is placed at `lib/secp256k1_native.bundle` (macOS) or `lib/secp256k1_native.so` (Linux).
|
|
118
120
|
|
|
119
|
-
`extconf.rb` checks for `__uint128_t` availability at configure time. If the type is absent, a no-op Makefile is generated and the extension is
|
|
121
|
+
`extconf.rb` checks for `__uint128_t` availability at configure time. If the type is absent, a no-op Makefile is generated and the extension is skipped. At runtime, `secp256k1.rb` wraps the `require` in a `rescue LoadError` — if the bundle is absent, the pure-Ruby implementation is used for public-scalar operations. Constant-time operations (`mul_ct`) will raise `InsecureOperationError` unless explicitly allowed via `SECP256K1_ALLOW_PURE_RUBY_CT=1` or `Secp256k1.allow_pure_ruby_ct!`.
|
|
120
122
|
|
|
121
123
|
## Running tests
|
|
122
124
|
|
data/lib/secp256k1/version.rb
CHANGED
data/lib/secp256k1.rb
CHANGED
|
@@ -19,6 +19,32 @@ require_relative 'secp256k1/version'
|
|
|
19
19
|
# All field operations work on plain Ruby +Integer+ values (arbitrary
|
|
20
20
|
# precision, C-backed in MRI). No external gems required.
|
|
21
21
|
module Secp256k1
|
|
22
|
+
# Raised when a constant-time operation is attempted without the native
|
|
23
|
+
# C extension loaded. The pure-Ruby implementation cannot guarantee
|
|
24
|
+
# constant-time execution due to interpreter-introduced timing variability.
|
|
25
|
+
class InsecureOperationError < SecurityError; end
|
|
26
|
+
|
|
27
|
+
# Whether the native C extension is loaded and active.
|
|
28
|
+
#
|
|
29
|
+
# @return [Boolean]
|
|
30
|
+
def self.native?
|
|
31
|
+
@native == true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Explicitly allow constant-time operations in pure-Ruby mode.
|
|
35
|
+
# Call this only after evaluating the risks documented in docs/risks.md.
|
|
36
|
+
def self.allow_pure_ruby_ct!
|
|
37
|
+
@allow_pure_ruby_ct = true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @api private
|
|
41
|
+
def self.pure_ruby_ct_allowed?
|
|
42
|
+
@allow_pure_ruby_ct || ENV.key?('SECP256K1_ALLOW_PURE_RUBY_CT')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
@native = false
|
|
46
|
+
@allow_pure_ruby_ct = false
|
|
47
|
+
|
|
22
48
|
# The secp256k1 field prime: p = 2^256 - 2^32 - 977
|
|
23
49
|
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
|
|
24
50
|
|
|
@@ -510,41 +536,58 @@ module Secp256k1
|
|
|
510
536
|
end
|
|
511
537
|
end
|
|
512
538
|
|
|
513
|
-
# Scalar multiplication: self * scalar (
|
|
539
|
+
# Scalar multiplication: self * scalar (constant-time, Montgomery ladder).
|
|
540
|
+
#
|
|
541
|
+
# Processes all 256 bits unconditionally so execution time does not
|
|
542
|
+
# depend on the scalar value. Safe for both secret and public scalars.
|
|
543
|
+
# This is the default because the safe path should be the easy path.
|
|
514
544
|
#
|
|
515
|
-
#
|
|
516
|
-
#
|
|
545
|
+
# For performance-critical public-scalar paths (e.g. batch verification)
|
|
546
|
+
# where constant-time is unnecessary, use {#mul_vt}.
|
|
547
|
+
#
|
|
548
|
+
# Raises {InsecureOperationError} if the native C extension is not loaded,
|
|
549
|
+
# unless explicitly allowed via {Secp256k1.allow_pure_ruby_ct!} or the
|
|
550
|
+
# +SECP256K1_ALLOW_PURE_RUBY_CT+ environment variable.
|
|
517
551
|
#
|
|
518
552
|
# @param scalar [Integer] the scalar multiplier
|
|
519
553
|
# @return [Point] the resulting point
|
|
520
554
|
def mul(scalar)
|
|
555
|
+
unless Secp256k1.native? || Secp256k1.pure_ruby_ct_allowed?
|
|
556
|
+
raise Secp256k1::InsecureOperationError,
|
|
557
|
+
'mul requires the native C extension for constant-time guarantees. ' \
|
|
558
|
+
'Set SECP256K1_ALLOW_PURE_RUBY_CT=1 or call Secp256k1.allow_pure_ruby_ct! to override.'
|
|
559
|
+
end
|
|
560
|
+
|
|
521
561
|
return self.class.infinity if scalar.zero? || infinity?
|
|
522
562
|
|
|
523
563
|
scalar %= N
|
|
524
564
|
return self.class.infinity if scalar.zero?
|
|
525
565
|
|
|
526
|
-
jp = Secp256k1.
|
|
566
|
+
jp = Secp256k1.scalar_multiply_ct(scalar, @x, @y)
|
|
527
567
|
affine = Secp256k1.jp_to_affine(jp)
|
|
528
568
|
return self.class.infinity if affine.nil?
|
|
529
569
|
|
|
530
570
|
self.class.new(affine[0], affine[1])
|
|
531
571
|
end
|
|
532
572
|
|
|
533
|
-
#
|
|
573
|
+
# @deprecated Use {#mul} instead. Alias retained for backward compatibility.
|
|
574
|
+
alias mul_ct mul
|
|
575
|
+
|
|
576
|
+
# Variable-time scalar multiplication: self * scalar (wNAF).
|
|
534
577
|
#
|
|
535
|
-
#
|
|
536
|
-
#
|
|
537
|
-
#
|
|
578
|
+
# Faster than {#mul} but leaks timing information about the scalar.
|
|
579
|
+
# Use only when the scalar is public (e.g. signature verification,
|
|
580
|
+
# computing known generator multiples). Never use with secret scalars.
|
|
538
581
|
#
|
|
539
|
-
# @param scalar [Integer] the
|
|
582
|
+
# @param scalar [Integer] the public scalar multiplier
|
|
540
583
|
# @return [Point] the resulting point
|
|
541
|
-
def
|
|
584
|
+
def mul_vt(scalar)
|
|
542
585
|
return self.class.infinity if scalar.zero? || infinity?
|
|
543
586
|
|
|
544
587
|
scalar %= N
|
|
545
588
|
return self.class.infinity if scalar.zero?
|
|
546
589
|
|
|
547
|
-
jp = Secp256k1.
|
|
590
|
+
jp = Secp256k1.scalar_multiply_wnaf(scalar, @x, @y)
|
|
548
591
|
affine = Secp256k1.jp_to_affine(jp)
|
|
549
592
|
return self.class.infinity if affine.nil?
|
|
550
593
|
|
|
@@ -619,8 +662,13 @@ module Secp256k1
|
|
|
619
662
|
jp_double jp_add jp_neg scalar_multiply_ct].each do |m|
|
|
620
663
|
singleton_class.define_method(m, Secp256k1Native.method(m).to_proc)
|
|
621
664
|
end
|
|
665
|
+
|
|
666
|
+
@native = true
|
|
622
667
|
rescue LoadError
|
|
623
|
-
# Extension not compiled — pure-Ruby fallback
|
|
668
|
+
# Extension not compiled — pure-Ruby fallback.
|
|
669
|
+
warn '[secp256k1-native] Native C extension not loaded — falling back to pure Ruby. ' \
|
|
670
|
+
'Constant-time operations (mul_ct) will raise unless explicitly allowed. ' \
|
|
671
|
+
'See: https://sgbett.github.io/secp256k1-native/risks/'
|
|
624
672
|
end
|
|
625
673
|
end
|
|
626
674
|
# rubocop:enable Naming/MethodParameterName, Metrics/ModuleLength
|
data/lib/secp256k1_native.bundle
CHANGED
|
Binary file
|