Skip to content
This repository has been archived by the owner on Aug 26, 2022. It is now read-only.

Commit

Permalink
Reorganize and modernize spec to rspec expect format. 100% code cover…
Browse files Browse the repository at this point in the history
…age. Disable rspec should.
  • Loading branch information
grempe committed May 14, 2016
1 parent a276db9 commit 83c04b9
Show file tree
Hide file tree
Showing 7 changed files with 467 additions and 336 deletions.
4 changes: 4 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ Style/FormatString:

Style/MethodName:
Enabled: false

Style/NumericLiterals:
Enabled: false

12 changes: 6 additions & 6 deletions lib/sirp/sirp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ def hex_to_bytes(str)
[str].pack('H*').unpack('C*')
end

def num_to_hex(num)
hex_str = num.to_s(16)
even_hex_str = hex_str.length.odd? ? '0' + hex_str : hex_str
even_hex_str.downcase
end

def sha_hex(h, hash_klass)
hash_klass.hexdigest([h].pack('H*'))
end
Expand Down Expand Up @@ -101,11 +107,5 @@ def calc_H_AMK(xaa, xmm, xkk, hash_klass)
byte_string = hex_to_bytes([xaa, xmm, xkk].join('')).pack('C*')
sha_str(byte_string, hash_klass).hex
end

def num_to_hex(num)
hex_str = num.to_s(16)
even_hex_str = hex_str.length.odd? ? '0' + hex_str : hex_str
even_hex_str.downcase
end
end
end
141 changes: 141 additions & 0 deletions spec/authentication.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# encoding: utf-8
require 'spec_helper'

describe SIRP do
# Simulate actual authentication scenario over HTTP
# when the server is RESTful and has to persist authentication
# state between challenge and response.
#
context 'simulated authentication' do
before :all do
@username = 'leonardo'
password = 'icnivad'
@auth = SIRP::Verifier.new(1024).generate_userauth(@username, password)

# Simulate database persistance layer
@db = {
@username => {
verifier: @auth[:verifier],
salt: @auth[:salt]
}
}
end

it 'should authenticate with matching server and client params' do
client = SIRP::Client.new(1024)
verifier = SIRP::Verifier.new(1024)

# phase 1 (client)
aa = client.start_authentication

# phaase 1 (server)
v = @auth[:verifier]
salt = @auth[:salt]
bb = verifier.generate_B(v)
b = format('%x' % verifier.b)

# phase 2 (client)
client_M = client.process_challenge(@username, 'icnivad', salt, bb)

# phase 2 (server)
proof = { A: aa, B: bb, b: b, I: @username, s: salt, v: v }
server_H_AMK = verifier.verify_session(proof, client_M)
expect(server_H_AMK).to be_truthy

# phase 2 (client)
expect(client.verify(server_H_AMK)).to be true
end

it 'should not authenticate when a bad password is injected in the flow' do
client = SIRP::Client.new(1024)
verifier = SIRP::Verifier.new(1024)

# phase 1 (client)
aa = client.start_authentication

# phaase 1 (server)
v = @auth[:verifier]
salt = @auth[:salt]
bb = verifier.generate_B(v)
b = format('%x' % verifier.b)

# phase 2 (client)
client_M = client.process_challenge(@username, 'BAD PASSWORD', salt, bb)

# phase 2 (server)
proof = { A: aa, B: bb, b: b, I: @username, s: salt, v: v }
server_H_AMK = verifier.verify_session(proof, client_M)
expect(server_H_AMK).to be false

# phase 2 (client)
expect(client.verify(server_H_AMK)).to be false
end

it 'should authenticate when simulating a stateless server' do
username = @username

# START PHASE 1

# P1 : client generates A and begins auth
client = SIRP::Client.new(1024)
aa = client.start_authentication

# P1 : username and A are sent (client -> server)

# P1 : server finds user in DB
_user = @db[username]
expect(_user).not_to be_nil
v = _user[:verifier]
salt = _user[:salt]

# P1 : server generates B, saves A and B to DB
verifier = SIRP::Verifier.new(1024)
_session = verifier.get_challenge_and_proof(username, v, salt, aa)
expect(_session[:challenge][:B]).to eq verifier.B
expect(_session[:challenge][:salt]).to eq salt

# P1 : store proof to memory
_user[:session_proof] = _session[:proof]

# P1 : clear variables to simulate end of phase 1
verifier = username = v = bb = salt = nil

