Module: Spaceship::Hashcash

Defined in:
spaceship/lib/spaceship/hashcash.rb

Class Method Summary collapse

Class Method Details

.make(bits:, challenge:) ⇒ Object

This App Store Connect hashcash spec was generously donated by…

                       __  _
  __ _  _ __   _ __   / _|(_)  __ _  _   _  _ __  ___  ___
 / _` || '_ \ | '_ \ | |_ | | / _` || | | || '__|/ _ \/ __|
| (_| || |_) || |_) ||  _|| || (_| || |_| || |  |  __/\__ \
 \__,_|| .__/ | .__/ |_|  |_| \__, | \__,_||_|   \___||___/
       |_|    |_|             |___/

<summary>

1:11:20230223170600:4d74fb15eb23f465f1f6fcbf534e5877::6373

X-APPLE-HC: 1:11:20230223170600:4d74fb15eb23f465f1f6fcbf534e5877::6373

^  ^      ^                       ^                     ^
|  |      |                       |                     +-- Counter
|  |      |                       +-- Resource
|  |      +-- Date YYMMDD[hhmm[ss]]
|  +-- Bits (number of leading zeros)
+-- Version

We can’t use an off-the-shelf Hashcash because Apple’s implementation is not quite the same as the spec/convention.

1. The spec calls for a nonce called "Rand" to be inserted between the Ext and Counter. They don't do that at all.
2. The Counter conventionally encoded as base-64 but Apple just uses the decimal number's string representation.

Iterate from Counter=0 to Counter=N finding an N that makes the SHA1(X-APPLE-HC) lead with Bits leading zero bits

We get the “Resource” from the X-Apple-HC-Challenge header and Bits from X-Apple-HC-Bits

</summary>



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'spaceship/lib/spaceship/hashcash.rb', line 35

def self.make(bits:, challenge:)
  version = 1
  date = Time.now.strftime("%Y%m%d%H%M%S")

  counter = 0
  loop do
    hc = [
      version, bits, date, challenge, ":#{counter}"
    ].join(":")

    if Digest::SHA1.digest(hc).unpack1('B*')[0, bits.to_i].to_i == 0
      return hc
    end
    counter += 1
  end
end