pq_crypto-jwt 0.2.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +3 -1
- data/lib/pq_crypto/jwt/jwa/ml_dsa_streaming.rb +37 -2
- data/lib/pq_crypto/jwt/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 32f1a2663779d332f693d8e7fe38cfdb730ad67aba2303c178a116d75a0656a6
|
|
4
|
+
data.tar.gz: 13b3a76f35d0175700678dd15f6b030ec284c56092a47425a7a1dc5ef5a7e3e5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 53333891ef2330c3e871916102ec6d08f8d7753c8b79f405b26f1f142562f5d6a91cb319aabb62499924b7bcd488a37a21f7a30390a250c356dd12ce83d14cb9
|
|
7
|
+
data.tar.gz: 16679d31fb72e7ea1d0563e52cbb12ada1f31ed3e8e3554ead19a81a0f3f62a9336c03ba0b43370fba7280706f7bca7d1bddc3038b739959c4a114cc4f755513
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Streaming detached JWS signing now rejects protected `b64` and `crit` headers instead of allowing semantic mismatch with the base64url-encoded signing input.
|
|
8
|
+
- Streaming detached JWS verification now fails closed for critical headers and `b64: false`.
|
|
9
|
+
- Streaming `chunk_size` is now validated as a positive integer before signing, verification, or detached signing-input reads.
|
|
10
|
+
- CI workflow now matches the documented release matrix for Ruby `3.1`, `3.2`, `3.3`, `3.4`, `4.0` crossed with `jwt ~> 3.1.0` and `jwt ~> 3.2.0`.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Negative streaming tests for `crit`, `b64`, malformed base64url headers, and non-positive chunk sizes.
|
|
15
|
+
- RFC 9964 regression tests for ML-DSA public key/signature sizes and AKP JWK thumbprint canonical members.
|
|
16
|
+
- Explicit tests and documentation for trusted private AKP JWK import without `verify_public: true`.
|
|
17
|
+
|
|
3
18
|
## 0.2.0
|
|
4
19
|
|
|
5
20
|
### Changed
|
data/README.md
CHANGED
|
@@ -113,7 +113,7 @@ Private AKP JWK uses RFC 9964 seed format: `priv` is the 32 raw-byte ML-DSA seed
|
|
|
113
113
|
secret_key = PQCrypto::JWT::JWK.secret_key_from_jwk(private_jwk)
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
With `pq_crypto 0.6.1`, private import trusts the JWK `pub` field as metadata because the parent gem does not expose public seed derivation yet. Callers that need a strict `pub`/`priv` consistency check can request it explicitly; this will raise `UnsupportedFeature` until the parent exposes `Signature.public_key_from_seed` or `Signature.keypair_from_seed`:
|
|
116
|
+
With `pq_crypto 0.6.1`, private import trusts the JWK `pub` field as metadata because the parent gem does not expose public seed derivation yet. `secret_key_from_jwk` without `verify_public: true` is suitable only for trusted private JWK material. It does not prove that `pub` belongs to `priv`. Callers that need a strict `pub`/`priv` consistency check can request it explicitly; this will raise `UnsupportedFeature` until the parent exposes `Signature.public_key_from_seed` or `Signature.keypair_from_seed`:
|
|
117
117
|
|
|
118
118
|
```ruby
|
|
119
119
|
secret_key = PQCrypto::JWT::JWK.secret_key_from_jwk(private_jwk, verify_public: true)
|
|
@@ -173,6 +173,8 @@ PQCrypto::JWT::JWA::MLDSA87
|
|
|
173
173
|
|
|
174
174
|
`verify_io` returns `[payload_position, header]` on success. `payload_position` is `nil` for non-seekable streams that do not respond to `#pos`.
|
|
175
175
|
|
|
176
|
+
The streaming helper signs the regular compact JWS signing input with a base64url-encoded payload. It does not implement RFC 7797 unencoded payload mode. `sign_io` rejects protected `b64` and `crit` header fields, and `verify_io` fails closed for critical headers or `b64: false`. `chunk_size` must be a positive integer.
|
|
177
|
+
|
|
176
178
|
## Non-goals
|
|
177
179
|
|
|
178
180
|
This adapter deliberately does **not** expose:
|
|
@@ -19,10 +19,12 @@ module PQCrypto
|
|
|
19
19
|
def sign_io(signing_key:, payload_io: nil, io: nil, header_fields: {}, chunk_size: DEFAULT_CHUNK_SIZE)
|
|
20
20
|
ensure_streaming!
|
|
21
21
|
ensure_key!(signing_key, PQCrypto::Signature::SecretKey, "signing")
|
|
22
|
+
validate_chunk_size!(chunk_size)
|
|
22
23
|
source = payload_io || io
|
|
23
24
|
raise ArgumentError, "payload_io must respond to #read" unless source.respond_to?(:read)
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
header = normalize_signing_header!(header_fields).merge("alg" => alg)
|
|
27
|
+
encoded_header = base64url(JSON.generate(header))
|
|
26
28
|
input = DetachedSigningInputIO.new(encoded_header, source, chunk_size: chunk_size)
|
|
27
29
|
signature = signing_key.sign_io(input, chunk_size: chunk_size, context: EMPTY_CONTEXT)
|
|
28
30
|
"#{encoded_header}..#{base64url(signature)}"
|
|
@@ -35,6 +37,7 @@ module PQCrypto
|
|
|
35
37
|
def verify_io(verification_key:, token:, payload_io:, chunk_size: DEFAULT_CHUNK_SIZE)
|
|
36
38
|
ensure_streaming!
|
|
37
39
|
ensure_key!(verification_key, PQCrypto::Signature::PublicKey, "verification")
|
|
40
|
+
validate_chunk_size!(chunk_size)
|
|
38
41
|
raise ArgumentError, "token must be a String" unless token.is_a?(String)
|
|
39
42
|
raise ArgumentError, "payload_io must respond to #read" unless payload_io.respond_to?(:read)
|
|
40
43
|
|
|
@@ -42,7 +45,7 @@ module PQCrypto
|
|
|
42
45
|
return false unless extra.nil? && encoded_payload == "" && encoded_signature
|
|
43
46
|
|
|
44
47
|
header = JSON.parse(Base64.urlsafe_decode64(encoded_header))
|
|
45
|
-
return false unless
|
|
48
|
+
return false unless supported_streaming_header?(header)
|
|
46
49
|
|
|
47
50
|
signature = Base64.urlsafe_decode64(encoded_signature)
|
|
48
51
|
return false unless signature_length_valid?(signature)
|
|
@@ -71,10 +74,42 @@ module PQCrypto
|
|
|
71
74
|
def base64url(bytes)
|
|
72
75
|
Base64.urlsafe_encode64(String(bytes).b, padding: false)
|
|
73
76
|
end
|
|
77
|
+
|
|
78
|
+
def normalize_signing_header!(header_fields)
|
|
79
|
+
unless header_fields.respond_to?(:to_hash)
|
|
80
|
+
raise ArgumentError, "header_fields must be a Hash-like object"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
header = header_fields.to_hash.each_with_object({}) { |(key, value), out| out[String(key)] = value }
|
|
84
|
+
unsupported = %w[b64 crit] & header.keys
|
|
85
|
+
unless unsupported.empty?
|
|
86
|
+
raise ArgumentError, "unsupported protected header#{unsupported.size == 1 ? '' : 's'}: #{unsupported.join(', ')}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
header
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def supported_streaming_header?(header)
|
|
93
|
+
return false unless header.is_a?(Hash) && header["alg"] == alg
|
|
94
|
+
return false if header.key?("crit")
|
|
95
|
+
return false if header.key?("b64") && header["b64"] != true
|
|
96
|
+
|
|
97
|
+
true
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def validate_chunk_size!(chunk_size)
|
|
101
|
+
return if chunk_size.is_a?(Integer) && chunk_size.positive?
|
|
102
|
+
|
|
103
|
+
raise ArgumentError, "chunk_size must be a positive Integer"
|
|
104
|
+
end
|
|
74
105
|
end
|
|
75
106
|
|
|
76
107
|
class DetachedSigningInputIO
|
|
77
108
|
def initialize(encoded_header, payload_io, chunk_size: MLDSAStreaming::DEFAULT_CHUNK_SIZE)
|
|
109
|
+
unless chunk_size.is_a?(Integer) && chunk_size.positive?
|
|
110
|
+
raise ArgumentError, "chunk_size must be a positive Integer"
|
|
111
|
+
end
|
|
112
|
+
|
|
78
113
|
@prefix = "#{encoded_header}.".b
|
|
79
114
|
@payload_io = payload_io
|
|
80
115
|
@chunk_size = chunk_size
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pq_crypto-jwt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Haydarov
|
|
@@ -100,7 +100,7 @@ licenses:
|
|
|
100
100
|
- MIT
|
|
101
101
|
metadata:
|
|
102
102
|
source_code_uri: https://github.com/roman-haidarov/pq_crypto-jwt
|
|
103
|
-
changelog_uri: https://github.com/roman-haidarov/pq_crypto-jwt/blob/v0.2.
|
|
103
|
+
changelog_uri: https://github.com/roman-haidarov/pq_crypto-jwt/blob/v0.2.1/CHANGELOG.md
|
|
104
104
|
bug_tracker_uri: https://github.com/roman-haidarov/pq_crypto-jwt/issues
|
|
105
105
|
post_install_message:
|
|
106
106
|
rdoc_options: []
|