Skip to content

Commit 190d513

Browse files
committed
chore(rsa_pss_spec): only run on OpenSSL 3+
1 parent b20ad29 commit 190d513

File tree

2 files changed

+194
-190
lines changed

2 files changed

+194
-190
lines changed
Lines changed: 87 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,105 @@
11
require "../../spec_helper"
22

3-
# This spec verifies compatibility with tokens from the l8w8jwt library
4-
# Reference: https://github.com/GlitchedPolygons/l8w8jwt/blob/master/examples/ps256/decode.c
5-
describe "PS256 decode compatibility" do
6-
# Token from l8w8jwt example
7-
jwt_token = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbWUta2V5LWlkLWhlcmUtMDEyMzQ1In0.eyJpYXQiOjE1ODAzNDAwMzcsImV4cCI6MTU4MDM0MDYzNywic3ViIjoiR29yZG9uIEZyZWVtYW4iLCJpc3MiOiJCbGFjayBNZXNhIiwiYXVkIjoiQWRtaW5pc3RyYXRvciIsImN0eCI6IlVuZm9yc2VlbiBDb25zZXF1ZW5jZXMiLCJhZ2UiOjI3LCJzaXplIjoxLjg1LCJhbGl2ZSI6dHJ1ZSwibnVsbHRlc3QiOm51bGx9.X4o81UkLLt1mBdoQozWPAtVIRvkX7249fs25FGqrlGzci2exAVQh6g8OzqZlhPO8_VSVGt1bTlurWPhrPwZoeViy1g86MRBLNoiuWEkPg0FFB2jhBPGF2u-cJ2YKd9VSLSjs1fcxSyfG5dKczDo_w3FUL_syNpOpWaWtvByxDn0Cez4SHfTIcaGPKsyYBKhy1t3RgFzm9mCMugRd40omPO4WFKQ1f-boO0ydfvcybEmxMBpT3DsqbKAD9oM0kFWsLMIzOXIp4Uo1J-k3utjieDwaiBu7x2g-bU_0XygnXWIfrSXtUOmntVVFe9am13fIeH-I_3SJlzhLI4QapJ-_s5xeyZ3Y8tHLs-Sqt85Bs_rnewnJpHESXn-G5eK7YTHEvC3luELNrGQlTzQIpTZLYwARikQlhBme-lqvH6hTdGwQy-jhlr41GF5hBKHArFTN0RJBRDKyGgJffDlDDsk3g9NpaZqvOqMvLBHk78TbrQnTKMKY6L7dnAoPcTcl8IgIr9lN37TKFuvAm6nDjcWQUViOO9YtDng3e8cjWaJiizGpTOct-IKn7ZXMzGRrFSmXSOWgeukP5jcwH5dU_0ICDbt2oaid7Bpm1z8EviBGNh0OmjqJ8FmsGst8zaAufpSBwCbV9OCUo84RminY6pW6Lm3BWwIbki-yUOExAWJPjN0"
8-
9-
# RSA 4096-bit public key from l8w8jwt example
10-
rsa_public_key = "-----BEGIN PUBLIC KEY-----\n" +
11-
"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoWFe7BbX1nWo5oaSv/Jv\n" +
12-
"IUCWsk/Vi2q8P0cGkefgN5J7MN7Kfv7lq0hl/1cZcJs81IC+GiC+V3aR2zLBNnJJ\n" +
13-
"axa4sqk+hF5DJcD2bF0B80uqPYQUXlQwki/heATnVcke8APuY0kOZykxoD0APAqw\n" +
14-
"0z5KDqgt2vA9G6keM6b9bbL+IvxM+yMk1QV0OQLh6Rkz46DyPSoUFWyXiist47PJ\n" +
15-
"KNyZAfFZx6vEivzBmqRHKe11W9oD/tN5VTQCH/UTSRfyWq/UUMFVMCksLwT6XoWI\n" +
16-
"7F5swgQkSahWkVJ93Qf8cUf1HIZYTMJBYPG4y2NDZ0+ytnH3BNXLMQXg9xbgv6B/\n" +
17-
"iaSVScI4CWIpQTAtNKnJwYg2+RhfYBC07iM56c4a+TjbCWgmd11UYc96dbw83uFR\n" +
18-
"jKZc3+SC38ITCgMuoDPNBlFJK6u8VfYylGEJolGcauVa6yZKwzsJGr5J/LANz+Zy\n" +
19-
"HZmANed+2Hjqxu/H1NGDBdvUGLQbhb/uBJ8oG8iAW5eUyjEJMX0RuncYnBrUjZdE\n" +
20-
"Fr0zJd5VkrfFTd26AjGusbiBevATfj83SNa9uK3N3lSNcLNyNXUjmfOU21NWHAk5\n" +
21-
"QV3TJb6SCTcqWFaYoyKR7H6zxRcArNuIAMW4KhOl4jdNnTxJllC4tr/gkE+uO1nt\n" +
22-
"B9ymLxQBRp8osHjuZpKXr3cCAwEAAQ==\n" +
23-
"-----END PUBLIC KEY-----"
24-
25-
it "can decode a PS256 token from l8w8jwt library" do
26-
# Decode without verification or validation (since token is expired)
27-
payload, header = JWT.decode(jwt_token, verify: false, validate: false)
28-
29-
# Verify header
30-
header["alg"].should eq("PS256")
31-
header["typ"].should eq("JWT")
32-
header["kid"].should eq("some-key-id-here-012345")
33-
34-
# Verify payload claims
35-
payload["iat"].should eq(1580340037)
36-
payload["exp"].should eq(1580340637)
37-
payload["sub"].should eq("Gordon Freeman")
38-
payload["iss"].should eq("Black Mesa")
39-
payload["aud"].should eq("Administrator")
40-
payload["ctx"].should eq("Unforseen Consequences")
41-
payload["age"].should eq(27)
42-
payload["size"].should eq(1.85)
43-
payload["alive"].should eq(true)
44-
payload["nulltest"].as_nil.should be_nil
45-
end
3+
{% if compare_versions(LibCrypto::OPENSSL_VERSION, "3.0.0") >= 0 %}
4+
# This spec verifies compatibility with tokens from the l8w8jwt library
5+
# Reference: https://github.com/GlitchedPolygons/l8w8jwt/blob/master/examples/ps256/decode.c
6+
describe "PS256 decode compatibility" do
7+
# Token from l8w8jwt example
8+
jwt_token = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbWUta2V5LWlkLWhlcmUtMDEyMzQ1In0.eyJpYXQiOjE1ODAzNDAwMzcsImV4cCI6MTU4MDM0MDYzNywic3ViIjoiR29yZG9uIEZyZWVtYW4iLCJpc3MiOiJCbGFjayBNZXNhIiwiYXVkIjoiQWRtaW5pc3RyYXRvciIsImN0eCI6IlVuZm9yc2VlbiBDb25zZXF1ZW5jZXMiLCJhZ2UiOjI3LCJzaXplIjoxLjg1LCJhbGl2ZSI6dHJ1ZSwibnVsbHRlc3QiOm51bGx9.X4o81UkLLt1mBdoQozWPAtVIRvkX7249fs25FGqrlGzci2exAVQh6g8OzqZlhPO8_VSVGt1bTlurWPhrPwZoeViy1g86MRBLNoiuWEkPg0FFB2jhBPGF2u-cJ2YKd9VSLSjs1fcxSyfG5dKczDo_w3FUL_syNpOpWaWtvByxDn0Cez4SHfTIcaGPKsyYBKhy1t3RgFzm9mCMugRd40omPO4WFKQ1f-boO0ydfvcybEmxMBpT3DsqbKAD9oM0kFWsLMIzOXIp4Uo1J-k3utjieDwaiBu7x2g-bU_0XygnXWIfrSXtUOmntVVFe9am13fIeH-I_3SJlzhLI4QapJ-_s5xeyZ3Y8tHLs-Sqt85Bs_rnewnJpHESXn-G5eK7YTHEvC3luELNrGQlTzQIpTZLYwARikQlhBme-lqvH6hTdGwQy-jhlr41GF5hBKHArFTN0RJBRDKyGgJffDlDDsk3g9NpaZqvOqMvLBHk78TbrQnTKMKY6L7dnAoPcTcl8IgIr9lN37TKFuvAm6nDjcWQUViOO9YtDng3e8cjWaJiizGpTOct-IKn7ZXMzGRrFSmXSOWgeukP5jcwH5dU_0ICDbt2oaid7Bpm1z8EviBGNh0OmjqJ8FmsGst8zaAufpSBwCbV9OCUo84RminY6pW6Lm3BWwIbki-yUOExAWJPjN0"
9+
10+
# RSA 4096-bit public key from l8w8jwt example
11+
rsa_public_key = "-----BEGIN PUBLIC KEY-----\n" +
12+
"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoWFe7BbX1nWo5oaSv/Jv\n" +
13+
"IUCWsk/Vi2q8P0cGkefgN5J7MN7Kfv7lq0hl/1cZcJs81IC+GiC+V3aR2zLBNnJJ\n" +
14+
"axa4sqk+hF5DJcD2bF0B80uqPYQUXlQwki/heATnVcke8APuY0kOZykxoD0APAqw\n" +
15+
"0z5KDqgt2vA9G6keM6b9bbL+IvxM+yMk1QV0OQLh6Rkz46DyPSoUFWyXiist47PJ\n" +
16+
"KNyZAfFZx6vEivzBmqRHKe11W9oD/tN5VTQCH/UTSRfyWq/UUMFVMCksLwT6XoWI\n" +
17+
"7F5swgQkSahWkVJ93Qf8cUf1HIZYTMJBYPG4y2NDZ0+ytnH3BNXLMQXg9xbgv6B/\n" +
18+
"iaSVScI4CWIpQTAtNKnJwYg2+RhfYBC07iM56c4a+TjbCWgmd11UYc96dbw83uFR\n" +
19+
"jKZc3+SC38ITCgMuoDPNBlFJK6u8VfYylGEJolGcauVa6yZKwzsJGr5J/LANz+Zy\n" +
20+
"HZmANed+2Hjqxu/H1NGDBdvUGLQbhb/uBJ8oG8iAW5eUyjEJMX0RuncYnBrUjZdE\n" +
21+
"Fr0zJd5VkrfFTd26AjGusbiBevATfj83SNa9uK3N3lSNcLNyNXUjmfOU21NWHAk5\n" +
22+
"QV3TJb6SCTcqWFaYoyKR7H6zxRcArNuIAMW4KhOl4jdNnTxJllC4tr/gkE+uO1nt\n" +
23+
"B9ymLxQBRp8osHjuZpKXr3cCAwEAAQ==\n" +
24+
"-----END PUBLIC KEY-----"
25+
26+
it "can decode a PS256 token from l8w8jwt library" do
27+
# Decode without verification or validation (since token is expired)
28+
payload, header = JWT.decode(jwt_token, verify: false, validate: false)
29+
30+
# Verify header
31+
header["alg"].should eq("PS256")
32+
header["typ"].should eq("JWT")
33+
header["kid"].should eq("some-key-id-here-012345")
34+
35+
# Verify payload claims
36+
payload["iat"].should eq(1580340037)
37+
payload["exp"].should eq(1580340637)
38+
payload["sub"].should eq("Gordon Freeman")
39+
payload["iss"].should eq("Black Mesa")
40+
payload["aud"].should eq("Administrator")
41+
payload["ctx"].should eq("Unforseen Consequences")
42+
payload["age"].should eq(27)
43+
payload["size"].should eq(1.85)
44+
payload["alive"].should eq(true)
45+
payload["nulltest"].as_nil.should be_nil
46+
end
4647

