active_hashcash 0.1.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 +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +85 -0
- data/lib/active_hashcash.rb +150 -0
- data/lib/hashcash.js +257 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 47a914f25827b270ed0e1bb2f0fc222316846b48232cf5dceb255c99725f1467
|
4
|
+
data.tar.gz: f5aaf27f5d578cfc271624c4ddd3746365e81424247cd50ed43744cb534ea893
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9799788e77f0f6d8415d0e69a153a8b88715e34d5742794ed64a0db3a30a41bd619bc54d9c840b1d4d37eff4f0f9cf90ec9bc4c552656aad8f086068997685e0
|
7
|
+
data.tar.gz: f439c8fb6265ae1a2999b6fe3995871762e09b8ab05b38a776dc4e0eafd4fe67d431396323425118f55d596c2f2270ff5160733ccc94fe7f1d47df0eba38ce9f
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 Alexis Bernard
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# ActiveHashcash
|
2
|
+
|
3
|
+
<img align="right" width="200px" src="logo.png" alt="Active Hashcash logo"/>
|
4
|
+
|
5
|
+
ActiveHashcash protects your Rails application against DoS and bots.
|
6
|
+
|
7
|
+
Hashcash is proof-of-work algorithm, invented by Adam Back in 1997, to protect systems against denial of service attacks.
|
8
|
+
ActiveHashcash is an easy way to protect any Rails application against brute force attacks and some bots.
|
9
|
+
|
10
|
+
The idea is to force clients to spend some time to solve a hard problem that is very easy to verify for the server.
|
11
|
+
We have developped ActiveHashcash after seeing brute force attacks against our Rails application monitoring service [RorVsWild](https://rorvswild.com).
|
12
|
+
|
13
|
+
The idea is to enable ActiveHashcash on sensitive forms such as login and registration. While the user is filling the form,
|
14
|
+
ActiveHashcash performs the work in JavaScript and set the result into a hidden input text. The form cannot be submitted while the proof of work has not been found.
|
15
|
+
The user submits the form, and the stamp is verified by the controller in a before action.
|
16
|
+
|
17
|
+
It blocks bots that do not interpret JavaScript since the proof of work is not computed. For the more sophisticated bots, we are happy to slow them down.
|
18
|
+
|
19
|
+
Here is a [demo on a login form](https://www.rorvswild.com/session) :
|
20
|
+
|
21
|
+

|
22
|
+
|
23
|
+
## Limitations
|
24
|
+
|
25
|
+
The JavaScript implementation is 10 to 20 times slower than the official C version.
|
26
|
+
It needs some work and knowledges to be optimised. Unfortunately, I'm not a JavaScript expert.
|
27
|
+
Maybe you have good JS skills to optimize it ?
|
28
|
+
|
29
|
+
## Installation
|
30
|
+
|
31
|
+
Add this line to your application's Gemfile:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem "active_hashcash"
|
35
|
+
```
|
36
|
+
|
37
|
+
Require hashcash from your JavaScript manifest.
|
38
|
+
|
39
|
+
```js
|
40
|
+
//= require hashcash
|
41
|
+
```
|
42
|
+
|
43
|
+
Add a Hashcash hidden field into the form you want to protect.
|
44
|
+
|
45
|
+
```erb
|
46
|
+
<form>
|
47
|
+
<%= hashcash_hidden_field_tag %>
|
48
|
+
</form>
|
49
|
+
```
|
50
|
+
|
51
|
+
Then you have to define a `before_action :check_hashcash` in you controller.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class SessionController < ApplicationController
|
55
|
+
include ActiveHashcash
|
56
|
+
|
57
|
+
# Only the action receiving the form needs to be protected
|
58
|
+
before_action :check_hashcash, only: :create
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
To customize some behaviour, you can override most of the methods which begins with `hashcash_`.
|
63
|
+
Simply have a look to `active_hashcash.rb`.
|
64
|
+
|
65
|
+
You must have Redis in order to prevent double spent stamps. Otherwise it will be useless.
|
66
|
+
It automatically tries to connect with the environement variables `ACTIVE_HASHCASH_REDIS_URL` or `REDIS_URL`.
|
67
|
+
You can also manually set the URL with `ActiveHashcash.redis_url = redis://user:password@localhost:6379`.
|
68
|
+
|
69
|
+
## Complexity
|
70
|
+
|
71
|
+
Complexity is the most important parameter. By default its value is 20 and requires most of the time 5 to 20 seconds to be solved on a decent laptop.
|
72
|
+
The user won't wait that long, since he needs to fill the form while the problem is solving.
|
73
|
+
Howevever, if your application includes people with slow and old devices, then consider lowering this value, to 16 or 18.
|
74
|
+
|
75
|
+
You can change the complexity, either with `ActiveHashcash.bits = 20` or by overriding the method `hashcash_bits` in you controller.
|
76
|
+
|
77
|
+
## Contributing
|
78
|
+
|
79
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/active_hashcash.
|
80
|
+
|
81
|
+
## License
|
82
|
+
|
83
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
84
|
+
|
85
|
+
Made by Alexis Bernard at [Base Secrète](https://basesecrete.com).
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module ActiveHashcash
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
include ActionView::Helpers::FormTagHelper
|
5
|
+
|
6
|
+
included do
|
7
|
+
helper_method :hashcash_hidden_field_tag
|
8
|
+
end
|
9
|
+
|
10
|
+
mattr_accessor :bits, instance_accessor: false, default: 20
|
11
|
+
mattr_accessor :resource, instance_accessor: false
|
12
|
+
mattr_accessor :redis_url, instance_accessor: false
|
13
|
+
|
14
|
+
# TODO: protect_from_brute_force bits: 20, exception: ActionController::InvalidAuthenticityToken, with: :handle_failed_hashcash
|
15
|
+
|
16
|
+
# Call me via a before_action when the form is submitted : `before_action :chech_hashcash, only: :create`
|
17
|
+
def check_hashcash
|
18
|
+
if hashcash_stamp_is_valid? && !hashcash_stamp_spent?
|
19
|
+
hashcash_redis.sadd("active_hashcash_stamps".freeze, hashcash_param)
|
20
|
+
hashcash_after_success
|
21
|
+
else
|
22
|
+
hashcash_after_failure
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Override the methods below in your controller, to change any parameter of behaviour.
|
27
|
+
|
28
|
+
# By default the host name is used as the resource.
|
29
|
+
# It' should be good for most cases and prevent from reusing the same stamp between sites.
|
30
|
+
def hashcash_resource
|
31
|
+
ActiveHashcash.resource || request.host
|
32
|
+
end
|
33
|
+
|
34
|
+
# Define the complexity, the higher the slower it is. Consider lowering this value to not exclude people with old and slow devices.
|
35
|
+
# On a decent laptop, it takes around 30 seconds for the JavaScript implementation to solve a 20 bits complexity and few seconds when it's 16.
|
36
|
+
def hashcash_bits
|
37
|
+
ActiveHashcash.bits
|
38
|
+
end
|
39
|
+
|
40
|
+
# Override if you want to rename the hashcash param.
|
41
|
+
def hashcash_param
|
42
|
+
params[:hashcash]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Override to customize message displayed on submit button while computing hashcash.
|
46
|
+
def hashcash_waiting_message
|
47
|
+
t("active_hashcash.waiting_label")
|
48
|
+
end
|
49
|
+
|
50
|
+
# Override to provide a different behaviour when hashcash failed
|
51
|
+
def hashcash_after_failure
|
52
|
+
raise ActionController::InvalidAuthenticityToken.new("Invalid hashcash #{hashcash_param}")
|
53
|
+
end
|
54
|
+
|
55
|
+
# Maybe you want something special when the hashcash is valid. By default nothing happens.
|
56
|
+
def hashcash_after_success
|
57
|
+
# Override me for your own needs.
|
58
|
+
end
|
59
|
+
|
60
|
+
# Call it inside the form that have to be protected and don't forget to initialize the JavaScript Hascash.setup().
|
61
|
+
# Unless you need something really special, you should not need to override this method.
|
62
|
+
def hashcash_hidden_field_tag(name = :hashcash)
|
63
|
+
options = {resource: hashcash_resource, bits: hashcash_bits, waiting_message: hashcash_waiting_message}
|
64
|
+
hidden_field_tag(name, "", "data-hashcash" => options.to_json)
|
65
|
+
end
|
66
|
+
|
67
|
+
def hashcash_redis
|
68
|
+
@hashcash_redis = Redis.new(url: hashcash_redis_url)
|
69
|
+
end
|
70
|
+
|
71
|
+
def hashcash_redis_url
|
72
|
+
ActiveHashcash.redis_url || ENV["ACTIVE_HASHCASH_REDIS_URL"] || ENV["REDIS_URL"]
|
73
|
+
end
|
74
|
+
|
75
|
+
def hashcash_stamp_is_valid?
|
76
|
+
stamp = Stamp.parse(hashcash_param)
|
77
|
+
stamp.valid? && stamp.bits >= hashcash_bits && stamp.parse_date >= Date.yesterday
|
78
|
+
end
|
79
|
+
|
80
|
+
def hashcash_stamp_spent?
|
81
|
+
hashcash_redis.sismember("active_hashcash_stamps".freeze, hashcash_param)
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
class Engine < ::Rails::Engine
|
87
|
+
config.assets.paths << File.expand_path("..", __FILE__)
|
88
|
+
|
89
|
+
config.after_initialize { load_translations }
|
90
|
+
|
91
|
+
def load_translations
|
92
|
+
if !I18n.backend.exists?(I18n.locale, "active_hashcash")
|
93
|
+
I18n.backend.store_translations(:de, {active_hashcash: {waiting_label: "Warten auf die Überprüfung ..."}})
|
94
|
+
I18n.backend.store_translations(:en, {active_hashcash: {waiting_label: "Waiting for verification ..."}})
|
95
|
+
I18n.backend.store_translations(:es, {active_hashcash: {waiting_label: "A la espera de la verificación ..."}})
|
96
|
+
I18n.backend.store_translations(:fr, {active_hashcash: {waiting_label: "En attente de vérification ..."}})
|
97
|
+
I18n.backend.store_translations(:it, {active_hashcash: {waiting_label: "In attesa di verifica ..."}})
|
98
|
+
I18n.backend.store_translations(:jp, {active_hashcash: {waiting_label: "検証待ち ..."}})
|
99
|
+
I18n.backend.store_translations(:pt, {active_hashcash: {waiting_label: "À espera de verificação ..."}})
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Stamp
|
105
|
+
attr_reader :version, :bits, :date, :resource, :extension, :rand, :counter
|
106
|
+
|
107
|
+
def self.parse(string)
|
108
|
+
args = string.split(":")
|
109
|
+
new(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.mint(resource, options = {})
|
113
|
+
new(
|
114
|
+
options[:version] || 1,
|
115
|
+
options[:bits] || ActiveHashcash.bits,
|
116
|
+
options[:date] || Date.today.strftime("%y%m%d"),
|
117
|
+
resource,
|
118
|
+
options[:ext],
|
119
|
+
options[:rand] || SecureRandom.alphanumeric(16),
|
120
|
+
options[:counter] || 0).work
|
121
|
+
end
|
122
|
+
|
123
|
+
def initialize(version, bits, date, resource, extension, rand, counter)
|
124
|
+
@version = version
|
125
|
+
@bits = bits.to_i
|
126
|
+
@date = date.respond_to?(:strftime) ? date.strftime("%y%m%d") : date
|
127
|
+
@resource = resource
|
128
|
+
@extension = extension
|
129
|
+
@rand = rand
|
130
|
+
@counter = counter
|
131
|
+
end
|
132
|
+
|
133
|
+
def valid?
|
134
|
+
Digest::SHA1.hexdigest(to_s).hex >> (160-bits) == 0
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_s
|
138
|
+
[version, bits, date, resource, extension, rand, counter].join(":")
|
139
|
+
end
|
140
|
+
|
141
|
+
def parse_date
|
142
|
+
Date.strptime(date, "%y%m%d")
|
143
|
+
end
|
144
|
+
|
145
|
+
def work
|
146
|
+
@counter += 1 until valid?
|
147
|
+
self
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/hashcash.js
ADDED
@@ -0,0 +1,257 @@
|
|
1
|
+
// http://www.hashcash.org/docs/hashcash.html
|
2
|
+
// <input type="hiden" name="hashcash" data-hashcash="{resource: 'site.example', bits: 16}"/>
|
3
|
+
Hashcash = function(input) {
|
4
|
+
options = JSON.parse(input.getAttribute("data-hashcash"))
|
5
|
+
Hashcash.disableParentForm(input, options)
|
6
|
+
input.dispatchEvent(new CustomEvent("hashcash:mint", {bubbles: true}))
|
7
|
+
|
8
|
+
Hashcash.mint(options.resource, options, function(stamp) {
|
9
|
+
input.value = stamp.toString()
|
10
|
+
Hashcash.enableParentForm(input, options)
|
11
|
+
input.dispatchEvent(new CustomEvent("hashcash:minted", {bubbles: true, detail: {stamp: stamp}}))
|
12
|
+
})
|
13
|
+
}
|
14
|
+
|
15
|
+
Hashcash.setup = function() {
|
16
|
+
if (document.readyState != "loading") {
|
17
|
+
var input = document.querySelector("input#hashcash")
|
18
|
+
input && new Hashcash(input)
|
19
|
+
} else
|
20
|
+
document.addEventListener("DOMContentLoaded", Hashcash.setup )
|
21
|
+
}
|
22
|
+
|
23
|
+
Hashcash.disableParentForm = function(input, options) {
|
24
|
+
input.form.querySelectorAll("[type=submit]").forEach(function(submit) {
|
25
|
+
submit.originalValue = submit.value
|
26
|
+
options["waiting_message"] && (submit.value = options["waiting_message"])
|
27
|
+
submit.disabled = true
|
28
|
+
})
|
29
|
+
}
|
30
|
+
|
31
|
+
Hashcash.enableParentForm = function(input, options) {
|
32
|
+
input.form.querySelectorAll("[type=submit]").forEach(function(submit) {
|
33
|
+
submit.originalValue && (submit.value = submit.originalValue)
|
34
|
+
submit.disabled = null
|
35
|
+
})
|
36
|
+
}
|
37
|
+
|
38
|
+
Hashcash.default = {
|
39
|
+
version: 1,
|
40
|
+
bits: 20,
|
41
|
+
extension: null,
|
42
|
+
}
|
43
|
+
|
44
|
+
Hashcash.mint = function(resource, options, callback) {
|
45
|
+
// Format date to YYMMDD
|
46
|
+
var date = new Date
|
47
|
+
var year = date.getFullYear().toString()
|
48
|
+
year = year.slice(year.length - 2, year.length)
|
49
|
+
var month = (date.getMonth() + 1).toString().padStart(2, "0")
|
50
|
+
var day = date.getDate().toString().padStart(2, "0")
|
51
|
+
|
52
|
+
var stamp = new Hashcash.Stamp(
|
53
|
+
options.version || Hashcash.default.version,
|
54
|
+
options.bits || Hashcash.default.bits,
|
55
|
+
options.date || year + month + day,
|
56
|
+
resource,
|
57
|
+
options.extension || Hashcash.default.extension,
|
58
|
+
options.rand || Math.random().toString(36).substr(2, 10),
|
59
|
+
)
|
60
|
+
return stamp.work(callback)
|
61
|
+
}
|
62
|
+
|
63
|
+
Hashcash.Stamp = function(version, bits, date, resource, extension, rand, counter = 0) {
|
64
|
+
this.version = version
|
65
|
+
this.bits = bits
|
66
|
+
this.date = date
|
67
|
+
this.resource = resource
|
68
|
+
this.extension = extension
|
69
|
+
this.rand = rand
|
70
|
+
this.counter = counter
|
71
|
+
}
|
72
|
+
|
73
|
+
Hashcash.Stamp.parse = function(string) {
|
74
|
+
var args = string.split(":")
|
75
|
+
return new Hashcash.Stamp(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
|
76
|
+
}
|
77
|
+
|
78
|
+
Hashcash.Stamp.prototype.toString = function() {
|
79
|
+
return [this.version, this.bits, this.date, this.resource, this.extension, this.rand, this.counter].join(":")
|
80
|
+
}
|
81
|
+
|
82
|
+
// Trigger the given callback when the problem is solved.
|
83
|
+
// In order to not freeze the page, setTimeout is called every 100ms to let some CPU to other tasks.
|
84
|
+
Hashcash.Stamp.prototype.work = function(callback) {
|
85
|
+
this.startClock()
|
86
|
+
var timer = performance.now()
|
87
|
+
while (!this.check())
|
88
|
+
if (this.counter++ && performance.now() - timer > 100)
|
89
|
+
return setTimeout(this.work.bind(this), 0, callback)
|
90
|
+
this.stopClock()
|
91
|
+
callback(this)
|
92
|
+
}
|
93
|
+
|
94
|
+
Hashcash.Stamp.prototype.check = function() {
|
95
|
+
var array = Hashcash.sha1(this.toString())
|
96
|
+
return array[0] >> (160-this.bits) == 0
|
97
|
+
}
|
98
|
+
|
99
|
+
Hashcash.Stamp.prototype.startClock = function() {
|
100
|
+
this.startedAt || (this.startedAt = performance.now())
|
101
|
+
}
|
102
|
+
|
103
|
+
Hashcash.Stamp.prototype.stopClock = function() {
|
104
|
+
this.endedAt || (this.endedAt = performance.now())
|
105
|
+
var duration = this.endedAt - this.startedAt
|
106
|
+
var speed = Math.round(this.counter * 1000 / duration)
|
107
|
+
console.debug("Hashcash " + this.toString() + " minted in " + duration + "ms (" + speed + " per seconds)")
|
108
|
+
}
|
109
|
+
|
110
|
+
/**
|
111
|
+
* Secure Hash Algorithm (SHA1)
|
112
|
+
* http://www.webtoolkit.info/
|
113
|
+
**/
|
114
|
+
Hashcash.sha1 = function(msg) {
|
115
|
+
var rotate_left = Hashcash.sha1.rotate_left
|
116
|
+
var Utf8Encode = Hashcash.sha1.Utf8Encode
|
117
|
+
|
118
|
+
var blockstart;
|
119
|
+
var i, j;
|
120
|
+
var W = new Array(80);
|
121
|
+
var H0 = 0x67452301;
|
122
|
+
var H1 = 0xEFCDAB89;
|
123
|
+
var H2 = 0x98BADCFE;
|
124
|
+
var H3 = 0x10325476;
|
125
|
+
var H4 = 0xC3D2E1F0;
|
126
|
+
var A, B, C, D, E;
|
127
|
+
var temp;
|
128
|
+
msg = Utf8Encode(msg);
|
129
|
+
var msg_len = msg.length;
|
130
|
+
var word_array = new Array();
|
131
|
+
for (i = 0; i < msg_len - 3; i += 4) {
|
132
|
+
j = msg.charCodeAt(i) << 24 | msg.charCodeAt(i + 1) << 16 |
|
133
|
+
msg.charCodeAt(i + 2) << 8 | msg.charCodeAt(i + 3);
|
134
|
+
word_array.push(j);
|
135
|
+
}
|
136
|
+
switch (msg_len % 4) {
|
137
|
+
case 0:
|
138
|
+
i = 0x080000000;
|
139
|
+
break;
|
140
|
+
case 1:
|
141
|
+
i = msg.charCodeAt(msg_len - 1) << 24 | 0x0800000;
|
142
|
+
break;
|
143
|
+
case 2:
|
144
|
+
i = msg.charCodeAt(msg_len - 2) << 24 | msg.charCodeAt(msg_len - 1) << 16 | 0x08000;
|
145
|
+
break;
|
146
|
+
case 3:
|
147
|
+
i = msg.charCodeAt(msg_len - 3) << 24 | msg.charCodeAt(msg_len - 2) << 16 | msg.charCodeAt(msg_len - 1) << 8 | 0x80;
|
148
|
+
break;
|
149
|
+
}
|
150
|
+
word_array.push(i);
|
151
|
+
while ((word_array.length % 16) != 14) word_array.push(0);
|
152
|
+
word_array.push(msg_len >>> 29);
|
153
|
+
word_array.push((msg_len << 3) & 0x0ffffffff);
|
154
|
+
for (blockstart = 0; blockstart < word_array.length; blockstart += 16) {
|
155
|
+
for (i = 0; i < 16; i++) W[i] = word_array[blockstart + i];
|
156
|
+
for (i = 16; i <= 79; i++) W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
|
157
|
+
A = H0;
|
158
|
+
B = H1;
|
159
|
+
C = H2;
|
160
|
+
D = H3;
|
161
|
+
E = H4;
|
162
|
+
for (i = 0; i <= 19; i++) {
|
163
|
+
temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
|
164
|
+
E = D;
|
165
|
+
D = C;
|
166
|
+
C = rotate_left(B, 30);
|
167
|
+
B = A;
|
168
|
+
A = temp;
|
169
|
+
}
|
170
|
+
for (i = 20; i <= 39; i++) {
|
171
|
+
temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
|
172
|
+
E = D;
|
173
|
+
D = C;
|
174
|
+
C = rotate_left(B, 30);
|
175
|
+
B = A;
|
176
|
+
A = temp;
|
177
|
+
}
|
178
|
+
for (i = 40; i <= 59; i++) {
|
179
|
+
temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
|
180
|
+
E = D;
|
181
|
+
D = C;
|
182
|
+
C = rotate_left(B, 30);
|
183
|
+
B = A;
|
184
|
+
A = temp;
|
185
|
+
}
|
186
|
+
for (i = 60; i <= 79; i++) {
|
187
|
+
temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
|
188
|
+
E = D;
|
189
|
+
D = C;
|
190
|
+
C = rotate_left(B, 30);
|
191
|
+
B = A;
|
192
|
+
A = temp;
|
193
|
+
}
|
194
|
+
H0 = (H0 + A) & 0x0ffffffff;
|
195
|
+
H1 = (H1 + B) & 0x0ffffffff;
|
196
|
+
H2 = (H2 + C) & 0x0ffffffff;
|
197
|
+
H3 = (H3 + D) & 0x0ffffffff;
|
198
|
+
H4 = (H4 + E) & 0x0ffffffff;
|
199
|
+
}
|
200
|
+
return [H0, H1, H2, H3, H4]
|
201
|
+
}
|
202
|
+
|
203
|
+
Hashcash.hexSha1 = function(msg) {
|
204
|
+
var array = Hashcash.sha1(msg)
|
205
|
+
var cvt_hex = Hashcash.sha1.cvt_hex
|
206
|
+
return cvt_hex(array[0]) + cvt_hex(array[1]) + cvt_hex(array[2]) + cvt_hex(array3) + cvt_hex(array[4])
|
207
|
+
}
|
208
|
+
|
209
|
+
Hashcash.sha1.rotate_left = function(n, s) {
|
210
|
+
var t4 = (n << s) | (n >>> (32 - s));
|
211
|
+
return t4;
|
212
|
+
};
|
213
|
+
|
214
|
+
Hashcash.sha1.lsb_hex = function(val) {
|
215
|
+
var str = '';
|
216
|
+
var i;
|
217
|
+
var vh;
|
218
|
+
var vl;
|
219
|
+
for (i = 0; i <= 6; i += 2) {
|
220
|
+
vh = (val >>> (i * 4 + 4)) & 0x0f;
|
221
|
+
vl = (val >>> (i * 4)) & 0x0f;
|
222
|
+
str += vh.toString(16) + vl.toString(16);
|
223
|
+
}
|
224
|
+
return str;
|
225
|
+
};
|
226
|
+
|
227
|
+
Hashcash.sha1.cvt_hex = function(val) {
|
228
|
+
var str = '';
|
229
|
+
var i;
|
230
|
+
var v;
|
231
|
+
for (i = 7; i >= 0; i--) {
|
232
|
+
v = (val >>> (i * 4)) & 0x0f;
|
233
|
+
str += v.toString(16);
|
234
|
+
}
|
235
|
+
return str;
|
236
|
+
};
|
237
|
+
|
238
|
+
Hashcash.sha1.Utf8Encode = function(string) {
|
239
|
+
string = string.replace(/\r\n/g, '\n');
|
240
|
+
var utftext = '';
|
241
|
+
for (var n = 0; n < string.length; n++) {
|
242
|
+
var c = string.charCodeAt(n);
|
243
|
+
if (c < 128) {
|
244
|
+
utftext += String.fromCharCode(c);
|
245
|
+
} else if ((c > 127) && (c < 2048)) {
|
246
|
+
utftext += String.fromCharCode((c >> 6) | 192);
|
247
|
+
utftext += String.fromCharCode((c & 63) | 128);
|
248
|
+
} else {
|
249
|
+
utftext += String.fromCharCode((c >> 12) | 224);
|
250
|
+
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
251
|
+
utftext += String.fromCharCode((c & 63) | 128);
|
252
|
+
}
|
253
|
+
}
|
254
|
+
return utftext;
|
255
|
+
};
|
256
|
+
|
257
|
+
Hashcash.setup()
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_hashcash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexis Bernard
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-07-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redis
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 5.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 5.2.0
|
41
|
+
description: Potect your Rails application against DoS and bots.
|
42
|
+
email:
|
43
|
+
- alexis@basesecrete.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- CHANGELOG.md
|
49
|
+
- LICENSE.txt
|
50
|
+
- README.md
|
51
|
+
- lib/active_hashcash.rb
|
52
|
+
- lib/hashcash.js
|
53
|
+
homepage: https://github.com/BaseSecrete/active_hashcash
|
54
|
+
licenses:
|
55
|
+
- MIT
|
56
|
+
metadata:
|
57
|
+
homepage_uri: https://github.com/BaseSecrete/active_hashcash
|
58
|
+
source_code_uri: https://github.com/BaseSecrete/active_hashcash
|
59
|
+
changelog_uri: https://github.com/BaseSecrete/active_hashcash/CHANGELOG.md
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.4.0
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubygems_version: 3.2.22
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: Potect your Rails application against DoS and bots.
|
79
|
+
test_files: []
|