# P1 : server sends salt and B to client
client_response = _session[:challenge]

# P1 : client receives B and salt (server -> client)
bb = client_response[:B]
salt = client_response[:salt]

# START PHASE 2

# P2 : client generates session key
# at this point _client_srp.a should be persisted!
# calculate_client_key is stateful!
mmc = client.process_challenge(@username, 'icnivad', salt, bb)
expect(client.A).to be_truthy
expect(client.M).to eq mmc
expect(client.K).to be_truthy
expect(client.H_AMK).to be_truthy

# P2 : client sends username and M -> server
client_M = client.M

# P2 : server receives client M (client -> server)
_user = @db[@username]

# P2 : server retrives session from DB
proof = _user[:session_proof]
verifier = SIRP::Verifier.new(1024)
server_H_AMK = verifier.verify_session(proof, client_M)
expect(server_H_AMK).to be_truthy

# Now the two parties have a shared, strong session key K.
# To complete authentication, they need to prove to each other that
# their keys match.

expect(client.verify(server_H_AMK)).to be true
expect(client.K).to eq verifier.K
end
end
end
62 changes: 62 additions & 0 deletions spec/client_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# encoding: utf-8
require 'spec_helper'

describe SIRP do
# From http://srp.stanford.edu/demo/demo.html using 1024 bit SHA1 values.
context 'client' do
before :all do
@username = 'user'
@password = 'password'
@salt = '16ccfa081895fe1ed0bb'
@a = '7ec87196e320a2f8dfe8979b1992e0d34439d24471b62c40564bb4302866e1c2'
@b = '8143e2f299852a05717427ea9d87c6146e747d0da6e95f4390264e55a43ae96'
end

it 'should calculate A from random a' do
client = SIRP::Client.new(1024)
aa1 = client.start_authentication
expect(('%b' % aa1.to_i(16)).length).to be >= 1000

client = SIRP::Client.new(1024)
aa2 = client.start_authentication
expect(('%b' % aa2.to_i(16)).length).to be >= 1000

expect(aa1).not_to eq aa2
end

it 'should calculate A deterministicly from known @a' do
client = SIRP::Client.new(1024)
client.set_a(@a.to_i(16))
aa = client.start_authentication
expect(aa).to eq '165366e23a10006a62fb8a0793757a299e2985103ad2e8cdee0cc37cac109f3f338ee12e2440eda97bfa7c75697709a5dc66faadca7806d43ea5839757d134ae7b28dd3333049198cc8d328998b8cd8352ff4e64b3bd5f08e40148d69b0843bce18cbbb30c7e4760296da5c92717fcac8bddc7875f55302e55d90a34226868d2'
end

it 'should calculate client session (S) and secret (K)' do
client = SIRP::Client.new(1024)
client.set_a(@a.to_i(16))
aa = client.start_authentication

# Simulate server B
bb = '56777d24af1121bd6af6aeb84238ff8d250122fe75ed251db0f47c289642ae7adb9ef319ce3ab23b6ecc97e5904749fc42f12bb016ecf39691db541f066667b8399bfa685c82b03ad8f92f75975ed086dbe0d470d4dd907ce11b19ee41b74aee72bd8445cde6b58c01f678e39ed9cd6b93c79382637df90777a96c10a768c510'
mm = client.process_challenge(@username, @password, @salt, bb)

# Client keys
expect(client.S).to eq '7f44592cc616e0d761b2d3309d513b69b386c35f3ed9b11e6d43f15799b673d6dcfa4117b4456af978458d62ad61e1a37be625f46d2a5bd9a50aae359e4541275f0f4bd4b4caed9d2da224b491231f905d47abd9953179aa608854b84a0e0c6195e73715932b41ab8d0d4a2977e7642163be6802c5907fb9e233b8c96e457314'
expect(client.K).to eq '404bf923682abeeb3c8c9164d2cdb6b6ba21b64d'
end

it 'should verify true with matching server H_AMK' do
server_HAMK = 'abc123'
client = SIRP::Client.new(1024)
client.set_h_amk('abc123')
expect(client.verify(server_HAMK)).to be true
end

it 'should verify false with non-matching server H_AMK' do
server_HAMK = 'bbaadd'
client = SIRP::Client.new(1024)
client.set_h_amk('abc123')
expect(client.verify(server_HAMK)).to be false
end
end
end
Loading

0 comments on commit 83c04b9

Please sign in to comment.