47-
it "can verify the PS256 signature with the public key" do
48-
# Verify signature (verify: true checks signature, validate: false skips expiry check)
49-
payload, _header = JWT.decode(jwt_token, rsa_public_key, JWT::Algorithm::PS256, verify: true, validate: false)
48+
it "can verify the PS256 signature with the public key" do
49+
# Verify signature (verify: true checks signature, validate: false skips expiry check)
50+
payload, _header = JWT.decode(jwt_token, rsa_public_key, JWT::Algorithm::PS256, verify: true, validate: false)
5051

51-
# Signature is valid, verify payload
52-
payload["sub"].should eq("Gordon Freeman")
53-
payload["iss"].should eq("Black Mesa")
54-
payload["aud"].should eq("Administrator")
55-
payload["ctx"].should eq("Unforseen Consequences")
56-
end
52+
# Signature is valid, verify payload
53+
payload["sub"].should eq("Gordon Freeman")
54+
payload["iss"].should eq("Black Mesa")
55+
payload["aud"].should eq("Administrator")
56+
payload["ctx"].should eq("Unforseen Consequences")
57+
end
5758

58-
it "distinguishes between signature verification and claim validation" do
59-
# verify: true, validate: false -> checks signature but not expiry
60-
payload, _ = JWT.decode(jwt_token, rsa_public_key, JWT::Algorithm::PS256, verify: true, validate: false)
61-
payload["sub"].should eq("Gordon Freeman")
59+
it "distinguishes between signature verification and claim validation" do
60+
# verify: true, validate: false -> checks signature but not expiry
61+
payload, _ = JWT.decode(jwt_token, rsa_public_key, JWT::Algorithm::PS256, verify: true, validate: false)
62+
payload["sub"].should eq("Gordon Freeman")
6263

