phylax 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e381c418f0f615ce325714db60e7859fb75be0584ed3cef79cdb39cf0a3ab5ce
4
- data.tar.gz: 552fd46214a14a2faaa3038ec6f4bbd5a677367fb5e9740d894e8bf3605981bd
3
+ metadata.gz: f8a97badaa44fd356a5e414446dc937fa8796a5e9b9a670cc8d43f76048ee275
4
+ data.tar.gz: bbc4c29d490c2f4599a49b2fd055c8abf78d5c771b37e9135ca76dd1236c0a49
5
5
  SHA512:
6
- metadata.gz: 4c77e2bf51d2a617e664cfc40c40fd91e79700c13d4d1a6ebc954909a24251b8dcc3cb48d84d8d9e5ead11964e3a18e60eaf48ef3ae49d92ac439e6044fe4fa3
7
- data.tar.gz: 4901fb8c6cee2d011097a46f500009882475220ce30980676bd6dcbbeeb788be1fc0e948c61d9160ef6cd6971b1549e168bebeb83f44fe38295219ba5d1ec018
6
+ metadata.gz: 598b3a11263052f1fbd66dbe5eacb78924c470c312fb8484ab08c84998c15eb8828776c0f753e3146f2e8c98cf32a8c08b7462d74b57142f5f3768b7cd50493c
7
+ data.tar.gz: 1d17e2334c63d1c5d0a9e3257faaf93586a447dc859cd730ecb00c9adce40b4b75c6701f75852c28e26d86f0cc896fec3b88be5bf22c81077eb4791b0995d7fd
data/README.md CHANGED
@@ -1,155 +1,11 @@
1
- # phylax
2
-
3
- **Misuse-resistant cryptography for Ruby, backed by the Windows CNG provider.**
4
-
5
- `phylax` (Greek *φύλαξ*, "guardian") is a thin, **safe-by-default** binding to the
6
- cryptography Windows already ships and validates: the **Cryptography API: Next
7
- Generation** (CNG / `bcrypt.dll`) and **DPAPI** (`crypt32.dll`). It implements no
8
- cryptography of its own — it exposes the operating system's own primitives through
9
- an ergonomic, hard-to-misuse API.
10
-
11
- The design goal is that the *easy* way is the *safe* way:
12
-
13
- - **Authenticated encryption that can't be nonce-reused.** `SecretBox` (AES-256-GCM)
14
- generates a fresh 96-bit nonce on every `seal` and frames it into the output, so
15
- the catastrophic GCM failure mode — reusing a `(key, nonce)` pair — is impossible
16
- through the public API. There is no nonce parameter to get wrong.
17
- - **Decryption is all-or-nothing.** `open` verifies the authentication tag inside the
18
- OS call and raises `Phylax::AuthenticationError` rather than ever returning
19
- unauthenticated plaintext.
20
- - **No silent weakening.** A key that isn't exactly 32 bytes is a loud error, not a
21
- silently-truncated AES-128 key. Only SHA-2 is accepted — `:md5`/`:sha1` raise.
22
- - **Constant-time comparison** is provided so you don't verify MACs with `==`.
23
-
24
- | What | API |
25
- |---|---|
26
- | Secure random | `Phylax.random_bytes(n)` |
27
- | Hashing (one-shot) | `Phylax.sha256(data)`, `.sha384`, `.sha512` |
28
- | Hashing (streaming) | `Phylax::Digest.new(:sha256)` |
29
- | HMAC (one-shot) | `Phylax.hmac_sha256(key, data)`, `.hmac_sha384`, `.hmac_sha512` |
30
- | HMAC (streaming) | `Phylax::HMAC.new(:sha256, key)`, `Phylax::HMAC.verify(...)` |
31
- | Key derivation | `Phylax.pbkdf2(password:, salt:, iterations:, length:, hash:)` |
32
- | Authenticated encryption | `Phylax::SecretBox` (AES-256-GCM) |
33
- | Constant-time compare | `Phylax.secure_compare(a, b)` |
34
- | Secrets at rest | `Phylax.protect(data, scope:, entropy:)` / `Phylax.unprotect` |
35
-
36
- ## Requirements
37
-
38
- - **Windows** with a native **MSVC (mswin)** Ruby. Not supported on MinGW/UCRT.
39
- - Visual Studio 2017+ / Build Tools with the **Desktop development with C++** workload.
40
-
41
- ## Install
42
-
43
- ```sh
44
- gem install phylax
45
- ```
46
-
47
- ## Authenticated encryption — `SecretBox`
48
-
49
- ```ruby
50
- require "phylax"
51
-
52
- key = Phylax::SecretBox.generate_key # 32 cryptographically random bytes
53
- box = Phylax::SecretBox.new(key)
54
-
55
- sealed = box.seal("attack at dawn") # => nonce ‖ ciphertext ‖ tag (binary)
56
- box.open(sealed) # => "attack at dawn"
57
-
58
- # Additional authenticated data (authenticated, not encrypted) — must match.
59
- sealed = box.seal(payload, aad: "v1:user-42")
60
- box.open(sealed, aad: "v1:user-42") # ok
61
- box.open(sealed, aad: "v1:user-99") # raises Phylax::AuthenticationError
62
-
63
- # Any tampering with the nonce, ciphertext, tag, or aad fails closed:
64
- box.open(sealed.tap { |s| s.setbyte(20, s.getbyte(20) ^ 1) })
65
- # => raises Phylax::AuthenticationError (no plaintext is returned)
66
- ```
67
-
68
- Derive the key from a password instead (PBKDF2-HMAC-SHA256):
69
-
70
- ```ruby
71
- salt = Phylax.random_bytes(16) # store this alongside the ciphertext
72
- box = Phylax::SecretBox.from_password("correct horse battery staple",
73
- salt: salt, iterations: 600_000)
74
- ```
75
-
76
- **Wire format.** `seal` returns `nonce (12 bytes) ‖ ciphertext (= plaintext length) ‖ tag (16 bytes)`,
77
- so the overhead is a constant `Phylax::SecretBox::OVERHEAD` (28) bytes. This is
78
- standard AES-256-GCM: a blob from `seal` can be decrypted by any GCM
79
- implementation (e.g. OpenSSL) given the same key, and vice versa.
80
-
81
- ## Hashing and HMAC
82
-
83
- ```ruby
84
- Phylax.sha256("data") # => 32 raw bytes (ASCII-8BIT)
85
- Phylax.sha256("data").unpack1("H*") # => hex
86
-
87
- # Streaming — non-destructive #digest, so you can keep updating:
88
- d = Phylax::Digest.new(:sha256)
89
- d << "chunk one" << "chunk two"
90
- d.hexdigest
91
-
92
- Phylax.hmac_sha256(key, "message") # keyed MAC, raw bytes
93
-
94
- # Verify a MAC the safe way (constant-time, never raises on mismatch):
95
- Phylax::HMAC.verify(:sha256, key, message, received_tag) # => true / false
96
- ```
97
-
98
- ## Key derivation, random, and comparison
99
-
100
- ```ruby
101
- Phylax.pbkdf2(password: pw, salt: salt, iterations: 600_000, length: 32)
102
- Phylax.random_bytes(16) # system CSPRNG (CTR_DRBG)
103
- Phylax.secure_compare(tag_a, tag_b) # constant-time for equal-length inputs
104
- ```
105
-
106
- ## Secrets at rest — DPAPI
107
-
108
- `protect` encrypts data with a key the OS derives from the current user (default)
109
- or the machine, so the blob can only be read back on the same machine (and, for
110
- `:user` scope, by the same user). Great for storing a credential or a `SecretBox`
111
- key on disk without a master password.
112
-
113
- ```ruby
114
- blob = Phylax.protect("api-token-value") # bound to this Windows user
115
- Phylax.unprotect(blob) # => "api-token-value"
116
-
117
- # Optional second secret ("entropy") that must be supplied to unprotect:
118
- blob = Phylax.protect(secret, entropy: app_salt)
119
- Phylax.unprotect(blob, entropy: app_salt)
120
-
121
- # Machine scope: any account on this machine can unprotect.
122
- Phylax.protect(secret, scope: :machine)
123
- ```
124
-
125
- ## Errors
126
-
127
- ```
128
- StandardError
129
- └─ Phylax::Error # base for everything phylax raises
130
- ├─ Phylax::AuthenticationError # GCM tag mismatch / tamper, or DPAPI integrity failure
131
- └─ Phylax::OSError # other Windows API failure (#api, #code)
132
- ```
133
-
134
- `ArgumentError`/`TypeError` are raised for bad sizes or types *before* any OS call
135
- (e.g. a key that isn't 32 bytes, an unknown hash, a non-String input).
136
-
137
- ## Security notes
138
-
139
- - **Algorithms.** AES-256-GCM (128-bit tag), SHA-256/384/512, HMAC-SHA-2,
140
- PBKDF2-HMAC-SHA-2, `BCryptGenRandom` (SP 800-90A CTR_DRBG). All are the OS's
141
- validated implementations; phylax only marshals bytes.
142
- - **Random-nonce limit.** With random 96-bit nonces, keep a single `SecretBox` key
143
- under ~2³² messages (the GCM birthday bound). Rotate keys for very high volume.
144
- - **`secure_compare` leaks length.** It returns `false` immediately when lengths
145
- differ and is constant-time only across equal-length inputs — fine for comparing
146
- fixed-length MACs, which is its purpose.
147
- - **DPAPI tamper detection is best-effort.** Windows may return an error *or*, rarely,
148
- succeed with corrupted output on a mangled blob. If you need a hard integrity
149
- guarantee, wrap the data in a `SecretBox` (or add your own HMAC) first.
150
- - **Windows/MSVC only.** The extension links `bcrypt.lib` + `crypt32.lib` and is
151
- built with `cl.exe` against a native-MSVC Ruby.
152
-
153
- ## License
154
-
155
- [MIT](LICENSE.txt).
1
+ # phylax (discontinued)
2
+
3
+ **phylax is no longer maintained.** This final release (0.1.1) contains no
4
+ code: installing it compiles nothing, and `require "phylax"` raises a
5
+ `LoadError` explaining the discontinuation.
6
+
7
+ Use Ruby's built-in openssl, lockbox or rbnacl instead.
8
+
9
+ Earlier releases remain on RubyGems only because RubyGems does not allow
10
+ versions older than 30 days to be yanked. They are unmaintained and not
11
+ recommended.
data/lib/phylax.rb CHANGED
@@ -1,252 +1,4 @@
1
- # frozen_string_literal: true
2
-
3
- require "phylax/version"
4
- require "phylax/phylax" # native extension: Phylax primitives + Digest/HMAC/SecretBox + errors
5
-
6
- # phylax — misuse-resistant cryptography for Ruby, backed by the Windows CNG
7
- # provider (bcrypt.dll) and DPAPI (crypt32.dll).
8
- #
9
- # It implements no cryptography itself; it is a thin, hard-to-misuse binding to
10
- # the operating system's own validated primitives.
11
- #
12
- # key = Phylax::SecretBox.generate_key # 32 secure random bytes
13
- # box = Phylax::SecretBox.new(key)
14
- # sealed = box.seal("attack at dawn") # fresh nonce per call, authenticated
15
- # box.open(sealed) # => "attack at dawn"
16
- #
17
- # Phylax.sha256("data") # raw 32-byte digest
18
- # Phylax.hmac_sha256(key, "msg") # keyed MAC
19
- # Phylax.pbkdf2(password: pw, salt: s, iterations: 600_000, length: 32)
20
- # Phylax.random_bytes(16)
21
- # Phylax.protect(secret) # DPAPI, bound to this user
22
- module Phylax
23
- # The only hash algorithms the gem accepts, mapped to their C-side index.
24
- HASHES = { sha256: 0, sha384: 1, sha512: 2 }.freeze
25
-
26
- # The C bridge methods are internal plumbing; the public, validated API below
27
- # wraps them. Keep them off the public surface so callers can't skip the
28
- # keyword/size checks (e.g. a negative PBKDF2 iteration count).
29
- private_class_method :__hash, :__hmac, :__pbkdf2, :__protect, :__unprotect
30
-
31
- module_function
32
-
33
- # --- one-shot hashing ----------------------------------------------------
34
-
35
- def sha256(data) = __hash(0, data)
36
- def sha384(data) = __hash(1, data)
37
- def sha512(data) = __hash(2, data)
38
-
39
- # --- one-shot HMAC -------------------------------------------------------
40
-
41
- def hmac_sha256(key, data) = __hmac(0, key, data)
42
- def hmac_sha384(key, data) = __hmac(1, key, data)
43
- def hmac_sha512(key, data) = __hmac(2, key, data)
44
-
45
- # --- PBKDF2 key derivation ----------------------------------------------
46
-
47
- # Derive a key from a password and salt. Defaults to HMAC-SHA256.
48
- # password: secret bytes (String)
49
- # salt: non-secret bytes (String); must not be empty, >= 16 advised
50
- # iterations: PRF iteration count (Integer >= 1)
51
- # length: desired key length in bytes (Integer >= 1)
52
- # hash: :sha256 / :sha384 / :sha512
53
- def pbkdf2(password:, salt:, iterations:, length:, hash: :sha256)
54
- idx = HASHES.fetch(hash) { raise ArgumentError, "unknown hash #{hash.inspect}" }
55
- raise ArgumentError, "iterations must be >= 1" if iterations < 1
56
- raise ArgumentError, "length must be >= 1" if length < 1
57
- raise ArgumentError, "salt must not be empty" if String(salt).empty?
58
-
59
- __pbkdf2(idx, password, salt, iterations, length)
60
- end
61
-
62
- # --- DPAPI: protect secrets at rest -------------------------------------
63
-
64
- # Encrypt +data+ with a key the OS derives from the current user (default)
65
- # or the machine. The returned opaque blob can only be unprotected on the
66
- # same machine (and, for :user scope, by the same user). An optional
67
- # +entropy+ string acts as a second secret that must be supplied identically
68
- # to #unprotect.
69
- def protect(data, scope: :user, entropy: nil)
70
- __protect(data, machine_scope!(scope), entropy)
71
- end
72
-
73
- # Reverse of #protect. Raises Phylax::AuthenticationError if the blob was
74
- # tampered with, the wrong entropy was given, or it belongs to another
75
- # user/machine. (DPAPI infers the scope from the blob; +scope+ is validated
76
- # for symmetry but does not change the operation.)
77
- def unprotect(data, scope: :user, entropy: nil)
78
- machine_scope!(scope)
79
- __unprotect(data, entropy)
80
- end
81
-
82
- def machine_scope!(scope)
83
- case scope
84
- when :user then false
85
- when :machine then true
86
- else raise ArgumentError, "scope must be :user or :machine, got #{scope.inspect}"
87
- end
88
- end
89
- private_class_method :machine_scope!
90
-
91
- # Base class for every error phylax raises.
92
- class Error < StandardError; end
93
-
94
- # The data could not be authenticated: a SecretBox ciphertext (or its AAD)
95
- # was tampered with or decrypted with the wrong key, or a DPAPI blob failed
96
- # its integrity/credential check. Never trust any output when this is raised.
97
- class AuthenticationError < Error; end
98
-
99
- # A Windows API returned a failure we surface verbatim. #api names the call;
100
- # #code is the NTSTATUS or Win32 error value.
101
- class OSError < Error
102
- def api = @api
103
- def code = @code
104
- end
105
-
106
- # Streaming message digest (SHA-256/384/512).
107
- #
108
- # d = Phylax::Digest.new(:sha256)
109
- # d << "chunk one" << "chunk two"
110
- # d.hexdigest # non-destructive; you may keep updating
111
- class Digest
112
- # Bytes in the final digest, keyed by algorithm symbol.
113
- SIZES = { sha256: 32, sha384: 48, sha512: 64 }.freeze
114
-
115
- attr_reader :name
116
-
117
- def initialize(name)
118
- idx = HASHES.fetch(name) { raise ArgumentError, "unknown hash #{name.inspect}" }
119
- @name = name
120
- _init(idx)
121
- end
122
-
123
- def <<(data)
124
- update(data)
125
- end
126
-
127
- # Discard any fed data and start over.
128
- def reset
129
- _init(HASHES.fetch(@name))
130
- self
131
- end
132
-
133
- def hexdigest
134
- digest.unpack1("H*")
135
- end
136
-
137
- # Convenience: hash +data+ in one call.
138
- def self.digest(name, data)
139
- new(name).update(data).digest
140
- end
141
-
142
- def self.hexdigest(name, data)
143
- digest(name, data).unpack1("H*")
144
- end
145
-
146
- def size
147
- digest_length
148
- end
149
- alias length size
150
-
151
- def inspect
152
- "#<Phylax::Digest #{@name}>"
153
- end
154
-
155
- private :_init
156
- end
157
-
158
- # Streaming keyed MAC (HMAC-SHA-256/384/512).
159
- #
160
- # m = Phylax::HMAC.new(:sha256, key)
161
- # m << "part one" << "part two"
162
- # m.hexdigest
163
- class HMAC
164
- attr_reader :name
165
-
166
- def initialize(name, key)
167
- idx = HASHES.fetch(name) { raise ArgumentError, "unknown hash #{name.inspect}" }
168
- @name = name
169
- _init(idx, key)
170
- end
171
-
172
- def <<(data)
173
- update(data)
174
- end
175
-
176
- def hexdigest
177
- digest.unpack1("H*")
178
- end
179
-
180
- def size
181
- digest_length
182
- end
183
- alias length size
184
-
185
- # Compute the MAC of +data+ under +key+ and compare it to +expected_tag+ in
186
- # constant time. Returns true/false and never raises on mismatch — the safe
187
- # way to verify a MAC.
188
- def self.verify(name, key, data, expected_tag)
189
- tag = new(name, key).update(data).digest
190
- Phylax.secure_compare(tag, expected_tag)
191
- end
192
-
193
- def inspect
194
- "#<Phylax::HMAC #{@name}>"
195
- end
196
-
197
- private :_init
198
- end
199
-
200
- # Authenticated symmetric encryption (AES-256-GCM) with a misuse-resistant
201
- # API: a fresh 96-bit nonce is generated on every #seal and framed into the
202
- # output, so nonce reuse is impossible by construction, and #open refuses to
203
- # return anything that fails authentication.
204
- class SecretBox
205
- KEY_BYTES = 32
206
- NONCE_BYTES = 12
207
- TAG_BYTES = 16
208
- OVERHEAD = NONCE_BYTES + TAG_BYTES # bytes #seal adds over the plaintext
209
-
210
- # A new random 32-byte key.
211
- def self.generate_key
212
- Phylax.random_bytes(KEY_BYTES)
213
- end
214
-
215
- # Derive a box's key from a password via PBKDF2-HMAC-SHA256. +salt+ is
216
- # required and should be random and stored alongside the ciphertext.
217
- def self.from_password(password, salt:, iterations: 600_000)
218
- key = Phylax.pbkdf2(password: password, salt: salt,
219
- iterations: iterations, length: KEY_BYTES, hash: :sha256)
220
- new(key)
221
- end
222
-
223
- # +key+ must be exactly 32 bytes (e.g. from .generate_key or .from_password).
224
- def initialize(key)
225
- key = String(key)
226
- unless key.bytesize == KEY_BYTES
227
- raise ArgumentError, "key must be exactly #{KEY_BYTES} bytes, got #{key.bytesize}"
228
- end
229
- _init(key)
230
- end
231
-
232
- # Encrypt and authenticate +plaintext+. Optional +aad+ (additional
233
- # authenticated data) is authenticated but not encrypted, and must be
234
- # supplied identically to #open. Returns nonce ‖ ciphertext ‖ tag.
235
- def seal(plaintext, aad: nil)
236
- _seal(plaintext, aad)
237
- end
238
-
239
- # Verify and decrypt a blob from #seal. Raises Phylax::AuthenticationError
240
- # if the ciphertext, tag, nonce, or aad don't match (or the blob is too
241
- # short to be authentic).
242
- def open(sealed, aad: nil)
243
- _open(sealed, aad)
244
- end
245
-
246
- def inspect
247
- "#<Phylax::SecretBox>" # never expose key material
248
- end
249
-
250
- private :_init, :_seal, :_open
251
- end
252
- end
1
+ # frozen_string_literal: true
2
+
3
+ raise LoadError, "phylax is discontinued and this release contains no code. Use Ruby's built-in openssl, lockbox or rbnacl instead." \
4
+ " Earlier phylax releases are unmaintained and not recommended."
metadata CHANGED
@@ -1,106 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phylax
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ned
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: rake
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - "~>"
17
- - !ruby/object:Gem::Version
18
- version: '13.0'
19
- type: :development
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - "~>"
24
- - !ruby/object:Gem::Version
25
- version: '13.0'
26
- - !ruby/object:Gem::Dependency
27
- name: rake-compiler
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '1.2'
33
- type: :development
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '1.2'
40
- - !ruby/object:Gem::Dependency
41
- name: minitest
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '5.0'
47
- type: :development
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '5.0'
54
- - !ruby/object:Gem::Dependency
55
- name: vcvars
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '0.1'
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- version: 0.1.1
64
- type: :development
65
- prerelease: false
66
- version_requirements: !ruby/object:Gem::Requirement
67
- requirements:
68
- - - "~>"
69
- - !ruby/object:Gem::Version
70
- version: '0.1'
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- version: 0.1.1
74
- description: |
75
- phylax (Greek "guardian") is a thin, safe-by-default binding to the Windows
76
- Cryptography API: Next Generation (CNG / bcrypt.dll) and DPAPI. It does not
77
- implement any cryptography of its own — it exposes the operating system's own
78
- validated primitives through an ergonomic, hard-to-misuse Ruby API: a
79
- cryptographically secure RNG, SHA-2 hashing and HMAC (one-shot and streaming),
80
- PBKDF2 key derivation, authenticated AES-256-GCM encryption (SecretBox, with
81
- nonces generated and framed automatically so reuse is impossible), a
82
- constant-time comparison, and DPAPI protect/unprotect for secrets at rest.
83
- Windows MSVC (mswin) Ruby only.
11
+ dependencies: []
12
+ description: phylax is discontinued. This final release contains no code and exists
13
+ only so that installing phylax explains the discontinuation instead of building
14
+ an unmaintained native extension.
84
15
  executables: []
85
- extensions:
86
- - ext/phylax/extconf.rb
16
+ extensions: []
87
17
  extra_rdoc_files: []
88
18
  files:
89
- - CHANGELOG.md
90
19
  - LICENSE.txt
91
20
  - README.md
92
- - ext/phylax/extconf.rb
93
- - ext/phylax/phylax.c
94
21
  - lib/phylax.rb
95
- - lib/phylax/version.rb
96
- homepage: https://github.com/main-path/phylax
97
22
  licenses:
98
23
  - MIT
99
24
  metadata:
100
- homepage_uri: https://github.com/main-path/phylax
101
- changelog_uri: https://github.com/main-path/phylax/blob/main/CHANGELOG.md
102
- bug_tracker_uri: https://github.com/main-path/phylax/issues
103
25
  rubygems_mfa_required: 'true'
26
+ post_install_message: phylax is discontinued and this release contains no code. Use
27
+ Ruby's built-in openssl, lockbox or rbnacl instead.
104
28
  rdoc_options: []
105
29
  require_paths:
106
30
  - lib
@@ -108,7 +32,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
32
  requirements:
109
33
  - - ">="
110
34
  - !ruby/object:Gem::Version
111
- version: '3.0'
35
+ version: '2.0'
112
36
  required_rubygems_version: !ruby/object:Gem::Requirement
113
37
  requirements:
114
38
  - - ">="
@@ -117,5 +41,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
41
  requirements: []
118
42
  rubygems_version: 3.6.9
119
43
  specification_version: 4
120
- summary: Misuse-resistant cryptography for Ruby, backed by the Windows CNG provider.
44
+ summary: Discontinued. Use Ruby's built-in openssl, lockbox or rbnacl instead.
121
45
  test_files: []
data/CHANGELOG.md DELETED
@@ -1,23 +0,0 @@
1
- # Changelog
2
-
3
- ## [0.1.0] - 2026-06-01
4
-
5
- Initial release.
6
-
7
- - `Phylax::SecretBox` — authenticated AES-256-GCM encryption with automatic,
8
- per-message random nonces framed as `nonce ‖ ciphertext ‖ tag`; nonce reuse is
9
- impossible through the public API. `seal`/`open` with optional AAD,
10
- `generate_key`, and `from_password` (PBKDF2-HMAC-SHA256).
11
- - `Phylax.sha256/sha384/sha512` and streaming `Phylax::Digest` (non-destructive
12
- `#digest` via `BCryptDuplicateHash`).
13
- - `Phylax.hmac_sha256/sha384/sha512`, streaming `Phylax::HMAC`, and the
14
- constant-time `Phylax::HMAC.verify`.
15
- - `Phylax.pbkdf2` (HMAC-SHA-2 PRF; releases the GVL during derivation).
16
- - `Phylax.random_bytes` (system-preferred CSPRNG).
17
- - `Phylax.secure_compare` (constant-time for equal-length inputs).
18
- - `Phylax.protect` / `Phylax.unprotect` — DPAPI secrets at rest, user or machine
19
- scope, optional entropy.
20
- - Error taxonomy: `Phylax::Error`, `Phylax::AuthenticationError`, `Phylax::OSError`.
21
-
22
- Built on the Windows CNG provider (`bcrypt.dll`) and DPAPI (`crypt32.dll`); no
23
- cryptography is implemented in the gem itself. Windows MSVC (mswin) Ruby only.
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # extconf.rb for the phylax extension — ergonomic Ruby bindings to the Windows
4
- # Cryptography API: Next Generation (CNG / bcrypt.dll) plus DPAPI (crypt32.dll).
5
-
6
- require "mkmf"
7
-
8
- unless RbConfig::CONFIG["target_os"] =~ /mswin/
9
- abort <<~MSG
10
- phylax requires a native Windows MSVC (mswin) Ruby — it binds the Windows
11
- CNG primitive provider (bcrypt.dll) and DPAPI (crypt32.dll) and is built
12
- with cl.exe. Your Ruby is "#{RbConfig::CONFIG['arch']}".
13
- MSG
14
- end
15
-
16
- # CNG primitives (BCrypt*) and DPAPI (CryptProtectData/CryptUnprotectData).
17
- # Pure C — no -EHsc, so rb_raise/longjmp is the normal, safe error mechanism.
18
- $libs = [$libs, "bcrypt.lib", "crypt32.lib"].join(" ")
19
-
20
- create_makefile("phylax/phylax")