two_factor_auth 0.1.1
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/LICENSE +634 -0
- data/Rakefile +34 -0
- data/app/assets/javascripts/two_factor_auth/application.js +13 -0
- data/app/assets/stylesheets/two_factor_auth/application.css +15 -0
- data/app/controllers/two_factor_auth/authentications_controller.rb +32 -0
- data/app/controllers/two_factor_auth/registrations_controller.rb +29 -0
- data/app/controllers/two_factor_auth/trusted_facets_controller.rb +10 -0
- data/app/controllers/two_factor_auth/two_factor_auth_controller.rb +19 -0
- data/app/helpers/two_factor_auth/application_helper.rb +21 -0
- data/app/helpers/two_factor_auth/authentications_helper.rb +17 -0
- data/app/helpers/two_factor_auth/registrations_helper.rb +17 -0
- data/app/models/two_factor_auth/authentication_client_data.rb +11 -0
- data/app/models/two_factor_auth/authentication_request.rb +30 -0
- data/app/models/two_factor_auth/authentication_response.rb +49 -0
- data/app/models/two_factor_auth/authentication_verifier.rb +68 -0
- data/app/models/two_factor_auth/client_data.rb +57 -0
- data/app/models/two_factor_auth/registration.rb +18 -0
- data/app/models/two_factor_auth/registration_request.rb +33 -0
- data/app/models/two_factor_auth/registration_response.rb +91 -0
- data/app/models/two_factor_auth/registration_verifier.rb +91 -0
- data/app/views/layouts/two_factor_auth/application.html.erb +16 -0
- data/app/views/two_factor_auth/authentications/new.html.erb +30 -0
- data/app/views/two_factor_auth/registrations/new.html.erb +26 -0
- data/config/routes.rb +3 -0
- data/lib/generators/templates/README +6 -0
- data/lib/generators/templates/initializer.rb +38 -0
- data/lib/generators/templates/migration.rb +15 -0
- data/lib/generators/two_factor_auth/install_generator.rb +32 -0
- data/lib/tasks/two_factor_auth_tasks.rake +13 -0
- data/lib/two_factor_auth/authentication_hook.rb +18 -0
- data/lib/two_factor_auth/engine.rb +5 -0
- data/lib/two_factor_auth/registration_hook.rb +17 -0
- data/lib/two_factor_auth/version.rb +3 -0
- data/lib/two_factor_auth.rb +155 -0
- data/test/controllers/two_factor_auth/authentications_controller_test.rb +70 -0
- data/test/controllers/two_factor_auth/registrations_controller_test.rb +57 -0
- data/test/controllers/two_factor_auth/trusted_facets_controller_test.rb +17 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/controllers/secrets_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/user.rb +8 -0
- data/test/dummy/app/views/layouts/application.html.erb +16 -0
- data/test/dummy/app/views/secrets/index.html.erb +10 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config/application.rb +24 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +78 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/assets.rb +8 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/devise.rb +259 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/two_factor_auth.rb +38 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/devise.en.yml +60 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20141026231953_devise_create_users.rb +42 -0
- data/test/dummy/db/migrate/20141224135949_create_two_factor_auth_registrations.rb +15 -0
- data/test/dummy/db/schema.rb +50 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +198 -0
- data/test/dummy/log/test.log +3490 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/helpers/two_factor_auth/authentication_helper_test.rb +54 -0
- data/test/helpers/two_factor_auth/registrations_helper_test.rb +34 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/lib/two_factor_auth_test.rb +169 -0
- data/test/models/two_factor_auth/authentication_request_test.rb +35 -0
- data/test/models/two_factor_auth/authentication_response_test.rb +44 -0
- data/test/models/two_factor_auth/authentication_verifier_test.rb +83 -0
- data/test/models/two_factor_auth/client_data_test.rb +79 -0
- data/test/models/two_factor_auth/registration_request_test.rb +29 -0
- data/test/models/two_factor_auth/registration_response_test.rb +87 -0
- data/test/models/two_factor_auth/registration_verifier_test.rb +96 -0
- data/test/test_helper.rb +43 -0
- metadata +351 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
module TwoFactorAuth
|
|
4
|
+
describe ClientData do
|
|
5
|
+
#parallelize_me!
|
|
6
|
+
|
|
7
|
+
let(:clientData) { "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZmluaXNoRW5yb2xsbWVudCIsImNoYWxsZW5nZSI6IjVmeGF6cWFuUkh0ZDdBdEVIQkd4eERwU2o2bWRCRjI2WEY0eGRBOW03SnciLCJvcmlnaW4iOiJodHRwOi8vbG9jYWwuZmlkb2xvZ2luLmNvbTozMDAwIiwiY2lkX3B1YmtleSI6IiJ9" }
|
|
8
|
+
let(:client_data) { ClientData.new(encoded: clientData, correct_typ: 'navigator.id.finishEnrollment') }
|
|
9
|
+
|
|
10
|
+
describe "decomposing attrs" do
|
|
11
|
+
it "extracts the typ" do
|
|
12
|
+
client_data.typ.must_equal "navigator.id.finishEnrollment"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "extracts the challenge" do
|
|
16
|
+
client_data.challenge.must_equal "5fxazqanRHtd7AtEHBGxxDpSj6mdBF26XF4xdA9m7Jw"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "extracts the origin" do
|
|
20
|
+
client_data.origin.must_equal "http://local.fidologin.com:3000"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe "validations" do
|
|
25
|
+
it "is valid with all fields valid" do
|
|
26
|
+
client_data.valid?.must_equal true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# I can't see a nice way to break these checks out of decompose_attrs and,
|
|
30
|
+
# because ActiveModel is all about mutability, it flushes errors when
|
|
31
|
+
# .valid? is called.
|
|
32
|
+
#it "is not if encoding is invalid" do
|
|
33
|
+
# cd = ClientData.new(encoded: "bad encoded data", correct_typ: 'navigator.id.finishEnrollment')
|
|
34
|
+
# cd.valid?.must_equal false
|
|
35
|
+
# cd.errors[:encoded].wont_be_empty
|
|
36
|
+
#end
|
|
37
|
+
#
|
|
38
|
+
#it "is not if json is invalid" do
|
|
39
|
+
# cd = ClientData.new(encoded: TwoFactorAuth.websafe_base64_encode("{blah"), correct_typ: 'navigator.id.finishEnrollment')
|
|
40
|
+
# cd.valid?.must_equal false
|
|
41
|
+
# cd.errors[:json].wont_be_empty
|
|
42
|
+
#end
|
|
43
|
+
|
|
44
|
+
it "is not if a key is added" do
|
|
45
|
+
client_data.attrs[:extra] = "pair"
|
|
46
|
+
client_data.valid?.must_equal false
|
|
47
|
+
client_data.errors[:attrs].wont_be_empty
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "is not if a key is missing" do
|
|
51
|
+
client_data.attrs.delete('origin')
|
|
52
|
+
client_data.valid?.must_equal false
|
|
53
|
+
client_data.errors[:attrs].wont_be_empty
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "accepts two possible values for correct_typ" do
|
|
57
|
+
cd = ClientData.new(encoded: clientData, correct_typ: 'navigator.id.finishEnrollment')
|
|
58
|
+
cd.valid?
|
|
59
|
+
cd.errors[:correct_typ].must_be_empty
|
|
60
|
+
cd = ClientData.new(encoded: clientData, correct_typ: 'navigator.id.getAssertion')
|
|
61
|
+
cd.valid?
|
|
62
|
+
cd.errors[:correct_typ].must_be_empty
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "raises on construction without a correct_typ" do
|
|
66
|
+
Proc.new {
|
|
67
|
+
ClientData.new(encoded: clientData)
|
|
68
|
+
}.must_raise ArgumentError
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "is not if typ is incorrect" do
|
|
72
|
+
client_data.typ = "invalid typ"
|
|
73
|
+
client_data.valid?.must_equal false
|
|
74
|
+
client_data.errors[:typ].wont_be_empty
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
module TwoFactorAuth
|
|
4
|
+
describe RegistrationRequest do
|
|
5
|
+
let(:app_id) { 'http://twofactorauth.example.com/' }
|
|
6
|
+
|
|
7
|
+
it "holds app id" do
|
|
8
|
+
rr = RegistrationRequest.new('http://id.example.com/')
|
|
9
|
+
rr.app_id.must_equal 'http://id.example.com/'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "creates random challenges" do
|
|
13
|
+
rr1 = RegistrationRequest.new(app_id)
|
|
14
|
+
rr2 = RegistrationRequest.new(app_id)
|
|
15
|
+
rr1.challenge.wont_equal rr2.challenge
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "uses the challenge given" do
|
|
19
|
+
encoded_challenge = TwoFactorAuth.websafe_base64_encode('0' * 32)
|
|
20
|
+
rr = RegistrationRequest.new(app_id, [], encoded_challenge)
|
|
21
|
+
rr.challenge.must_equal encoded_challenge
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "does not return Signs without KeyHandles" do
|
|
25
|
+
rr = RegistrationRequest.new(app_id)
|
|
26
|
+
rr.signs.must_equal []
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
module TwoFactorAuth
|
|
4
|
+
describe RegistrationResponse do
|
|
5
|
+
#parallelize_me!
|
|
6
|
+
|
|
7
|
+
let(:registrationData) { "BQQqdFC3zhANYW9DmErAjFQYZjBExK22PLx-ViMOch04-wZ990aqOcF2gxS5gzSUDKzpPGXpliMk3UoXgYlC2QNuQGbQ4E5v_UrLCzT58SXg902p9JXmLboTF42QkuZXIbdea_97h96lVovJ7xrA-iWTrZiOSRVZoBZsTrCW64XMrUcwggIcMIIBBqADAgECAgQk26tAMAsGCSqGSIb3DQEBCzAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowKzEpMCcGA1UEAwwgWXViaWNvIFUyRiBFRSBTZXJpYWwgMTM1MDMyNzc4ODgwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQCsJS-NH1HeUHEd46-xcpN7SpHn6oeb-w5r-veDCBwy1vUvWnJanjjv4dR_rV5G436ysKUAXUcsVe5fAnkORo2oxIwEDAOBgorBgEEAYLECgEBBAAwCwYJKoZIhvcNAQELA4IBAQCjY64OmDrzC7rxLIst81pZvxy7ShsPy2jEhFWEkPaHNFhluNsCacNG5VOITCxWB68OonuQrIzx70MfcqwYnbIcgkkUvxeIpVEaM9B7TI40ZHzp9h4VFqmps26QCkAgYfaapG4SxTK5k_lCPvqqTPmjtlS03d7ykkpUj9WZlVEN1Pf02aTVIZOHPHHJuH6GhT6eLadejwxtKDBTdNTv3V4UlvjDOQYQe9aL1jUNqtLDeBHso8pDvJMLc0CX3vadaI2UVQxM-xip4kuGouXYj0mYmaCbzluBDFNsrzkNyL3elg3zMMrKvAUhoYMjlX_-vKWcqQsgsQ0JtSMcWMJ-umeDMEUCIQCPkI4L_gHM88JrqJj_ZNRghQyC0gJyCC9RBrnfI2mDTwIgPOuEiD1AOfRaGO_EaHi-z4XyIGDhkG8-BYH-syVY5_o" }
|
|
8
|
+
let(:response) { RegistrationResponse.new(encoded: registrationData) }
|
|
9
|
+
|
|
10
|
+
describe "decomposing fields" do
|
|
11
|
+
|
|
12
|
+
it "extracts reserved byte" do
|
|
13
|
+
response.reserved_byte.must_equal [5].pack('C*')
|
|
14
|
+
response.reserved_byte.length.must_equal 1
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "extracts user's public key" do
|
|
18
|
+
response.public_key.must_equal "\x04*tP\xB7\xCE\x10\raoC\x98J\xC0\x8CT\x18f0D\xC4\xAD\xB6<\xBC~V#\x0Er\x1D8\xFB\x06}\xF7F\xAA9\xC1v\x83\x14\xB9\x834\x94\f\xAC\xE9<e\xE9\x96\#$\xDDJ\x17\x81\x89B\xD9\x03n".force_encoding('ASCII-8BIT')
|
|
19
|
+
response.public_key.length.must_equal 65
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "extracts key handle" do
|
|
23
|
+
response.key_handle.must_equal "f\xD0\xE0No\xFDJ\xCB\v4\xF9\xF1%\xE0\xF7M\xA9\xF4\x95\xE6-\xBA\x13\x17\x8D\x90\x92\xE6W!\xB7^k\xFF{\x87\xDE\xA5V\x8B\xC9\xEF\x1A\xC0\xFA%\x93\xAD\x98\x8EI\x15Y\xA0\x16lN\xB0\x96\xEB\x85\xCC\xADG".force_encoding('ASCII-8BIT')
|
|
24
|
+
response.key_handle.length.must_equal 64
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "extracts attestation certificate" do
|
|
28
|
+
response.certificate.must_equal "0\x82\x02\x1C0\x82\x01\x06\xA0\x03\x02\x01\x02\x02\x04$\xDB\xAB@0\v\x06\t*\x86H\x86\xF7\r\x01\x01\v0.1,0*\x06\x03U\x04\x03\x13#Yubico U2F Root CA Serial 4572006310 \x17\r140801000000Z\x18\x0F20500904000000Z0+1)0'\x06\x03U\x04\x03\f Yubico U2F EE Serial 135032778880Y0\x13\x06\a*\x86H\xCE=\x02\x01\x06\b*\x86H\xCE=\x03\x01\a\x03B\x00\x04\x02\xB0\x94\xBE4}GyA\xC4w\x8E\xBE\xC5\xCAM\xED*G\x9F\xAA\x1Eo\xEC9\xAF\xEB\xDE\f p\xCB[\xD4\xBDi\xC9jx\xE3\xBF\x87Q\xFE\xB5y\e\x8D\xFA\xCA\xC2\x94\x01u\x1C\xB1W\xB9|\t\xE49\x1A6\xA3\x120\x100\x0E\x06\n+\x06\x01\x04\x01\x82\xC4\n\x01\x01\x04\x000\v\x06\t*\x86H\x86\xF7\r\x01\x01\v\x03\x82\x01\x01\x00\xA3c\xAE\x0E\x98:\xF3\v\xBA\xF1,\x8B-\xF3ZY\xBF\x1C\xBBJ\e\x0F\xCBh\xC4\x84U\x84\x90\xF6\x874Xe\xB8\xDB\x02i\xC3F\xE5S\x88L,V\a\xAF\x0E\xA2{\x90\xAC\x8C\xF1\xEFC\x1Fr\xAC\x18\x9D\xB2\x1C\x82I\x14\xBF\x17\x88\xA5Q\x1A3\xD0{L\x8E4d|\xE9\xF6\x1E\x15\x16\xA9\xA9\xB3n\x90\n@ a\xF6\x9A\xA4n\x12\xC52\xB9\x93\xF9B>\xFA\xAAL\xF9\xA3\xB6T\xB4\xDD\xDE\xF2\x92JT\x8F\xD5\x99\x95Q\r\xD4\xF7\xF4\xD9\xA4\xD5!\x93\x87<q\xC9\xB8~\x86\x85>\x9E-\xA7^\x8F\fm(0St\xD4\xEF\xDD^\x14\x96\xF8\xC39\x06\x10{\xD6\x8B\xD65\r\xAA\xD2\xC3x\x11\xEC\xA3\xCAC\xBC\x93\vs@\x97\xDE\xF6\x9Dh\x8D\x94U\fL\xFB\x18\xA9\xE2K\x86\xA2\xE5\xD8\x8FI\x98\x99\xA0\x9B\xCE[\x81\fSl\xAF9\r\xC8\xBD\xDE\x96\r\xF30\xCA\xCA\xBC\x05!\xA1\x83#\x95\x7F\xFE\xBC\xA5\x9C\xA9\v \xB1\r\t\xB5#\x1CX\xC2~\xBAg\x83".force_encoding('ASCII-8BIT')
|
|
29
|
+
response.certificate.length.must_equal 544
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "extracts signature" do
|
|
33
|
+
response.signature.must_equal "0E\x02!\x00\x8F\x90\x8E\v\xFE\x01\xCC\xF3\xC2k\xA8\x98\xFFd\xD4`\x85\f\x82\xD2\x02r\b/Q\x06\xB9\xDF#i\x83O\x02 <\xEB\x84\x88=@9\xF4Z\x18\xEF\xC4hx\xBE\xCF\x85\xF2 `\xE1\x90o>\x05\x81\xFE\xB3%X\xE7\xFA".force_encoding('ASCII-8BIT')
|
|
34
|
+
response.signature.length.must_equal 71
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "starts with registrationData a known size" do
|
|
38
|
+
sum = TwoFactorAuth.websafe_base64_decode(registrationData).length
|
|
39
|
+
sum.must_equal 746
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "extracts all data to fields" do
|
|
43
|
+
# length of fields + byte for key handle length = raw
|
|
44
|
+
sum = TwoFactorAuth.websafe_base64_decode(registrationData).length
|
|
45
|
+
(
|
|
46
|
+
response.reserved_byte.length +
|
|
47
|
+
response.public_key.length +
|
|
48
|
+
1 +
|
|
49
|
+
response.key_handle.length +
|
|
50
|
+
response.certificate.length +
|
|
51
|
+
response.signature.length
|
|
52
|
+
).must_equal sum
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "#certificate_public_key" do
|
|
57
|
+
it "extracts the certificate's public key" do
|
|
58
|
+
response.certificate_public_key.to_bn.to_i.must_equal 53772106386491294167326085516082733333742865191976757260691005884697072590710730429546268855611153149813381544656650324644681905213198659862106126975310390
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe "validations" do
|
|
63
|
+
it "is valid with all fields valid" do
|
|
64
|
+
response.valid?.must_equal true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "is not if reserved byte is incorrect" do
|
|
68
|
+
response.reserved_byte = 1.chr
|
|
69
|
+
response.valid?.must_equal false
|
|
70
|
+
response.errors[:reserved_byte].wont_be_empty
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "is not if vertificate is not valid" do
|
|
74
|
+
response.certificate = "not actually a cert"
|
|
75
|
+
response.valid?.must_equal false
|
|
76
|
+
response.errors[:certificate].wont_be_empty
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "is not if public key is not valid" do
|
|
80
|
+
response.public_key = "invalid pk"
|
|
81
|
+
response.valid?.must_equal false
|
|
82
|
+
response.errors[:public_key].wont_be_empty
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
module TwoFactorAuth
|
|
4
|
+
describe RegistrationVerifier do
|
|
5
|
+
#parallelize_me!
|
|
6
|
+
|
|
7
|
+
let(:app_id) { "http://local.fidologin.com:3000" }
|
|
8
|
+
let(:request) { RegistrationRequest.new(app_id, [], "5fxazqanRHtd7AtEHBGxxDpSj6mdBF26XF4xdA9m7Jw") }
|
|
9
|
+
let(:client_data) { ClientData.new encoded: "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZmluaXNoRW5yb2xsbWVudCIsImNoYWxsZW5nZSI6IjVmeGF6cWFuUkh0ZDdBdEVIQkd4eERwU2o2bWRCRjI2WEY0eGRBOW03SnciLCJvcmlnaW4iOiJodHRwOi8vbG9jYWwuZmlkb2xvZ2luLmNvbTozMDAwIiwiY2lkX3B1YmtleSI6IiJ9", correct_typ: 'navigator.id.finishEnrollment' }
|
|
10
|
+
let(:response) { RegistrationResponse.new encoded: "BQQqdFC3zhANYW9DmErAjFQYZjBExK22PLx-ViMOch04-wZ990aqOcF2gxS5gzSUDKzpPGXpliMk3UoXgYlC2QNuQGbQ4E5v_UrLCzT58SXg902p9JXmLboTF42QkuZXIbdea_97h96lVovJ7xrA-iWTrZiOSRVZoBZsTrCW64XMrUcwggIcMIIBBqADAgECAgQk26tAMAsGCSqGSIb3DQEBCzAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowKzEpMCcGA1UEAwwgWXViaWNvIFUyRiBFRSBTZXJpYWwgMTM1MDMyNzc4ODgwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQCsJS-NH1HeUHEd46-xcpN7SpHn6oeb-w5r-veDCBwy1vUvWnJanjjv4dR_rV5G436ysKUAXUcsVe5fAnkORo2oxIwEDAOBgorBgEEAYLECgEBBAAwCwYJKoZIhvcNAQELA4IBAQCjY64OmDrzC7rxLIst81pZvxy7ShsPy2jEhFWEkPaHNFhluNsCacNG5VOITCxWB68OonuQrIzx70MfcqwYnbIcgkkUvxeIpVEaM9B7TI40ZHzp9h4VFqmps26QCkAgYfaapG4SxTK5k_lCPvqqTPmjtlS03d7ykkpUj9WZlVEN1Pf02aTVIZOHPHHJuH6GhT6eLadejwxtKDBTdNTv3V4UlvjDOQYQe9aL1jUNqtLDeBHso8pDvJMLc0CX3vadaI2UVQxM-xip4kuGouXYj0mYmaCbzluBDFNsrzkNyL3elg3zMMrKvAUhoYMjlX_-vKWcqQsgsQ0JtSMcWMJ-umeDMEUCIQCPkI4L_gHM88JrqJj_ZNRghQyC0gJyCC9RBrnfI2mDTwIgPOuEiD1AOfRaGO_EaHi-z4XyIGDhkG8-BYH-syVY5_o" }
|
|
11
|
+
let(:verifier) { RegistrationVerifier.new({
|
|
12
|
+
login: 'login placeholder',
|
|
13
|
+
request: request,
|
|
14
|
+
client_data: client_data,
|
|
15
|
+
response: response,
|
|
16
|
+
}) }
|
|
17
|
+
|
|
18
|
+
describe '#application_parameter' do
|
|
19
|
+
it "is the sha256 hash of the app id" do
|
|
20
|
+
verifier.application_parameter.must_equal "Nh\xA3j\x7F\xB2\xEE\xD6D\x1F\x18\t\xF1\xAEL\t\x7F\fjo\n\x80\xDE\xAD{\x9E\x13\x04\xAE\xFD.\xD6".force_encoding('ASCII-8bit')
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe '#challenge_parameter' do
|
|
25
|
+
it "is the sha256 hash of the base64 decoded json string" do
|
|
26
|
+
verifier.challenge_parameter.must_equal "\x05\xB4\xF4\x06\xDA\xF3So\xD9\x96\xB4XCSw\x93\xBFq\xDDS\xEE\x13NT\xB1\xFD\x88m\xF7\xCF\xB8|".force_encoding('ASCII-8bit')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe '#digest' do
|
|
31
|
+
it "combines fields to spec" do
|
|
32
|
+
verifier.digest.must_equal "\xFD\xED*TyGh\b\xD9\x01\x9A\x01ih\xF8/\x00\xA8\x98\x06\xD7~\x00s\x0F\x82h\xAA=\xD1\xAF\xA0".force_encoding('ASCII-8BIT')
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe 'validation' do
|
|
37
|
+
it 'is if response matches and signs request' do
|
|
38
|
+
verifier.valid?.must_equal true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'is not if client_data is invalid' do
|
|
42
|
+
verifier.client_data = ClientData.new encoded: "foo", correct_typ: 'navigator.id.finishEnrollment'
|
|
43
|
+
verifier.valid?.must_equal false
|
|
44
|
+
verifier.errors[:client_data].wont_be_empty
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'is not if response is invalid' do
|
|
48
|
+
verifier.response = RegistrationResponse.new encoded: "foo"
|
|
49
|
+
verifier.valid?.must_equal false
|
|
50
|
+
verifier.errors[:response].wont_be_empty
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'is not if client challenge is different' do
|
|
54
|
+
verifier.client_data.challenge = "wrong challenge"
|
|
55
|
+
verifier.valid?.must_equal false
|
|
56
|
+
verifier.errors[:client_data].wont_be_empty
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'is not if client origin is different' do
|
|
60
|
+
verifier.client_data.origin = "http://wrong.origin"
|
|
61
|
+
verifier.valid?.must_equal false
|
|
62
|
+
verifier.errors[:client_data].wont_be_empty
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'is not if signature is invalid' do
|
|
66
|
+
verifier.response.signature = "bad sig"
|
|
67
|
+
verifier.valid?.must_equal false
|
|
68
|
+
verifier.errors[:response].wont_be_empty
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe "#registration_attributes returns attributes worth persisting for registration" do
|
|
73
|
+
let(:attrs) { verifier.registration_attributes }
|
|
74
|
+
|
|
75
|
+
it "has login" do
|
|
76
|
+
attrs[:login].must_equal "login placeholder"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "starts counter at zero" do
|
|
80
|
+
attrs[:counter].must_equal 0
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "has key handle" do
|
|
84
|
+
attrs[:key_handle].must_equal "f\xD0\xE0No\xFDJ\xCB\v4\xF9\xF1%\xE0\xF7M\xA9\xF4\x95\xE6-\xBA\x13\x17\x8D\x90\x92\xE6W!\xB7^k\xFF{\x87\xDE\xA5V\x8B\xC9\xEF\x1A\xC0\xFA%\x93\xAD\x98\x8EI\x15Y\xA0\x16lN\xB0\x96\xEB\x85\xCC\xADG".force_encoding('ASCII-8BIT')
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "has user's public key" do
|
|
88
|
+
attrs[:public_key].must_equal "\x04*tP\xB7\xCE\x10\raoC\x98J\xC0\x8CT\x18f0D\xC4\xAD\xB6<\xBC~V#\x0Er\x1D8\xFB\x06}\xF7F\xAA9\xC1v\x83\x14\xB9\x834\x94\f\xAC\xE9<e\xE9\x96\#$\xDDJ\x17\x81\x89B\xD9\x03n".force_encoding('ASCII-8BIT')
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "has attestation certificate" do
|
|
92
|
+
attrs[:certificate].must_equal "0\x82\x02\x1C0\x82\x01\x06\xA0\x03\x02\x01\x02\x02\x04$\xDB\xAB@0\v\x06\t*\x86H\x86\xF7\r\x01\x01\v0.1,0*\x06\x03U\x04\x03\x13#Yubico U2F Root CA Serial 4572006310 \x17\r140801000000Z\x18\x0F20500904000000Z0+1)0'\x06\x03U\x04\x03\f Yubico U2F EE Serial 135032778880Y0\x13\x06\a*\x86H\xCE=\x02\x01\x06\b*\x86H\xCE=\x03\x01\a\x03B\x00\x04\x02\xB0\x94\xBE4}GyA\xC4w\x8E\xBE\xC5\xCAM\xED*G\x9F\xAA\x1Eo\xEC9\xAF\xEB\xDE\f p\xCB[\xD4\xBDi\xC9jx\xE3\xBF\x87Q\xFE\xB5y\e\x8D\xFA\xCA\xC2\x94\x01u\x1C\xB1W\xB9|\t\xE49\x1A6\xA3\x120\x100\x0E\x06\n+\x06\x01\x04\x01\x82\xC4\n\x01\x01\x04\x000\v\x06\t*\x86H\x86\xF7\r\x01\x01\v\x03\x82\x01\x01\x00\xA3c\xAE\x0E\x98:\xF3\v\xBA\xF1,\x8B-\xF3ZY\xBF\x1C\xBBJ\e\x0F\xCBh\xC4\x84U\x84\x90\xF6\x874Xe\xB8\xDB\x02i\xC3F\xE5S\x88L,V\a\xAF\x0E\xA2{\x90\xAC\x8C\xF1\xEFC\x1Fr\xAC\x18\x9D\xB2\x1C\x82I\x14\xBF\x17\x88\xA5Q\x1A3\xD0{L\x8E4d|\xE9\xF6\x1E\x15\x16\xA9\xA9\xB3n\x90\n@ a\xF6\x9A\xA4n\x12\xC52\xB9\x93\xF9B>\xFA\xAAL\xF9\xA3\xB6T\xB4\xDD\xDE\xF2\x92JT\x8F\xD5\x99\x95Q\r\xD4\xF7\xF4\xD9\xA4\xD5!\x93\x87<q\xC9\xB8~\x86\x85>\x9E-\xA7^\x8F\fm(0St\xD4\xEF\xDD^\x14\x96\xF8\xC39\x06\x10{\xD6\x8B\xD65\r\xAA\xD2\xC3x\x11\xEC\xA3\xCAC\xBC\x93\vs@\x97\xDE\xF6\x9Dh\x8D\x94U\fL\xFB\x18\xA9\xE2K\x86\xA2\xE5\xD8\x8FI\x98\x99\xA0\x9B\xCE[\x81\fSl\xAF9\r\xC8\xBD\xDE\x96\r\xF30\xCA\xCA\xBC\x05!\xA1\x83#\x95\x7F\xFE\xBC\xA5\x9C\xA9\v \xB1\r\t\xB5#\x1CX\xC2~\xBAg\x83".force_encoding('ASCII-8BIT')
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
|
2
|
+
require File.expand_path("../dummy/config/environment", __FILE__)
|
|
3
|
+
require "rails/test_help"
|
|
4
|
+
require "minitest/rails"
|
|
5
|
+
require 'devise'
|
|
6
|
+
|
|
7
|
+
Rails.backtrace_cleaner.remove_silencers!
|
|
8
|
+
# Load support files
|
|
9
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
|
10
|
+
require_relative 'dummy/app/models/user'
|
|
11
|
+
|
|
12
|
+
class ActiveSupport::TestCase
|
|
13
|
+
ActiveRecord::Migration.check_pending!
|
|
14
|
+
|
|
15
|
+
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
|
|
16
|
+
#
|
|
17
|
+
# Note: You'll currently still have to declare fixtures explicitly in integration tests
|
|
18
|
+
# -- they do not yet inherit this setting
|
|
19
|
+
fixtures :all
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class ActionController::TestCase
|
|
23
|
+
include Devise::TestHelpers
|
|
24
|
+
include Warden::Test::Helpers
|
|
25
|
+
Warden.test_mode!
|
|
26
|
+
|
|
27
|
+
def teardown
|
|
28
|
+
Warden.test_reset!
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def register_as user, registration
|
|
32
|
+
sign_in user
|
|
33
|
+
assert_equal registration.login, user
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def authenticate_as user, registration
|
|
37
|
+
sign_in user
|
|
38
|
+
assert_equal registration.login, user
|
|
39
|
+
controller.user_session['two_factor_auth_authenticated'] = Time.now
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
attr_reader :controller
|
|
43
|
+
end
|