Skip to content

Commit

Permalink
Merge branch 'dev-E2EE-2407' into 'master'
Browse files Browse the repository at this point in the history
feat(E2EE-2407): Add support of PrehashedAndEncryptedPassphrase in Enroll API

See merge request TankerHQ/sdk-ruby!179
  • Loading branch information
ntalfer committed Dec 20, 2024
2 parents 72d78ae + c53fdbd commit 4dc3f4d
Show file tree
Hide file tree
Showing 12 changed files with 735 additions and 342 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ spec/reports/
/vendor
/build
/conan
.tool-versions
TAGS
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"rubyLsp.bypassTypechecker": true
}
1 change: 1 addition & 0 deletions lib/tanker/c_tanker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ module CTanker
blocking_attach_function :tanker_http_handle_response, [CHttpRequest, CHttpResponse], :void

blocking_attach_function :tanker_prehash_password, [:string], CFuture
blocking_attach_function :tanker_prehash_and_encrypt_password, [:string, :string], CFuture

blocking_attach_function :tanker_authenticate_with_idp, [:session_pointer, :string, :string], CFuture
blocking_attach_function :tanker_free_authenticate_with_idp_result, [:pointer], :void
Expand Down
10 changes: 8 additions & 2 deletions lib/tanker/c_tanker/c_verification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ class CVerification < FFI::Struct
:preverified_email, :pointer,
:preverified_phone_number, :pointer,
:preverified_oidc, COIDCVerification,
:oidc_authorization_code_verification, COIDCAuthorizationCodeVerification
:oidc_authorization_code_verification, COIDCAuthorizationCodeVerification,
:prehashed_and_encrypted_passphrase, :pointer

TYPE_EMAIL = 1
TYPE_PASSPHRASE = 2
Expand All @@ -105,6 +106,7 @@ class CVerification < FFI::Struct
TYPE_E2E_PASSPHRASE = 8
TYPE_PREVERIFIED_OIDC = 9
TYPE_OIDC_AUTHORIZATION_CODE = 10
TYPE_PREHASHED_AND_ENCRYPTED_PASSPHRASE = 11

def initialize(verification) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity Not relevant for a case/when
super()
Expand Down Expand Up @@ -157,11 +159,15 @@ def initialize(verification) # rubocop:disable Metrics/MethodLength, Metrics/Cyc
verification.authorization_code,
verification.state
)
when Tanker::PrehashedAndEncryptedPassphraseVerification
@prehashed_and_encrypted_passphrase = CTanker.new_cstring verification.prehashed_and_encrypted_passphrase
self[:type] = TYPE_PREHASHED_AND_ENCRYPTED_PASSPHRASE
self[:prehashed_and_encrypted_passphrase] = @prehashed_and_encrypted_passphrase
else
raise ArgumentError, 'Unknown Tanker::Verification type!'
end

self[:version] = 8
self[:version] = 9
end
end

Expand Down
7 changes: 7 additions & 0 deletions lib/tanker/core/encryption.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ def self.prehash_password(str)
CTanker.tanker_prehash_password(str).get_string
end

def self.prehash_and_encrypt_password(password, public_key)
ASSERT_UTF8.call(password)
ASSERT_UTF8.call(public_key)

CTanker.tanker_prehash_and_encrypt_password(password, public_key).get_string
end

private

def encrypt_common(data, encryption_options)
Expand Down
12 changes: 12 additions & 0 deletions lib/tanker/core/verification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,16 @@ def initialize(e2e_passphrase)
@e2e_passphrase = e2e_passphrase
end
end

class PrehashedAndEncryptedPassphraseVerification < Verification
attr_reader :prehashed_and_encrypted_passphrase

def initialize(prehashed_and_encrypted_passphrase)
super()

ASSERT_UTF8.call(prehashed_and_encrypted_passphrase)

@prehashed_and_encrypted_passphrase = prehashed_and_encrypted_passphrase
end
end
end
2 changes: 2 additions & 0 deletions lib/tanker/core/verification_method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,6 @@ def ==(other)
end

class E2ePassphraseVerificationMethod < VerificationMethod; end