63-
# verify: false, validate: false -> no checks at all
64-
payload, _ = JWT.decode(jwt_token, verify: false, validate: false)
65-
payload["sub"].should eq("Gordon Freeman")
64+
# verify: false, validate: false -> no checks at all
65+
payload, _ = JWT.decode(jwt_token, verify: false, validate: false)
66+
payload["sub"].should eq("Gordon Freeman")
6667

67-
# verify: true, validate: true -> checks signature AND expiry (will fail)
68-
expect_raises(JWT::ExpiredSignatureError) do
69-
JWT.decode(jwt_token, rsa_public_key, JWT::Algorithm::PS256, verify: true, validate: true)
68+
# verify: true, validate: true -> checks signature AND expiry (will fail)
69+
expect_raises(JWT::ExpiredSignatureError) do
70+
JWT.decode(jwt_token, rsa_public_key, JWT::Algorithm::PS256, verify: true, validate: true)
71+
end
7072
end
71-
end
7273

73-
# Note: Validation tests (iss, sub, aud) are not included here because
74-
# the token is expired and validation checks exp first. The existing
75-
# claim validation tests in other specs cover those features.
74+
# Note: Validation tests (iss, sub, aud) are not included here because
75+
# the token is expired and validation checks exp first. The existing
76+
# claim validation tests in other specs cover those features.
7677

