An unofficial, mostly Bitwarden-compatible API server written in Ruby (Sinatra and ActiveRecord)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

The Kdf parameter in 979849f82 is the KDF type, store it

0 is PBKDF2, others are coming later

Closes #66

+211 -86
+5
db/migrate/20180818212323_add_user_kdf_type.rb
··· 1 + class AddUserKdfType < ActiveRecord::Migration[5.1] 2 + def change 3 + add_column :users, :kdf_type, :integer, :default => 0, :null => false 4 + end 5 + end
+43 -11
lib/bitwarden.rb
··· 20 20 21 21 class Bitwarden 22 22 class InvalidCipherString < StandardError; end 23 + class Bitwarden::KDF 24 + PBKDF2 = 0 25 + 26 + TYPES = { 27 + 0 => PBKDF2, 28 + } 29 + TYPE_IDS = TYPES.invert 30 + 31 + DEFAULT_ITERATIONS = { 32 + PBKDF2 => 5000, 33 + } 34 + 35 + ITERATION_RANGES = { 36 + PBKDF2 => 5000 .. 1_000_000, 37 + } 38 + end 23 39 24 40 # convenience methods for hashing/encryption/decryption that the apps do, 25 41 # just so we can test against 26 42 class << self 27 - # pbkdf2 stretch a password+salt 28 - def makeKey(password, salt, kdf_iterations) 29 - PBKDF2.new(:password => password, :salt => salt, 30 - :iterations => kdf_iterations, 31 - :hash_function => OpenSSL::Digest::SHA256, 32 - :key_length => (256 / 8)).bin_string 43 + # stretch a password+salt 44 + def makeKey(password, salt, kdf_type, kdf_iterations) 45 + case kdf_type 46 + when Bitwarden::KDF::PBKDF2 47 + if !(r = Bitwarden::KDF::ITERATION_RANGES[kdf_type]).include?(kdf_iterations) 48 + raise "PBKDF2 iterations must be between #{r}" 49 + end 50 + 51 + PBKDF2.new(:password => password, :salt => salt, 52 + :iterations => kdf_iterations, 53 + :hash_function => OpenSSL::Digest::SHA256, 54 + :key_length => (256 / 8)).bin_string 55 + else 56 + raise "unknown kdf type #{kdf_type.inspect}" 57 + end 33 58 end 34 59 35 60 # encrypt random bytes with a key to make new encryption key ··· 52 77 end 53 78 54 79 # base64-encode a wrapped, stretched password+salt for signup/login 55 - def hashPassword(password, salt, kdf_iterations) 56 - key = makeKey(password, salt, kdf_iterations) 57 - Base64.strict_encode64(PBKDF2.new(:password => key, :salt => password, 58 - :iterations => 1, :key_length => 256/8, 59 - :hash_function => OpenSSL::Digest::SHA256).bin_string) 80 + def hashPassword(password, salt, kdf_type, kdf_iterations) 81 + key = makeKey(password, salt, kdf_type, kdf_iterations) 82 + 83 + case kdf_type 84 + when Bitwarden::KDF::PBKDF2 85 + # stretching has already been done in makeKey, only do 1 iteration here 86 + Base64.strict_encode64(PBKDF2.new(:password => key, :salt => password, 87 + :iterations => 1, :key_length => 256/8, 88 + :hash_function => OpenSSL::Digest::SHA256).bin_string) 89 + else 90 + raise "unknown kdf type #{kdf_type.inspect}" 91 + end 60 92 end 61 93 62 94 # encrypt+mac a value with a key and mac key and random iv, return a
+1
lib/db.rb
··· 21 21 class Db 22 22 def self.connect environment: 23 23 dbconfig = YAML.load(File.read('db/config.yml')) 24 + ActiveRecord::Base.dump_schema_after_migration = false 24 25 ActiveRecord::Base.establish_connection dbconfig[environment] 25 26 end 26 27 end
+16 -3
lib/routes/api.rb
··· 24 24 return validation_error("#{p} cannot be blank") 25 25 end 26 26 27 - iterations = User::DEFAULT_KDF_ITERATIONS 27 + kdf_type = User::DEFAULT_KDF_TYPE 28 + iterations = Bitwarden::KDF::DEFAULT_ITERATIONS[kdf_type] 28 29 29 30 if u = User.find_by_email(params[:email]) 30 31 iterations = u.kdf_iterations 32 + kdf_type = Bitwarden::KDF::TYPES[u.kdf_type] 31 33 end 32 34 33 35 { 34 - "Kdf" => 0, 36 + "Kdf" => Bitwarden::KDF::TYPE_IDS[kdf_type], 35 37 "KdfIterations" => iterations, 36 38 }.to_json 37 39 end ··· 44 46 return validation_error("Signups are not permitted") 45 47 end 46 48 47 - need_params(:masterpasswordhash, :kdfiterations) do |p| 49 + need_params(:masterpasswordhash, :kdf, :kdfiterations) do |p| 48 50 return validation_error("#{p} cannot be blank") 49 51 end 50 52 ··· 56 58 return validation_error("Invalid key") 57 59 end 58 60 61 + kdf_type = Bitwarden::KDF::TYPES[params[:kdf].to_i] 62 + if !kdf_type 63 + return validation_error("invalid kdf type") 64 + end 65 + 66 + if !Bitwarden::KDF::ITERATION_RANGES[kdf_type]. 67 + include?(params[:kdfiterations].to_i) 68 + return validation_error("invalid kdf iterations") 69 + end 70 + 59 71 begin 60 72 Bitwarden::CipherString.parse(params[:key]) 61 73 rescue Bitwarden::InvalidCipherString ··· 74 86 u.password_hash = params[:masterpasswordhash] 75 87 u.password_hint = params[:masterpasswordhint] 76 88 u.key = params[:key] 89 + u.kdf_type = Bitwarden::KDF::TYPE_IDS[kdf_type] 77 90 u.kdf_iterations = params[:kdfiterations] 78 91 79 92 # is this supposed to come from somewhere?
+6 -4
lib/user.rb
··· 20 20 self.table_name = "users" 21 21 #set_primary_key "uuid" 22 22 23 - DEFAULT_KDF_ITERATIONS = 5000 23 + DEFAULT_KDF_TYPE = Bitwarden::KDF::PBKDF2 24 24 25 25 before_create :generate_uuid_primary_key 26 26 before_validation :generate_security_stamp ··· 76 76 # a new key derived from the new password 77 77 78 78 orig_key = Bitwarden.decrypt(self.key, 79 - Bitwarden.makeKey(old_pwd, self.email, self.kdf_iterations), nil) 79 + Bitwarden.makeKey(old_pwd, self.email, 80 + Bitwarden::KDF::TYPES[self.kdf_type], self.kdf_iterations), nil) 80 81 81 82 self.key = Bitwarden.encrypt(orig_key, 82 - Bitwarden.makeKey(new_pwd, self.email, self.kdf_iterations)).to_s 83 + Bitwarden.makeKey(new_pwd, self.email, 84 + Bitwarden::KDF::TYPES[self.kdf_type], self.kdf_iterations)).to_s 83 85 84 86 self.password_hash = Bitwarden.hashPassword(new_pwd, self.email, 85 - self.kdf_iterations) 87 + self.kdf_type, self.kdf_iterations) 86 88 self.security_stamp = SecureRandom.uuid 87 89 end 88 90
+15 -6
spec/cipher_spec.rb
··· 4 4 5 5 describe "cipher module" do 6 6 before do 7 + User.all.delete_all 8 + 7 9 post "/api/accounts/register", { 8 10 :name => nil, 9 11 :email => "api@example.com", 10 12 :masterPasswordHash => Bitwarden.hashPassword("asdf", "api@example.com", 11 - User::DEFAULT_KDF_ITERATIONS), 13 + User::DEFAULT_KDF_TYPE, 14 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 12 15 :masterPasswordHint => nil, 13 16 :key => Bitwarden.makeEncKey( 14 17 Bitwarden.makeKey("adsf", "api@example.com", 15 - User::DEFAULT_KDF_ITERATIONS) 18 + User::DEFAULT_KDF_TYPE, 19 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 16 20 ), 17 - :kdf => 0, 18 - :kdfIterations => User::DEFAULT_KDF_ITERATIONS, 21 + :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 22 + :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 19 23 } 24 + last_response.status.must_equal 200 20 25 21 26 post "/identity/connect/token", { 22 27 :grant_type => "password", 23 28 :username => "api@example.com", 24 29 :password => Bitwarden.hashPassword("asdf", "api@example.com", 25 - User::DEFAULT_KDF_ITERATIONS), 30 + User::DEFAULT_KDF_TYPE, 31 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 26 32 :scope => "api offline_access", 27 33 :client_id => "browser", 28 34 :deviceType => 3, ··· 30 36 :deviceName => "firefox", 31 37 :devicePushToken => "" 32 38 } 39 + last_response.status.must_equal 200 33 40 34 41 @access_token = last_json_response["access_token"] 35 42 end ··· 84 91 85 92 # update 86 93 87 - ik = Bitwarden.makeKey("asdf", "api@example.com", User::DEFAULT_KDF_ITERATIONS) 94 + ik = Bitwarden.makeKey("asdf", "api@example.com", 95 + User::DEFAULT_KDF_TYPE, 96 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 88 97 k = Bitwarden.makeEncKey(ik) 89 98 new_name = Bitwarden.encrypt("some new name", k[0, 32], k[32, 32]).to_s 90 99
+14 -8
spec/cipherstring_spec.rb
··· 4 4 it "should make a key from a password and salt" do 5 5 b64 = "2K4YP5Om9r5NpA7FCS4vQX5t+IC4hKYdTJN/C20cz9c=" 6 6 7 - k = Bitwarden.makeKey("this is a password", "nobody@example.com", 5000) 7 + k = Bitwarden.makeKey("this is a password", "nobody@example.com", 8 + Bitwarden::KDF::PBKDF2, 5000) 8 9 Base64.strict_encode64(k).encode("utf-8").must_equal b64 9 10 10 11 # make sure key and salt affect it 11 - k = Bitwarden.makeKey("this is a password", "nobody2@example.com", 5000) 12 + k = Bitwarden.makeKey("this is a password", "nobody2@example.com", 13 + Bitwarden::KDF::PBKDF2, 5000) 12 14 Base64.strict_encode64(k).encode("utf-8").wont_equal b64 13 15 14 - k = Bitwarden.makeKey("this is A password", "nobody@example.com", 5000) 16 + k = Bitwarden.makeKey("this is A password", "nobody@example.com", 17 + Bitwarden::KDF::PBKDF2, 5000) 15 18 Base64.strict_encode64(k).encode("utf-8").wont_equal b64 16 19 end 17 20 18 21 it "should make a cipher string from a key" do 19 22 cs = Bitwarden.makeEncKey( 20 - Bitwarden.makeKey("this is a password", "nobody@example.com", 5000) 23 + Bitwarden.makeKey("this is a password", "nobody@example.com", 24 + Bitwarden::KDF::PBKDF2, 5000) 21 25 ) 22 26 23 27 cs.must_match(/^0\.[^|]+|[^|]+$/) ··· 25 29 26 30 it "should hash a password" do 27 31 #def hashedPassword(password, salt) 28 - Bitwarden.hashPassword("secret password", "user@example.com", 5000). 29 - must_equal "VRlYxg0x41v40mvDNHljqpHcqlIFwQSzegeq+POW1ww=" 32 + Bitwarden.hashPassword("secret password", "user@example.com", 33 + Bitwarden::KDF::PBKDF2, 5000).must_equal "VRlYxg0x41v40mvDNHljqpHcqlIFwQSzegeq+POW1ww=" 30 34 end 31 35 32 36 it "should parse a cipher string" do ··· 46 50 end 47 51 48 52 it "should encrypt and decrypt properly" do 49 - ik = Bitwarden.makeKey("password", "user@example.com", 5000) 53 + ik = Bitwarden.makeKey("password", "user@example.com", 54 + Bitwarden::KDF::PBKDF2, 5000) 50 55 ek = Bitwarden.makeEncKey(ik) 51 56 k = Bitwarden.decrypt(ek, ik, nil) 52 57 j = Bitwarden.encrypt("hi there", k[0, 32], k[32, 32]) 53 58 54 59 cs = Bitwarden::CipherString.parse(j) 55 60 56 - ik = Bitwarden.makeKey("password", "user@example.com", 5000) 61 + ik = Bitwarden.makeKey("password", "user@example.com", 62 + Bitwarden::KDF::PBKDF2, 5000) 57 63 Bitwarden.decrypt(cs.to_s, k[0, 32], k[32, 32]).must_equal "hi there" 58 64 end 59 65
+4 -2
spec/db_spec.rb
··· 9 9 u = User.new 10 10 u.email = "#{rand}@#{rand}.com" 11 11 u.password_hash = Bitwarden.hashPassword("blah", u.email, 12 - User::DEFAULT_KDF_ITERATIONS) 12 + User::DEFAULT_KDF_TYPE, 13 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 13 14 u.password_hint = nil 14 15 u.key = Bitwarden.makeEncKey( 15 - Bitwarden.makeKey("blah", u.email, User::DEFAULT_KDF_ITERATIONS), 16 + Bitwarden.makeKey("blah", u.email, User::DEFAULT_KDF_TYPE, 17 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 16 18 ) 17 19 u.culture = "en-US" 18 20 u.save.must_equal true
+19 -6
spec/folder_spec.rb
··· 4 4 5 5 describe "folder module" do 6 6 before do 7 + User.all.delete_all 8 + 7 9 post "/api/accounts/register", { 8 10 :name => nil, 9 11 :email => "api@example.com", 10 12 :masterPasswordHash => Bitwarden.hashPassword("asdf", "api@example.com", 11 - User::DEFAULT_KDF_ITERATIONS), 13 + User::DEFAULT_KDF_TYPE, 14 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 12 15 :masterPasswordHint => nil, 13 16 :key => Bitwarden.makeEncKey( 14 - Bitwarden.makeKey("adsf", "api@example.com", User::DEFAULT_KDF_ITERATIONS), 17 + Bitwarden.makeKey("adsf", "api@example.com", User::DEFAULT_KDF_TYPE, 18 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 15 19 ), 16 - :kdf => 0, 17 - :kdfIterations => User::DEFAULT_KDF_ITERATIONS, 20 + :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 21 + :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 18 22 } 23 + if last_response.status != 200 24 + raise last_response.inspect 25 + end 19 26 20 27 post "/identity/connect/token", { 21 28 :grant_type => "password", 22 29 :username => "api@example.com", 23 30 :password => Bitwarden.hashPassword("asdf", "api@example.com", 24 - User::DEFAULT_KDF_ITERATIONS), 31 + User::DEFAULT_KDF_TYPE, 32 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 25 33 :scope => "api offline_access", 26 34 :client_id => "browser", 27 35 :deviceType => 3, ··· 29 37 :deviceName => "firefox", 30 38 :devicePushToken => "" 31 39 } 40 + if last_response.status != 200 41 + raise last_response.inspect 42 + end 32 43 33 44 @access_token = last_json_response["access_token"] 34 45 end ··· 61 72 62 73 # update 63 74 64 - ik = Bitwarden.makeKey("asdf", "api@example.com", User::DEFAULT_KDF_ITERATIONS) 75 + ik = Bitwarden.makeKey("asdf", "api@example.com", 76 + User::DEFAULT_KDF_TYPE, 77 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 65 78 k = Bitwarden.makeEncKey(ik) 66 79 new_name = Bitwarden.encrypt("some new name", k[0, 32], k[32, 32]).to_s 67 80
+66 -33
spec/identity_spec.rb
··· 1 1 require_relative "spec_helper.rb" 2 2 3 3 describe "identity module" do 4 + before do 5 + User.all.delete_all 6 + end 7 + 4 8 it "should return successful response to account creation" do 5 9 post "/api/accounts/register", { 6 10 :name => nil, 7 11 :email => "nobody@example.com", 8 12 :masterPasswordHash => Bitwarden.hashPassword("asdf", 9 - "nobody@example.com", User::DEFAULT_KDF_ITERATIONS), 13 + "nobody@example.com", User::DEFAULT_KDF_TYPE, 14 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 10 15 :masterPasswordHint => nil, 11 16 :key => Bitwarden.makeEncKey( 12 - Bitwarden.makeKey("adsf", "nobody@example.com", 13 - User::DEFAULT_KDF_ITERATIONS) 17 + Bitwarden.makeKey("adsf", "nobody@example.com", User::DEFAULT_KDF_TYPE, 18 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 14 19 ), 15 - :kdf => 0, 16 - :kdfIterations => User::DEFAULT_KDF_ITERATIONS 20 + :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 21 + :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 17 22 } 18 23 last_response.status.must_equal 200 19 24 end ··· 24 29 :name => nil, 25 30 :email => "nobody2@example.com", 26 31 :masterPasswordHash => Bitwarden.hashPassword("asdf", 27 - "nobody2@example.com", User::DEFAULT_KDF_ITERATIONS), 32 + "nobody2@example.com", User::DEFAULT_KDF_TYPE, 33 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 28 34 :masterPasswordHint => nil, 29 35 :key => Bitwarden.makeEncKey( 30 36 Bitwarden.makeKey("adsf", "nobody2@example.com", 31 - User::DEFAULT_KDF_ITERATIONS) 37 + User::DEFAULT_KDF_TYPE, 38 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 32 39 ), 33 - :kdf => 0, 34 - :kdfIterations => User::DEFAULT_KDF_ITERATIONS, 40 + :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 41 + :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 35 42 } 36 43 if x == 0 37 44 last_response.status.must_equal 200 ··· 42 49 end 43 50 44 51 it "validates required parameters" do 45 - post "/api/accounts/register", { 52 + okh = { 46 53 :name => nil, 47 54 :email => "nobody3@example.com", 48 - :masterPasswordHash => "", 55 + :masterPasswordHash => Bitwarden.hashPassword("asdf", 56 + "nobody3@example.com", User::DEFAULT_KDF_TYPE, 57 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 49 58 :masterPasswordHint => nil, 50 59 :key => Bitwarden.makeEncKey( 51 60 Bitwarden.makeKey("adsf", "nobody3@example.com", 52 - User::DEFAULT_KDF_ITERATIONS) 61 + User::DEFAULT_KDF_TYPE, 62 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 53 63 ), 54 - :kdf => 0, 55 - :kdfIterations => User::DEFAULT_KDF_ITERATIONS, 64 + :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 65 + :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 56 66 } 67 + 68 + post "/api/accounts/register", okh.merge({ 69 + :masterPasswordHash => "", 70 + }) 57 71 last_response.status.wont_equal 200 58 72 59 - post "/api/accounts/register", { 60 - :name => nil, 61 - :email => "nobody3@example.com", 62 - :masterPasswordHash => Bitwarden.hashPassword("asdf", 63 - "nobody3@example.com", User::DEFAULT_KDF_ITERATIONS), 64 - :masterPasswordHint => nil, 73 + post "/api/accounts/register", okh.merge({ 65 74 :key => "junk", 66 - :kdf => 0, 67 - :kdfIterations => User::DEFAULT_KDF_ITERATIONS, 68 - } 75 + }) 76 + last_response.status.wont_equal 200 77 + 78 + post "/api/accounts/register", okh.merge({ 79 + :kdf => 100, 80 + }) 81 + last_response.status.wont_equal 200 82 + 83 + post "/api/accounts/register", okh.merge({ 84 + :kdfIterations => 5, 85 + }) 69 86 last_response.status.wont_equal 200 87 + 88 + post "/api/accounts/register", okh 89 + last_response.status.must_equal 200 70 90 end 71 91 72 92 it "actually creates the user account and allows logging in" do ··· 74 94 :name => nil, 75 95 :email => "nobody4@example.com", 76 96 :masterPasswordHash => Bitwarden.hashPassword("asdf", 77 - "nobody4@example.com", User::DEFAULT_KDF_ITERATIONS), 97 + "nobody4@example.com", User::DEFAULT_KDF_TYPE, 98 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 78 99 :masterPasswordHint => nil, 79 100 :key => Bitwarden.makeEncKey( 80 101 Bitwarden.makeKey("adsf", "nobody4@example.com", 81 - User::DEFAULT_KDF_ITERATIONS) 102 + User::DEFAULT_KDF_TYPE, 103 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 82 104 ), 83 - :kdf => 0, 84 - :kdfIterations => User::DEFAULT_KDF_ITERATIONS, 105 + :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 106 + :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 85 107 } 86 108 last_response.status.must_equal 200 87 109 ··· 89 111 u.uuid.wont_be_nil 90 112 u.password_hash.must_equal "PGC1vNJZUL3z5wTKAgpXsODf6KzIPcr0XCzTplceXQU=" 91 113 114 + post "/api/accounts/prelogin", { 115 + :email => "nobody4@example.com", 116 + } 117 + last_response.status.must_equal 200 118 + last_json_response["KdfIterations"].must_equal( 119 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 120 + 92 121 post "/identity/connect/token", { 93 122 :grant_type => "password", 94 123 :username => "nobody4@example.com", 95 124 :password => Bitwarden.hashPassword("asdf", "nobody4@example.com", 96 - User::DEFAULT_KDF_ITERATIONS), 125 + User::DEFAULT_KDF_TYPE, 126 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 97 127 :scope => "api offline_access", 98 128 :client_id => "browser", 99 129 :deviceType => 3, ··· 116 146 :name => nil, 117 147 :email => "nobody5@example.com", 118 148 :masterPasswordHash => Bitwarden.hashPassword("asdf", 119 - "nobody5@example.com", User::DEFAULT_KDF_ITERATIONS), 149 + "nobody5@example.com", User::DEFAULT_KDF_TYPE, 150 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 120 151 :masterPasswordHint => nil, 121 152 :key => Bitwarden.makeEncKey( 122 153 Bitwarden.makeKey("adsf", "nobody5@example.com", 123 - User::DEFAULT_KDF_ITERATIONS) 154 + User::DEFAULT_KDF_TYPE, 155 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 124 156 ), 125 - :kdf => 0, 126 - :kdfIterations => User::DEFAULT_KDF_ITERATIONS, 157 + :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 158 + :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 127 159 } 128 160 last_response.status.must_equal 200 129 161 ··· 131 163 :grant_type => "password", 132 164 :username => "nobody5@example.com", 133 165 :password => Bitwarden.hashPassword("asdf", "nobody5@example.com", 134 - User::DEFAULT_KDF_ITERATIONS), 166 + User::DEFAULT_KDF_TYPE, 167 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 135 168 :scope => "api offline_access", 136 169 :client_id => "browser", 137 170 :deviceType => 3,
+5 -3
spec/spec_helper.rb
··· 14 14 require File.realpath(File.dirname(__FILE__) + "/../lib/rubywarden.rb") 15 15 require "#{APP_ROOT}/lib/app.rb" 16 16 17 - #load 'db/schema.rb' 18 17 ActiveRecord::Migrator.up "db/migrate" 19 18 20 - include Rack::Test::Methods 19 + # in case migrations changed what we're testing 20 + [ User, Cipher, Device, Folder ].each do |c| 21 + c.send(:reset_column_information) 22 + end 21 23 22 - #ActiveRecord::Migration.maintain_test_schema! 24 + include Rack::Test::Methods 23 25 24 26 def last_json_response 25 27 JSON.parse(last_response.body)
+17 -10
spec/user_spec.rb
··· 5 5 USER_PASSWORD = "p4ssw0rd" 6 6 7 7 before do 8 - User.all.each{|u| u.destroy } 8 + User.all.delete_all 9 9 10 10 u = User.new 11 11 u.email = USER_EMAIL 12 - u.kdf_iterations = User::DEFAULT_KDF_ITERATIONS 12 + u.kdf_type = Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE] 13 + u.kdf_iterations = Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE] 13 14 u.password_hash = Bitwarden.hashPassword(USER_PASSWORD, USER_EMAIL, 14 - u.kdf_iterations) 15 + Bitwarden::KDF::TYPES[u.kdf_type], u.kdf_iterations) 15 16 u.password_hint = "it's like password but not" 16 17 u.key = Bitwarden.makeEncKey(Bitwarden.makeKey(USER_PASSWORD, USER_EMAIL, 17 - u.kdf_iterations)) 18 + Bitwarden::KDF::TYPES[u.kdf_type], u.kdf_iterations)) 18 19 u.save 19 20 end 20 21 ··· 23 24 u.email.must_equal USER_EMAIL 24 25 u.has_password_hash?( 25 26 Bitwarden.hashPassword(USER_PASSWORD, USER_EMAIL, 26 - User::DEFAULT_KDF_ITERATIONS)).must_equal true 27 + User::DEFAULT_KDF_TYPE, 28 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE])).must_equal true 27 29 28 30 u.has_password_hash?( 29 31 Bitwarden.hashPassword(USER_PASSWORD, USER_EMAIL + "2", 30 - User::DEFAULT_KDF_ITERATIONS)).wont_equal true 32 + User::DEFAULT_KDF_TYPE, 33 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE])).wont_equal true 31 34 end 32 35 33 36 it "encrypts and decrypts user's ciphers" do 34 37 u = User.find_by_email(USER_EMAIL) 35 38 36 39 mk = Bitwarden.makeKey(USER_PASSWORD, USER_EMAIL, 37 - User::DEFAULT_KDF_ITERATIONS) 40 + User::DEFAULT_KDF_TYPE, 41 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 38 42 39 43 c = Cipher.new 40 44 c.user_uuid = u.uuid ··· 56 60 u = User.find_by_email(USER_EMAIL) 57 61 58 62 mk = Bitwarden.makeKey(USER_PASSWORD, USER_EMAIL, 59 - User::DEFAULT_KDF_ITERATIONS) 63 + User::DEFAULT_KDF_TYPE, 64 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 60 65 61 66 c = Cipher.new 62 67 c.user_uuid = u.uuid ··· 75 80 :grant_type => "password", 76 81 :username => USER_EMAIL, 77 82 :password => Bitwarden.hashPassword(USER_PASSWORD + "2", USER_EMAIL, 78 - User::DEFAULT_KDF_ITERATIONS), 83 + User::DEFAULT_KDF_TYPE, 84 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 79 85 :scope => "api offline_access", 80 86 :client_id => "browser", 81 87 :deviceType => 3, ··· 86 92 last_response.status.must_equal 200 87 93 88 94 mk = Bitwarden.makeKey(USER_PASSWORD + "2", USER_EMAIL, 89 - User::DEFAULT_KDF_ITERATIONS) 95 + User::DEFAULT_KDF_TYPE, 96 + Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 90 97 91 98 c = Cipher.find_by_uuid(c.uuid) 92 99 u.decrypt_data_with_master_password_key(c.to_hash["Name"], mk).