class PrehashedAndEncryptedPassphraseVerificationMethod < VerificationMethod; end
end
915 changes: 577 additions & 338 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package-mode = false
[tool.poetry.dependencies]
python = "^3.12"

tankerci = { version = "== 2024.4.5172", source = "gitlab" }
tankerci = { version = "== 2024.12.5467", source = "gitlab" }

[tool.poetry.dev-dependencies]
black = "24.3.0"
Expand Down
2 changes: 2 additions & 0 deletions spec/admin_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class AppConfig
attr_reader :oidc_config
attr_reader :trustchain_url
attr_reader :verification_api_token
attr_reader :enroll_passphrase_public_encryption_key

def self.safe_get_env(var)
val = ENV.fetch(var, nil)
Expand Down Expand Up @@ -62,6 +63,7 @@ def initialize
}
}
@oidc_config = OIDCConfig.new client_id, client_secret, issuer, provider_name, fake_oidc_issuer_url, users
@enroll_passphrase_public_encryption_key = AppConfig.safe_get_env('TANKER_ENROLL_PASSPHRASE_PUBLIC_ENCRYPTION_KEY')
end
end

Expand Down
48 changes: 48 additions & 0 deletions spec/core_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,54 @@
expect(Tanker::Core.prehash_password(input)).to eq expected
end

it 'fails to prehash and encrypt password when password is an empty string' do
password = ''
public_key = 'XJTPOJqdKJLCGwimhANxqrtiC2BOTmJUjJG7l4s5UhY='
expect { Tanker::Core.prehash_and_encrypt_password(password, public_key) }.to(raise_error) do |e|
expect(e).to be_a(Tanker::Error)
expect(e).to be_a(Tanker::Error::InvalidArgument)
expect(e.code).to eq(Tanker::Error::INVALID_ARGUMENT)
end
end

it 'fails to prehash and encrypt password when public key is an empty string' do
password = 'Happy birthday ! 🥳'
public_key = ''
expect { Tanker::Core.prehash_and_encrypt_password(password, public_key) }.to(raise_error) do |e|
expect(e).to be_a(Tanker::Error)
expect(e).to be_a(Tanker::Error::InvalidArgument)
expect(e.code).to eq(Tanker::Error::INVALID_ARGUMENT)
end
end

it 'fails to prehash and encrypt password when public key is not a base64-encoded string' do
password = 'Happy birthday ! 🥳'
public_key = '🎂'
expect { Tanker::Core.prehash_and_encrypt_password(password, public_key) }.to(raise_error) do |e|
expect(e).to be_a(Tanker::Error)
expect(e).to be_a(Tanker::Error::InvalidArgument)
expect(e.code).to eq(Tanker::Error::INVALID_ARGUMENT)
end
end

it 'fails to prehash and encrypt password when public key is invalid' do
password = 'Happy birthday ! 🥳'
public_key = 'fake'
expect { Tanker::Core.prehash_and_encrypt_password(password, public_key) }.to(raise_error) do |e|
expect(e).to be_a(Tanker::Error)
expect(e).to be_a(Tanker::Error::InvalidArgument)
expect(e.code).to eq(Tanker::Error::INVALID_ARGUMENT)
end
end

it 'can prehash and encrypt password' do
password = 'Happy birthday ! 🥳'
public_key = 'XJTPOJqdKJLCGwimhANxqrtiC2BOTmJUjJG7l4s5UhY='
paep = Tanker::Core.prehash_and_encrypt_password(password, public_key)
expect(paep).to be_kind_of String
expect(paep.length).to be > 0
end

it 'can share with a provisional user' do
message = 'No plugin updates available'
alice = Tanker::Core.new @options
Expand Down
73 changes: 72 additions & 1 deletion spec/verification_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ def extract_subject(id_token)
jbody['sub']
end

def create_paep_verification(passphrase)
public_key = AppConfig.instance.enroll_passphrase_public_encryption_key
paep = Tanker::Core.prehash_and_encrypt_password(passphrase, public_key)
Tanker::PrehashedAndEncryptedPassphraseVerification.new paep
end

RSpec.describe "#{Tanker} Verification" do
before(:all) do
Tanker::App.use_test_log_handler
Expand Down Expand Up @@ -320,6 +326,22 @@ def extract_subject(id_token)
tanker.free
end

