An unofficial, mostly Bitwarden-compatible API server written in Ruby (Sinatra and ActiveRecord)
at master 115 lines 3.0 kB view raw
1#!/usr/bin/env ruby 2# 3# Copyright (c) 2018 joshua stein <jcs@jcs.org> 4# 5# Permission to use, copy, modify, and distribute this software for any 6# purpose with or without fee is hereby granted, provided that the above 7# copyright notice and this permission notice appear in all copies. 8# 9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16# 17 18require File.realpath(File.dirname(__FILE__) + "/../lib/rubywarden.rb") 19require "getoptlong" 20 21def usage 22 puts "usage: #{$0} -u user@example.com" 23 exit 1 24end 25 26username = nil 27 28begin 29 GetoptLong.new( 30 [ "--user", "-u", GetoptLong::REQUIRED_ARGUMENT ], 31 ).each do |opt,arg| 32 case opt 33 when "--user" 34 username = arg 35 end 36 end 37 38rescue GetoptLong::InvalidOption 39 usage 40end 41 42if !username 43 usage 44end 45 46@u = User.find_by_email(username) 47if !@u 48 raise "can't find existing User record for #{username.inspect}" 49end 50 51print "master password for #{@u.email}: " 52system("stty -echo") 53password = STDIN.gets.chomp 54system("stty echo") 55print "\n" 56 57unless @u.has_password_hash?(Bitwarden.hashPassword(password, @u.email, 58Bitwarden::KDF::TYPES[@u.kdf_type], @u.kdf_iterations)) 59 raise "master password does not match stored hash" 60end 61 62new_master = nil 63new_master_conf = nil 64new_master_hint = nil 65 66while new_master.to_s == "" || (new_master != new_master_conf) 67 print "new master password: " 68 system("stty -echo") 69 new_master = STDIN.gets.chomp 70 system("stty echo") 71 print "\n" 72 73 print "new master password (again): " 74 system("stty -echo") 75 new_master_conf = STDIN.gets.chomp 76 system("stty echo") 77 print "\n" 78 79 if new_master == new_master_conf 80 print "new master password hint (optional): " 81 system("stty -echo") 82 new_master_hint = STDIN.gets.chomp 83 system("stty echo") 84 print "\n" 85 else 86 puts "error: passwords do not match" 87 end 88end 89 90new_kdf_iterations = nil 91while new_kdf_iterations.to_s == "" 92 print "kdf iterations (currently #{@u.kdf_iterations}, default " << 93 "#{Bitwarden::KDF::DEFAULT_ITERATIONS[@u.kdf_type]}): " 94 new_kdf_iterations = STDIN.gets.chomp 95 96 if new_kdf_iterations.to_s == "" 97 new_kdf_iterations = Bitwarden::KDF::DEFAULT_ITERATIONS[@u.kdf_type] 98 break 99 elsif !(r = Bitwarden::KDF::ITERATION_RANGES[@u.kdf_type]). 100 include?(new_kdf_iterations.to_i) 101 puts "iterations must be between #{r}" 102 new_kdf_iterations = nil 103 else 104 new_kdf_iterations = new_kdf_iterations.to_i 105 break 106 end 107end 108 109@u.update_master_password(password, new_master, new_kdf_iterations) 110@u.password_hint = new_master_hint 111if !@u.save 112 puts "error saving new password" 113end 114 115puts "master password changed"