77-
it "properly decodes all data types in payload" do
78-
payload, _ = JWT.decode(jwt_token, verify: false, validate: false)
78+
it "properly decodes all data types in payload" do
79+
payload, _ = JWT.decode(jwt_token, verify: false, validate: false)
7980

80-
# Integer
81-
payload["age"].as_i.should eq(27)
81+
# Integer
82+
payload["age"].as_i.should eq(27)
8283

83-
# Float
84-
payload["size"].as_f.should eq(1.85)
84+
# Float
85+
payload["size"].as_f.should eq(1.85)
8586

86-
# Boolean
87-
payload["alive"].as_bool.should eq(true)
87+
# Boolean
88+
payload["alive"].as_bool.should eq(true)
8889

89-
# Null (JSON null is represented as JSON::Any, use .as_nil to extract)
90-
payload["nulltest"].as_nil.should be_nil
90+
# Null (JSON null is represented as JSON::Any, use .as_nil to extract)
91+
payload["nulltest"].as_nil.should be_nil
9192

92-
# Strings
93-
payload["sub"].as_s.should eq("Gordon Freeman")
94-
payload["ctx"].as_s.should eq("Unforseen Consequences")
95-
end
93+
# Strings
94+
payload["sub"].as_s.should eq("Gordon Freeman")
95+
payload["ctx"].as_s.should eq("Unforseen Consequences")
96+
end
9697

97-
it "token is expired and should fail exp validation" do
98-
# Token exp is 1580340637 (2020-01-29 23:30:37 UTC), which has passed
99-
expect_raises(JWT::ExpiredSignatureError, "Signature is expired") do
100-
JWT.decode(jwt_token, rsa_public_key, JWT::Algorithm::PS256)
98+
it "token is expired and should fail exp validation" do
99+
# Token exp is 1580340637 (2020-01-29 23:30:37 UTC), which has passed
100+
expect_raises(JWT::ExpiredSignatureError, "Signature is expired") do
101+
JWT.decode(jwt_token, rsa_public_key, JWT::Algorithm::PS256)
102+
end
101103
end
102104
end
103-
end
105+
{% end %}

0 commit comments

Comments
 (0)