active_hashcash 0.3.2 → 0.5.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 +4 -4
- data/CHANGELOG.md +15 -1
- data/README.md +77 -50
- data/app/assets/javascripts/hashcash.js +237 -176
- data/app/controllers/active_hashcash/addresses_controller.rb +1 -1
- data/app/controllers/active_hashcash/application_controller.rb +2 -1
- data/app/controllers/active_hashcash/assets_controller.rb +2 -2
- data/app/controllers/active_hashcash/stamps_controller.rb +1 -1
- data/app/helpers/active_hashcash/addresses_helper.rb +1 -1
- data/app/helpers/active_hashcash/application_helper.rb +1 -1
- data/app/helpers/active_hashcash/stamps_helper.rb +1 -1
- data/app/jobs/active_hashcash/application_job.rb +1 -1
- data/app/mailers/active_hashcash/application_mailer.rb +1 -1
- data/app/models/active_hashcash/application_record.rb +1 -1
- data/app/models/active_hashcash/stamp.rb +13 -1
- data/config/locales/ca.yml +4 -0
- data/db/migrate/20240215143453_create_active_hashcash_stamps.rb +7 -0
- data/lib/active_hashcash/engine.rb +1 -1
- data/lib/active_hashcash/version.rb +1 -1
- data/lib/active_hashcash.rb +49 -8
- metadata +4 -7
- data/lib/hashcash.js +0 -257
data/lib/active_hashcash.rb
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "active_hashcash/version"
|
|
2
4
|
require "active_hashcash/engine"
|
|
3
5
|
|
|
6
|
+
# ActiveHashcash protects Rails applications against bots and brute force attacks without annoying humans.
|
|
7
|
+
# See the rdoc-ref:README.md for more explanations about Hashcash.
|
|
8
|
+
#
|
|
9
|
+
# Include this module into your Rails controller.
|
|
10
|
+
#
|
|
11
|
+
# class SessionController < ApplicationController
|
|
12
|
+
# include ActiveHashcash
|
|
13
|
+
#
|
|
14
|
+
# before_action :check_hashcash, only: :create
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# Your are welcome to override most of the methods to customize to your needs.
|
|
18
|
+
# For example, if your app runs behind a loab balancer you should probably override #hashcash_ip_address.
|
|
19
|
+
#
|
|
4
20
|
module ActiveHashcash
|
|
5
21
|
extend ActiveSupport::Concern
|
|
6
22
|
|
|
@@ -16,7 +32,14 @@ module ActiveHashcash
|
|
|
16
32
|
|
|
17
33
|
mattr_accessor :date_format, instance_accessor: false, default: "%y%m%d"
|
|
18
34
|
|
|
19
|
-
|
|
35
|
+
mattr_accessor :base_controller_class, default: "ActionController::Base"
|
|
36
|
+
|
|
37
|
+
# Call that method via a before_action when the form is submitted:
|
|
38
|
+
#
|
|
39
|
+
# before_action :check_hashcash, only: :create
|
|
40
|
+
#
|
|
41
|
+
# In case of invalid hashcash it calls hashcash_after_failure that you can override.
|
|
42
|
+
# Otherwise, hashcash stamp is stored in database to prevent from double spending.
|
|
20
43
|
def check_hashcash
|
|
21
44
|
attrs = {
|
|
22
45
|
ip_address: hashcash_ip_address,
|
|
@@ -30,21 +53,26 @@ module ActiveHashcash
|
|
|
30
53
|
end
|
|
31
54
|
end
|
|
32
55
|
|
|
33
|
-
#
|
|
34
|
-
|
|
56
|
+
# Returns remote IP address.
|
|
57
|
+
# They are used to automatically increase complexity when the same IP sends many valid hashcash.
|
|
58
|
+
# If you're app is behind a load balancer, you should probably override it to read the right HTTP header.
|
|
35
59
|
def hashcash_ip_address
|
|
36
60
|
request.remote_ip
|
|
37
61
|
end
|
|
38
62
|
|
|
63
|
+
# Return current request path to be saved to the sucessful ActiveHash::Stamp.
|
|
64
|
+
# If multiple forms are protected via hashcash this is an interesting info.
|
|
39
65
|
def hashcash_request_path
|
|
40
66
|
request.path
|
|
41
67
|
end
|
|
42
68
|
|
|
69
|
+
# Override this method to store custom data for each stamp.
|
|
70
|
+
# It must returns a hash or nil.
|
|
43
71
|
def hashcash_stamp_context
|
|
44
|
-
# Override this method to store custom data for each stamp
|
|
45
72
|
end
|
|
46
73
|
|
|
47
|
-
#
|
|
74
|
+
# This is the resource used to build the hashcash stamp.
|
|
75
|
+
# By default the host name is returned.
|
|
48
76
|
# It' should be good for most cases and prevent from reusing the same stamp between sites.
|
|
49
77
|
def hashcash_resource
|
|
50
78
|
ActiveHashcash.resource || request.host
|
|
@@ -52,7 +80,7 @@ module ActiveHashcash
|
|
|
52
80
|
|
|
53
81
|
# Returns the complexity, the higher the slower it is.
|
|
54
82
|
# Complexity is increased logarithmicly for each IP during the last 24H to slowdown brute force attacks.
|
|
55
|
-
# The minimun value returned is
|
|
83
|
+
# The minimun value returned is ActiveHashcash.bits.
|
|
56
84
|
def hashcash_bits
|
|
57
85
|
if (previous_stamp_count = ActiveHashcash::Stamp.where(ip_address: hashcash_ip_address).where(created_at: 1.day.ago..).count) > 0
|
|
58
86
|
(ActiveHashcash.bits + Math.log2(previous_stamp_count)).floor
|
|
@@ -71,7 +99,9 @@ module ActiveHashcash
|
|
|
71
99
|
t("active_hashcash.waiting_label")
|
|
72
100
|
end
|
|
73
101
|
|
|
74
|
-
#
|
|
102
|
+
# That method is called when #check_hashcash fails.
|
|
103
|
+
# It raises ActionController::InvalidAuthenticityToken so HTTP response will be 422 by default.
|
|
104
|
+
# Override this method to provide a different behaviour.
|
|
75
105
|
def hashcash_after_failure
|
|
76
106
|
raise ActionController::InvalidAuthenticityToken.new("Invalid hashcash #{hashcash_param}")
|
|
77
107
|
end
|
|
@@ -83,8 +113,19 @@ module ActiveHashcash
|
|
|
83
113
|
|
|
84
114
|
# Call it inside the form that have to be protected and don't forget to initialize the JavaScript Hascash.setup().
|
|
85
115
|
# Unless you need something really special, you should not need to override this method.
|
|
116
|
+
#
|
|
117
|
+
# <% form_for model do |form| %>
|
|
118
|
+
# <%= hashcash_hidden_field_tag %>
|
|
119
|
+
#
|
|
120
|
+
# <% end %>
|
|
121
|
+
#
|
|
86
122
|
def hashcash_hidden_field_tag(name = :hashcash)
|
|
87
|
-
options = {
|
|
123
|
+
options = {
|
|
124
|
+
resource: hashcash_resource,
|
|
125
|
+
bits: hashcash_bits,
|
|
126
|
+
waiting_message: hashcash_waiting_message,
|
|
127
|
+
date: Date.today.strftime("%y%m%d")
|
|
128
|
+
}
|
|
88
129
|
view_context.hidden_field_tag(name, "", "data-hashcash" => options.to_json)
|
|
89
130
|
end
|
|
90
131
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_hashcash
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexis Bernard
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -62,6 +61,7 @@ files:
|
|
|
62
61
|
- app/views/active_hashcash/stamps/index.html.erb
|
|
63
62
|
- app/views/active_hashcash/stamps/show.html.erb
|
|
64
63
|
- app/views/layouts/active_hashcash/application.html.erb
|
|
64
|
+
- config/locales/ca.yml
|
|
65
65
|
- config/locales/de.yml
|
|
66
66
|
- config/locales/en.yml
|
|
67
67
|
- config/locales/es.yml
|
|
@@ -74,7 +74,6 @@ files:
|
|
|
74
74
|
- lib/active_hashcash.rb
|
|
75
75
|
- lib/active_hashcash/engine.rb
|
|
76
76
|
- lib/active_hashcash/version.rb
|
|
77
|
-
- lib/hashcash.js
|
|
78
77
|
- lib/tasks/active_hashcash_tasks.rake
|
|
79
78
|
homepage: https://github.com/BaseSecrete/active_hashcash
|
|
80
79
|
licenses:
|
|
@@ -83,7 +82,6 @@ metadata:
|
|
|
83
82
|
homepage_uri: https://github.com/BaseSecrete/active_hashcash
|
|
84
83
|
source_code_uri: https://github.com/BaseSecrete/active_hashcash
|
|
85
84
|
changelog_uri: https://github.com/BaseSecrete/active_hashcash/CHANGELOG.md
|
|
86
|
-
post_install_message:
|
|
87
85
|
rdoc_options: []
|
|
88
86
|
require_paths:
|
|
89
87
|
- lib
|
|
@@ -98,8 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
98
96
|
- !ruby/object:Gem::Version
|
|
99
97
|
version: '0'
|
|
100
98
|
requirements: []
|
|
101
|
-
rubygems_version:
|
|
102
|
-
signing_key:
|
|
99
|
+
rubygems_version: 4.0.10
|
|
103
100
|
specification_version: 4
|
|
104
101
|
summary: Protect Rails applications against bots and brute force attacks without annoying
|
|
105
102
|
humans.
|
data/lib/hashcash.js
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
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()
|