wave-dispatch 0.6.1 → 0.6.2

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/wave_dispatch.rb +34 -6
  3. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70c5dbfaf54392986a7df2311a690f13c3b1b0ae20c43243e6a6a07bfc0f816b
4
- data.tar.gz: 8cb2064a6084c6053661c8106ae9d5bbe652c40ec7a6862fd3e774cf8e038497
3
+ metadata.gz: 4132ab0b5f81a3e912759da3d32a77629508687645eab9d475631a6f47a9e4b7
4
+ data.tar.gz: 66ae5e4b3895592957e4870ca7df3038ca2abe43020f7c9048380cb5466d31c9
5
5
  SHA512:
6
- metadata.gz: 68e200f7f520106e46eb633865b2b0128c3cf1e5ce3aff3408c3d433ab41e88875bfae880a02304b0b9e96315549193c43f4b7ef39a75943b35ead0fca82682e
7
- data.tar.gz: d9eb998d92b6e79a4b2a80414ee83fd2d39a48d71e544cce19acf6303aee88ab5e2f70e375e43cb7ab910dca268bf61741f1a2b887b217038f31ed65657336a6
6
+ metadata.gz: c03bdcc61a39530fd52182943c741d5d05aff21078d251961599e8ddf18393a8c4280f28028a2f9eb78ac1736bc961304a93f0b5d1cc3e533f282952144ba934
7
+ data.tar.gz: 0eea51f1031d5ace0517fef4da3caaebcdbe4f21a0e7a75e585a03e0b1e37b747866aa973265e584c99f82ab75283c387471cef586ecf58c8f0a2e2360f6ff2a
data/lib/wave_dispatch.rb CHANGED
@@ -7,7 +7,7 @@ require "uri"
7
7
  require "openssl"
8
8
 
9
9
  module WaveDispatch
10
- VERSION = "0.6.1"
10
+ VERSION = "0.6.2"
11
11
 
12
12
  # 0.5.1 — payment hook: a Proc called once with the 402 challenge body (Hash) that returns the
13
13
  # headers (Hash[String => String]) to retry the request with. Pair with `Client.wallet_hook(provider:,
@@ -94,11 +94,14 @@ module WaveDispatch
94
94
  accept = accepts.find { |a| a.is_a?(Hash) && a["protocol"] == provider.to_s } || accepts.first || {}
95
95
  case provider
96
96
  when :cdp
97
- # CDP-JWT signing is non-trivial in stdlib-only Ruby; the built-in returns a marker payload that
98
- # the worker accepts via the wave-payments adapter when WAVE_VERIFY_URL is set. For full on-chain
99
- # CDP signing, build your own Proc with the official Coinbase CDP gem.
100
- { provider: "cdp", address: creds[:address], accept: accept,
101
- hint: "use the coinbase-cdp gem for CDP-JWT signing in production" }.to_json
97
+ # 0.6.2 — real CDP-JWT (ES256/P-256) signing via OpenSSL stdlib. creds: {api_key, api_secret (PEM EC),
98
+ # address?, network?}. The signed JWT is the cdp-payment header value; WAVE's CDP verifier validates
99
+ # the signature on its side. No optional dep needed Ruby's stdlib OpenSSL handles everything.
100
+ raise "dispatch.wallet_hook(cdp): api_key required" unless creds[:api_key]
101
+ raise "dispatch.wallet_hook(cdp): api_secret required (PEM EC private key)" unless creds[:api_secret]
102
+ jwt = WaveDispatch.sign_cdp_jwt(creds, accept)
103
+ { provider: "cdp", jwt: jwt, address: creds[:address],
104
+ network: creds[:network] || "base", accept: accept }.to_json
102
105
  when :privy
103
106
  %i[app_id app_secret wallet_id].each { |k| raise "dispatch.wallet_hook(privy): #{k} required" unless creds[k] }
104
107
  basic = Base64.strict_encode64("#{creds[:app_id]}:#{creds[:app_secret]}")
@@ -127,4 +130,29 @@ module WaveDispatch
127
130
  raise "dispatch.wallet_sign: unsupported provider #{provider.inspect}"
128
131
  end
129
132
  end
133
+
134
+ # 0.6.2 — CDP-JWT (ES256/P-256) signing via OpenSSL stdlib. Header:
135
+ # {alg:'ES256', kid:<api_key>, typ:'JWT', nonce:<rand-hex16>}. Payload: {sub:<api_key>, iss:'cdp',
136
+ # nbf:<now>, exp:<now+120>, uri:'POST dispatch.wave.online<resource>', claim:<accept>}.
137
+ # ECDSA signs the base64url(header).base64url(payload) input; the DER signature is unpacked to raw
138
+ # 32+32 r||s (IEEE P-1363), which is exactly what JWS ES256 expects.
139
+ def self.sign_cdp_jwt(creds, accept)
140
+ require "openssl"
141
+ require "securerandom"
142
+ nonce = SecureRandom.hex(16)
143
+ now = Time.now.to_i
144
+ uri = "POST dispatch.wave.online#{(accept || {})["resource"] || "/"}"
145
+ header = { alg: "ES256", kid: creds[:api_key], typ: "JWT", nonce: nonce }
146
+ payload = { sub: creds[:api_key], iss: "cdp", nbf: now, exp: now + 120, uri: uri, claim: accept }
147
+ b64url = ->(s) { Base64.urlsafe_encode64(s).gsub("=", "") }
148
+ to_sign = b64url.call(header.to_json) + "." + b64url.call(payload.to_json)
149
+ key = OpenSSL::PKey.read(creds[:api_secret])
150
+ raise "dispatch.wallet_hook(cdp): api_secret must be an EC P-256 private key" unless key.is_a?(OpenSSL::PKey::EC)
151
+ der_sig = key.sign(OpenSSL::Digest.new("SHA256"), to_sign)
152
+ # DER → raw r||s. OpenSSL ASN1 parses the SEQUENCE into [r, s] big-integers.
153
+ asn1 = OpenSSL::ASN1.decode(der_sig)
154
+ r = asn1.value[0].value.to_s(2).rjust(32, "\x00".b)
155
+ s = asn1.value[1].value.to_s(2).rjust(32, "\x00".b)
156
+ to_sign + "." + b64url.call(r + s)
157
+ end
130
158
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wave-dispatch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - WAVE Online, LLC