philiprehberger-crypt 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f934ae2dfc3f4f4dc390661a3d9d70dc78d021d4addf51bc27aa8ff8c5701f85
4
- data.tar.gz: 2fb862fd3ecdab2aaed5d2a24a76f229a07d3e471f61604894445388d42a3b1c
3
+ metadata.gz: 9b8b7e1b7ba025acb109315c7917651f67b9645ef6abc1f7699dbe4e471020b2
4
+ data.tar.gz: 988b00641592540f69b355896dd6d524f7369830f93a33ad450909e4260671a2
5
5
  SHA512:
6
- metadata.gz: dff085e8d30a2a6bd928291f80249a98fa075f3481866b8b7b1cd83898bac6a4671fc1903f9cd9496cf183ab2fca7a4559da5b7faea3439531470caf58fe819f
7
- data.tar.gz: e15ccc8344e3324526e0d9feace9f2d780d37f7141be6d851a597ed9ef29dbdb52245883adf27cd4f78f3fec959f72a9661caff4acd3525796d1430ec13e8ef4
6
+ metadata.gz: 85775a37db94d000bd73b20ed1d403e6dc5b891e38600860aea6ce0087c1c45d680886b3efc5212258b4f9132a691e7d52a92d1b2e824c935a698737f3590591
7
+ data.tar.gz: 799433a04830123ffe629edaf5460dcad49f477e48108c66c7cbb1bbfb7c7611e78c9cf77f97cd7211de060b036d93eec7119e49cd7b1626f9af8a7aff47864e
data/CHANGELOG.md CHANGED
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.0] - 2026-04-09
11
+
12
+ ### Added
13
+ - `hmac(data, key:, algorithm:)` for computing HMAC signatures (SHA-256/384/512)
14
+ - `hmac_verify(data, signature:, key:, algorithm:)` for constant-time HMAC verification
15
+ - `derive_key` now accepts an `iterations:` keyword to configure PBKDF2 work factor
16
+
10
17
  ## [0.2.0] - 2026-04-03
11
18
 
12
19
  ### Added
data/README.md CHANGED
@@ -96,6 +96,16 @@ Philiprehberger::Crypt.random_bytes(32)
96
96
  # => 32-byte binary string
97
97
  ```
98
98
 
99
+ ### HMAC Signing
100
+
101
+ ```ruby
102
+ key = Philiprehberger::Crypt.random_hex(16)
103
+ signature = Philiprehberger::Crypt.hmac("payload", key: key)
104
+
105
+ Philiprehberger::Crypt.hmac_verify("payload", signature: signature, key: key)
106
+ # => true
107
+ ```
108
+
99
109
  ### Secure Comparison
100
110
 
101
111
  ```ruby
@@ -112,7 +122,9 @@ Philiprehberger::Crypt.secure_compare(token_a, token_b)
112
122
  | `.rotate_key(encrypted, old_key:, new_key:)` | Re-encrypt data with a new key |
113
123
  | `.envelope_encrypt(data, master_key:)` | Envelope encrypt with random data key |
114
124
  | `.envelope_decrypt(envelope, master_key:)` | Decrypt envelope-encrypted data |
115
- | `.derive_key(password, salt:)` | Derive a 32-byte key using PBKDF2-HMAC-SHA256 |
125
+ | `.derive_key(password, salt:, iterations:)` | Derive a 32-byte key using PBKDF2-HMAC-SHA256 |
126
+ | `.hmac(data, key:, algorithm:)` | Compute hex-encoded HMAC signature |
127
+ | `.hmac_verify(data, signature:, key:, algorithm:)` | Constant-time HMAC verification |
116
128
  | `.random_salt` | Generate a 32-byte cryptographic random salt |
117
129
  | `.random_token` | Generate a URL-safe Base64 random token |
118
130
  | `.random_hex(n)` | Generate a hex-encoded random string (2*n characters) |
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module Crypt
5
- VERSION = '0.2.0'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
@@ -71,17 +71,47 @@ module Philiprehberger
71
71
  #
72
72
  # @param password [String] the password to derive from
73
73
  # @param salt [String] a random salt (use {.random_salt} to generate)
74
+ # @param iterations [Integer] number of PBKDF2 iterations (default: 100_000)
74
75
  # @return [String] a 32-byte raw key suitable for {.encrypt}/{.decrypt}
75
- def self.derive_key(password, salt:)
76
+ # @raise [ArgumentError] if iterations is less than 1
77
+ def self.derive_key(password, salt:, iterations: PBKDF2_ITERATIONS)
78
+ raise ArgumentError, 'iterations must be >= 1' if iterations.to_i < 1
79
+
76
80
  OpenSSL::PKCS5.pbkdf2_hmac(
77
81
  password.to_s,
78
82
  salt,
79
- PBKDF2_ITERATIONS,
83
+ iterations.to_i,
80
84
  KEY_LENGTH,
81
85
  OpenSSL::Digest.new('SHA256')
82
86
  )
83
87
  end
84
88
 
89
+ # Compute an HMAC signature for data using the given key.
90
+ #
91
+ # @param data [String] the data to sign
92
+ # @param key [String] the HMAC secret key
93
+ # @param algorithm [Symbol] hash algorithm (:sha256, :sha384, or :sha512)
94
+ # @return [String] hex-encoded HMAC digest
95
+ # @raise [ArgumentError] if algorithm is unsupported
96
+ def self.hmac(data, key:, algorithm: :sha256)
97
+ algo = HASH_ALGORITHMS[algorithm]
98
+ raise ArgumentError, "Unsupported algorithm: #{algorithm}. Use :sha256, :sha384, or :sha512" unless algo
99
+
100
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(algo), key.to_s, data.to_s)
101
+ end
102
+
103
+ # Verify an HMAC signature in constant time.
104
+ #
105
+ # @param data [String] the original data
106
+ # @param signature [String] the expected hex-encoded HMAC signature
107
+ # @param key [String] the HMAC secret key
108
+ # @param algorithm [Symbol] hash algorithm used to sign
109
+ # @return [Boolean] true if the signature matches
110
+ def self.hmac_verify(data, signature:, key:, algorithm: :sha256)
111
+ expected = hmac(data, key: key, algorithm: algorithm)
112
+ secure_compare(expected, signature.to_s)
113
+ end
114
+
85
115
  # Generate a cryptographically secure random salt.
86
116
  #
87
117
  # @return [String] a 32-byte random salt (raw bytes)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-crypt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-04 00:00:00.000000000 Z
11
+ date: 2026-04-09 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A high-level encryption toolkit providing AES-256-GCM encryption and
14
14
  decryption, key rotation, envelope encryption, PBKDF2 key derivation, secure random