it 'fails to register with a prehashed and encrypted passphrase' do
tanker = Tanker::Core.new @options
tanker.start @identity

paep_verification = create_paep_verification('p@assword')

expect { tanker.register_identity paep_verification }
.to(raise_error) do |e|
expect(e).to be_a(Tanker::Error)
expect(e).to be_a(Tanker::Error::InvalidArgument)
expect(e.code).to eq(Tanker::Error::INVALID_ARGUMENT)
end

tanker.free
end

it 'fails to verify with preverified email' do
email = 'mono@chromat.ic'

Expand Down Expand Up @@ -387,6 +409,28 @@ def extract_subject(id_token)
tanker2.free
end

it 'fails to verify with a prehashed and encrypted passphrase' do
passphrase = 'p@ssword'
tanker1 = Tanker::Core.new @options
tanker1.start @identity
tanker1.register_identity Tanker::PassphraseVerification.new(passphrase)
tanker1.free

tanker2 = Tanker::Core.new @options
tanker2.start @identity

paep_verification = create_paep_verification(passphrase)

expect { tanker2.verify_identity paep_verification }
.to(raise_error) do |e|
expect(e).to be_a(Tanker::Error)
expect(e).to be_a(Tanker::Error::InvalidArgument)
expect(e.code).to eq(Tanker::Error::INVALID_ARGUMENT)
end

tanker2.free
end

it 'sets verification method with preverified email' do
email = 'mono@chromat.ic'

Expand Down Expand Up @@ -454,6 +498,24 @@ def extract_subject(id_token)
expect(tanker2.status).to eq(Tanker::Status::READY)
tanker2.free
end

it 'fails to set verification method to prehashed and encrypted passphrase' do
passphrase = '42 is the Answer to the Ultimate Question of Life, the Universe, and Everything'
tanker = Tanker::Core.new @options
tanker.start @identity
tanker.register_identity Tanker::PassphraseVerification.new passphrase

paep_verification = create_paep_verification(passphrase)

expect { tanker.set_verification_method paep_verification }
.to(raise_error) do |e|
expect(e).to be_a(Tanker::Error)
expect(e).to be_a(Tanker::Error::InvalidArgument)
expect(e.code).to eq(Tanker::Error::INVALID_ARGUMENT)
end

tanker.free
end
end

describe 'session tokens' do
Expand Down Expand Up @@ -564,10 +626,12 @@ def extract_subject(id_token)

phone_number = '+33639982233'
email = 'mono@chromat.ic'
passphrase = 'Penny Lane is in my ears and in my eyes'
@server.enroll_user(@identity, [
Tanker::PreverifiedPhoneNumberVerification.new(phone_number),
Tanker::PreverifiedEmailVerification.new(email),
Tanker::PreverifiedOIDCVerification.new(@martine_subject, provider_id)
Tanker::PreverifiedOIDCVerification.new(@martine_subject, provider_id),
create_paep_verification(passphrase)
])

tanker1 = Tanker::Core.new @options
Expand All @@ -576,6 +640,8 @@ def extract_subject(id_token)
tanker2.start @identity
tanker3 = Tanker::Core.new @options
tanker3.start @identity
tanker4 = Tanker::Core.new @options
tanker4.start @identity

expect(tanker1.status).to eq(Tanker::Status::IDENTITY_VERIFICATION_NEEDED)
tanker1.verify_identity Tanker::EmailVerification.new(
Expand All @@ -595,9 +661,14 @@ def extract_subject(id_token)
tanker3.verify_identity Tanker::OIDCIDTokenVerification.new(@martine_id_token)
expect(tanker3.status).to eq(Tanker::Status::READY)

expect(tanker4.status).to eq(Tanker::Status::IDENTITY_VERIFICATION_NEEDED)
tanker4.verify_identity Tanker::PassphraseVerification.new(passphrase)
expect(tanker4.status).to eq(Tanker::Status::READY)

tanker1.free
tanker2.free
tanker3.free
tanker4.free
end
end

Expand Down

0 comments on commit 4dc3f4d

Please sign in to comment.