Как-то мне надо было добавить в админку просмотр списка паролей. База хранилась на сервере в формате KeePass (kdbx v2), сервер был на ноде — недолго думая, я взял первый попавшийся пакет и сделал. А потом понадобилось то же самое, но прямо у пользователя в браузере, без сервера. Ничего не нашлось. Первым желанием было форкнуть либу и заменить использование node api, но от первого просмотра кода желание пропало, решил сделать сам.



Под катом расскажу о проблемах, с которыми я столкнулся, и способах их решения

Чтение формата kdbx в браузере


Пришлось изучить формат kdbx, не описанный, к сожалению, нигде кроме исходников и этой небольшой статьи. Но зато он достаточно прост. Чтобы прочитать kdbx, нужно:
  1. прочитать бинарный заголовок (что в нём?)
  2. инициализировать алгоритм шифрования и генератор случайных значений (salsa-20)
  3. посчитать хэш пароля и ключа, получить credentials hash
  4. N раз (значение из заголовка) зашифровать хэш, получив мастер-ключ — это самая вычислительно трудоёмкая операция
  5. дешифровать данные
  6. проверить корректность расшифровки, сравнив блок данных с блоком из заголовка
  7. разGZIPать данные
  8. распарсить xml
  9. сгенерировать соль для защищённых полей
  10. по требованию, когда они нужны, теперь можно расXORить защищённые поля
  11. прочитать xml-метаданные (что в них?)
  12. прочитать группы и записи

Из алгоритмов там используются AES для симметричного шифрования и SHA256 для хеширования. Самая быстрая реализация для web, которую я нашёл — это asmCrypto, она написана на asm.js, работает быстрее остальных браузерных альтернатив и, что немаловажно, умеет собираться по кусочкам, включая только нужные модули с алгоритмами. Пропатчив её так, чтобы все циклы трансформации ключа происходили внутри asm.js, без дополнительного копирования данных, мне удалось достичь скорости в 4..7 раз меньшей, чем у native-реализации (если в KeePass поставить задержку в 1 секунду, откроется файл секунд за 6). С дефолтными настройками время открытия файла в среднем около 200мс. WebAssembly нас всех спасёт, а пока что вот так.
UPD: в комментариях подсказали про WebCrypto, теперь время открытия в браузерах с его поддержкой почти не уступает KeePass.

Для работы с gzip взял pako — он маленький, быстрый и MIT. Сравнительно с шифрованием, значимых затрат на декомпрессии не заметил.

В результате получилась kdbxweb, которая работает в node.js и современных браузерах. Из-за необходимости работать с бинарным форматом, в списке поддерживаемых браузеров нет старых версий. Библиотека+зависимости получились в 150кб.

Приложение


Итак, либа есть. А почему бы теперь не написать веб-интерфейс для неё? Хороший менеджер паролей для веба должен:
  • быть одиним файлом, чтобы можно было взять и, скажем, положить себе в дропбокс, на хостинг или даже на диск
  • не требовать абсолютно никакого сервера, ничего никуда не пересылать, работать в браузере
  • занимать меньше 1МБ (условно; важен порядок на самом деле)
  • уметь работать из файла и оффлайн
  • быстро загружаться
  • работать во всех браузерах
  • не хранить в памяти пароли в открытом виде
  • синкаться с дропбоксом
  • использовать существующий формат, не изобретать велосипед
  • так или иначе поддерживать все фичи формата
  • работать с несколькими файлами/базами одновременно
  • быть обратно-совместимым с оригинальным приложением
  • быть опен-сорсным

Большинство из этого удалось достичь.
Приложение написал на backbone+zepto (сначала хотел попробовать Angular2, но как-то не пошло, сгенерированный код самого фреймворка пока что получается размером более 1MB, наверное ещё слишком бета и потом будет лучше).
Версии браузеров, поддерживающих читалку формата, не так далеки от последних, поэтому кроссбраузерных костылей практически нет.

input type=password


Когда вы вводите текст в любой инпут, все вводимые значения даже с историей ввода могут сохраняться в памяти браузера бесконечно долго. И если в десктоп-приложении можно почистить память, то в браузере у вас нет контроля за памятью, занимаемой строками: когда GC захочет, тогда и почистит, при этом память он может занулить, а может и нет:



Некоторые приложения считают, что память можно не чистить даже после закрытия файла, хотя для приложений это, конечно, не так критично, как для веба:



Так не пойдёт, если в приложении ещё куда ни шло, то в браузере я бы в такой инпут мастер-пароль вводить не стал, поэтому пришлось сделать велосипед: при вводе символы XOR-ятся с рандомной последовательностью, подобранной так, чтобы на выходе получились символы некоего неиспользуемого блока unicode. Их и показывает инпут, думая, что это значение (всё равно там звёздочки), само значение при этом посимвольно сохраняется в буфер. Сложить значение с ключом, имея полный доступ к памяти, конечно, можно, но это уже другой уровень сложности:



Сохранение файлов


Сохранить файл, сгенерированный в браузере, не так-то просто, но нужно:



Есть FileSaver.js, но в нём засада в Safari, вот тред с интересными комментариями, где автор просит купить ему макбук. Почти все браузеры сейчас поддерживают
<a href="" download="file-name.ext">
Но только не Сафари. Сафари упорно не хочет ни понимать атрибут download (чувсвтва по этому поводу можно излить на webkit.org), ни скачивать Blob, жалуясь на то, что это якобы небезопасно. Но если законвертировать файл в base64, скачать его таки можно. Отправил автору патч, теперь в сафари FileSaver работает, но имя файла всё равно осталось Unknown. Если кто-то знает способ сделать лучше — расскажите, пожалуйста.

Десктоп


Работать, каждый раз скачивая файл, неудобно, собрал приложения на electron. Работает он шустро, API отлично описанное и интуитивно понятное, проблем с ним не заметил вообще:



Авто-апдейтер пока только под мак, но в будущем скорее всего его допишут и под другие ос. Как и для node-webkit, для электрона уже появились electron-builder и electron-packager, которыми я и воспользовался для сборки приложения и инсталлятора.
Конфиг для сборки инсталлятора, который делает dmg для mac os x и exe installer для windows (сборку deb для linux пока они не поддерживают):
{
  "osx" : {
    "title": "KeeWeb",
    "background": "../graphics/dmg-bg.png",
    "icon": "../graphics/app.icns",
    "icon-size": 80,
    "contents": [
      { "x": 438, "y": 344, "type": "link", "path": "/Applications" },
      { "x": 192, "y": 344, "type": "file" }
    ]
  },
  "win" : {
    "title": "KeeWeb",
    "icon": "graphics/app.ico"
  }
}


Electron добавляет приятную возможность получить путь открываемых файлов. Когда в приложение тащат файл или открывают его из инпута, в File добавляется свойство path, в котором содержится полный путь к файлу. Сохранить файл можно потом как обычно в ноде, fs.writeFile.



Иконки


В KeePass есть много иконок со странными названиями в енуме. Но во-первых, они немного устарели для 2015, а во-вторых, растровую графику использовать не хотелось, чтобы сохранить приложение маленьким. Пришлось составить соответствие вручную, к счастью, в font awesome нашлось абсолютно всё:



Цвета


Формат позволяет задать цвет записи и цвет фона, что KeePass и использует, однако я считаю возможность выбирать произвольный цвет текста и фона самому из колор-пикера не лучшим решением. Цвет записи нужен для отнесения записи к какой-любо логической группе (например, зелёные — деньги) и быстрого их поиска — чтобы отмеченные бросались в глаза и были быстро доступны.



Поэтому в приложении цвет используется как пометка звёздочкой, но вместо жёлтой звезды можно выбрать звезду любого цвета:



В случае, когда в импортитуемом файле есть цвета, алгоритм вычисляет ближайший по схожести тона, Hue. Обратно в KeePass цвета экспортируются как светлый фон выбранного оттенка.

Dropbox


Приятно удивился, что для подключения к дропбоксу из SPA через dropbox-js теперь не надо встраивать «зашифрованный» secret key в приложение, как это было раньше. Он не так и не научился отслеживать закрытие вкладки в popup-авторизации, но его легко научить (но надо патчить либу).

Копирование


API копирования заработало в браузерах не так давно: в chrome оно появилось летом, в firefox — осенью этого года, в IE было раньше, но с вопросом пользователю. В сафари его до сих пор нет, а копировать текст как-то хочется. Особенно в мобильном сафари. Но если копировать там и нельзя, то можно показать пользователю баббл Copy, сделав прозрачный input. В экран ещё раз тыкнуть придётся, но всё-таки уже не так болезненно, как выделение и копирование:



Поиск и корзина


Если человек открыл несколько файлов, скорее всего, он хочет, чтобы поиск был по ним всем. Поэтому поиск, фильтры, сортировка и корзина сделаны общими. Фильтры расположены в порядке востребованности пользователем. Сначала All Items (он даже доступен по шорткату Ctrl/Cmd-A, т.к. чаще всего хочется показать всё и просто найти в поиске необходимое), потом цвета-фавориты, за ними теги, структура и уже совсем внизу мусорка:



Поиск умеет работать по всему: поля, теги, вложения. В списке записей, при сортировке по разным полям, на второй строке показывается поле, по которому сортировали:



Формат поддерживает файлы без корзины, однако в UI корзина всё время есть. Это сделано затем, чтобы показать её отдельно от списка групп, одну на все файлы. В списке обычных групп корзине делать нечего, она должна быть особенной.

Название


Когда я рассказал о приложении на sourceforge в форуме KeePass, Dominik Reichl (разработчик KeePass) написал мне на почту, что получилось хорошо, предложил добавить приложение на страничку неофициальных клиентов, и попросил поменять название, что я и сделал.

О скучном


В приложении пока что встречаются баги, которые я постепенно вылавливаю. Используйте на свой страх и риск, делайте бэкапы. В крайнем случае есть экспорт в XML, где можно посмотреть, что пошло не так. Многое пока не сделано, но вообще идея приложения мне понравилась, я собираюсь его развивать и поддерживать.

Все компоненты приложения, как и оно само, доступны под MIT.

Ссылки


keeweb — веб-приложение
releases — десктоп-релиз
github — код
features — фичи на одной страничке в виде картинок
kdbxweb — читалка kdbx для браузеров и node.js

Комментарии (138)


  1. btd
    01.11.2015 10:00
    +1

    Хорошо у вас получилось я себе делал похожее для того чтобы перестать юзать chrome для сохранения паролей, но пока не допилил. Только я использовал апи node.js для крипто функций и gzip. В итоге собирал все с помощью browserify.


    1. Antelle
      01.11.2015 10:04

      Интереса ради — как оно по скорости в ноде и браузере? Если сделать в keepass файл и поставить вычисления длиной в 1сек — за сколько откроется?


      1. btd
        01.11.2015 10:25

        Я точных замеров не проводил, но чтение, изменение и сохранение базы (без записи на диск, только преобразование к buffer), субъективно в node.js в 3-4 раза быстрее (в ноде <1сек, в браузере ~3сек).


        1. Antelle
          01.11.2015 10:37

          Это в стандартной конфигурации с 6000 циклами? У меня в этом случае 200мс и в ноде, и в браузере.


          1. btd
            01.11.2015 10:44

            Нет, у меня база с 50000 циклами, не помню в чем ее создавал.


            1. Antelle
              01.11.2015 10:46
              +1

              Понятно, секунда задержки — это где-то 3-10млн, в зависимости от машины. Проверил у себя 50k, открылось меньше чем за секунду.


  1. 121212121
    01.11.2015 10:58
    +1

    Напишите Доминику, пусть ссылочку добавит.


    1. Antelle
      01.11.2015 10:59
      +3

      Предпоследний абзац статьи, «Название» :) Воскресенье, утро, ага.


  1. artspb
    01.11.2015 12:03
    +1

    Круто, спасибо! Я джва года этого ждал :) Когда-то тоже пытался соорудить нечто подобное на GWT, научился открывать базу в браузере, но на UI мне стало скучно и я забил.


    1. artspb
      01.11.2015 12:55
      +1

      Пара вопросов:
      1. А почему вы ограничиваете расширения открываемых файлов? Формально, расширение может быть любым, тем более для ключа.
      2. Не разобрался, приложение может открывать базы, у которых одновременно задан пароль и ключ? Интуитивно не заработало…
      3. Не думали реализовать возможность брать ключ из того же Dropbox, но уже без ограничения на Apps/KeeWeb?


      1. Antelle
        01.11.2015 13:04
        +1

        1. Kdbx обычно у всех называются kdbx, здесь нет смысла снимать ограничение. Ключ сейчас ограничен, потому что непонятно, что предлагается выбрать (это такая подсказка пользователю). Вобщем, я понял, кнопка с ключом интуитивно не понятна, надо придумать, что с ней сделать, и убрать ограничение. Подумаю, спасибо.
        2. может, надо выбрать базу, нажать на кнопочку с ключом (справа от поля), выбрать файл, ввести пароль и нажать enter
        3. про ключ в дропбоксе есть в todo; без ограничения на папку приложения — думал, как это лучше сделать. В том же приложении (всмысле приложение — dropbox app) не хочется давать много прав (могут и аппрув не дать), наверное сделаю ещё одно приложение, которое будет иметь доступ ко всему Dropbox пользователя, и для тех, кто хочет, можно будет выбрать такой вариант.


        1. artspb
          01.11.2015 13:36

          1. На счет kdbx согласен. А по поводу ключа: все-таки KeePass пользуются обычно относительно продвинутые пользователи, и уж тем более, если они задали ключ. Сложности с выбором файла быть не должно, кмк.
          2. Кнопка интуитивно понятна, но не работает. База, ключ, пароль «test». Пробовал сначала вводить пароль, потом указывать ключ, и наоборот — не открывает. ЧЯДНТ?
          3. Звучит разумно.


          1. Antelle
            01.11.2015 13:50
            +2

            1. согласен, ограничение уберу. может, перенесу кнопку ключа под пароль, а на её месте сделаю стрелочку-enter, сейчас из-за этого может быть путаница.
            2. скорее всего — баг, спасибо, сегодня вечером скачаю вашу базу и посмотрю
            3. там ещё есть chooser, на который аппрув не нужен, может с ним сделаю, погуглю ещё, как у других.


          1. Antelle
            01.11.2015 22:30
            +1

            Я открыл для себя, что ключи в KeePass бывают не только в формате XML (которые генерит KeePass), а ещё и простыми файлами, от которых в этом случае надо посчитать хэш. Спасибо, добавлю их поддержку. А ещё понял, что надо сделать обработку ошибок лучше: это у вас тестовый файл, а так базу с паролями-то не попросишь у пользователя на проверку; сделаю.


          1. Antelle
            04.11.2015 10:41
            +1

            Теперь этот ключ открывается, и ограничения нет, спасибо.


            1. artspb
              05.11.2015 23:03
              +1

              Вам спасибо! Проверил — все работает так, как надо. Кстати, было бы классно добавить хоткей Cmd+B, который копирует логин в буфер. И, может быть, Cmd+U, который открывает ссылку в новом окне.


              1. Antelle
                05.11.2015 23:39
                +1

                Хоткеи такие сейчас не используются — добавлю, пусть будут, будет удобнее. Делаю новый ui для открытия, скоро он станет понятнее:


                1. artspb
                  06.11.2015 23:57
                  +1

                  Кстати. Было бы классно, если бы пароль в поле ввода можно было выделить с помощью Cmd+A. Объясню. Часто бывает, что пароль набираешь быстро и опечатываешься. Причем знаешь, что была опечатка, но точно не знаешь, сколько символов назад. Привычка — Cmd+A, Del и все сначала. Сейчас это не работает, приходится удаляться посимвольно.


                  1. Antelle
                    07.11.2015 00:29
                    +1

                    Должно выделяться, у меня работает и в хроме, и в мак-приложении. А в какой версии такой баг?
                    Я кажется понял, вы про мастер-пароль. Да, точно, сделаю обязательно.


                    1. artspb
                      07.11.2015 00:33

                      KeeWeb v0.1.1, Mac OS X 10.11, Chrome 46.0.2490.80 (64-bit). Может быть, конечно, что я просто как-то не так собрал.


                    1. artspb
                      07.11.2015 00:34

                      Если что, я пароль к самой базе, а не сохраненные в ней пароли имею в виду.


                      1. Antelle
                        07.11.2015 00:35
                        +1

                        Нашёл, исправил, 0.2 будет норм, спасибо.


        1. Kolonist
          01.11.2015 13:57

          Kdbx обычно у всех называются kdbx, здесь нет смысла снимать ограничение
          У меня файл имеет другое расширение. Не вижу никакого смысла в ограничении по расширению, зачем оно?


          1. Antelle
            01.11.2015 14:06

            Как выше написал, непонятно, что выбирать. Переделаю форму, станет понятно, ограничение будет не нужно, уберу


  1. ANTPro
    01.11.2015 12:43
    +2

    Под Windows чертовски кривой инсталятор. Как можно было его собрать без:

    !insertmacro MUI_PAGE_DIRECTORY
    
    ...
    
    !insertmacro MUI_UNPAGE_CONFIRM
    

    ?


    1. Antelle
      01.11.2015 12:45

      Я не совсем понимаю, что это значит. Напишите, пожалуйста, баг в electron-builder, проект вроде бы живой и активно поддерживается. А ещё они ассоциации с типами файлов пока так и не научились делать…


      1. ANTPro
        01.11.2015 14:51

        Я добавил в шаблон поддержку нескольких языков, юникод и несколько страниц. Могу сделать и ассоциации только название параметра нужно.
        Попробуйте этот шаблон: https://github.com/ANTPro/electron-builder/blob/master/templates/installer.nsi.tpl
        если соберется, то сделаю патч.


        1. Antelle
          01.11.2015 22:54

          Не собралось:

          c:\Projects\keeweb>electron-builder KeeWeb-win32-ia32 --platform=win --out=. --config=./util/electron-builder.json
          - Running electron-builder 2.0.2
          - Starting creation of installer for ?win
          makensis: Processing config: C:\Program Files (x86)\NSIS\nsisconf.nsh
          
          makensis: Processing script file: "C:\Users\Antelle\AppData\Local\Temp\11jjzaeuvakgwsg4gs848installer.nsi" (ACP)
          Unicode expects 1 parameters, got 4.
          Usage: Unicode true|false
          Error in script "C:\Users\Antelle\AppData\Local\Temp\11jjzaeuvakgwsg4gs848installer.nsi" on line 5 -- aborting creation process
          
          Finished makensis with code 1


          1. ANTPro
            02.11.2015 10:41

            Похоже используется старый NSIS, а нужен NSIS 3.0. Я уже сделал патч.


  1. Kolonist
    01.11.2015 13:59

    А почему вы выбрали electron, а не nw.js?


    1. Antelle
      01.11.2015 14:10

      Давно хотелось попробовать и изучить (с первого взгляда очень понравился), а с nw.js дело я уже имел.


      1. Kolonist
        01.11.2015 14:18
        +7

        И что в итоге? electron себя оправдал? Какие плюсы/минусы в сравнении с nw.js?


        1. Antelle
          01.11.2015 21:58
          +1

          На первый взгляд, примерно то же самое. Детально я пока интересного рассказать могу не много, потому что приложение собрал только недавно и ещё в подробности не вдавался. Поживу с приложением, поиграюсь, поизучаю — если будет что рассказать, напишу пост, что и как. Из первого непонравившегося в электроне — отсутствие поддержки winxp и авто-апдейр только под мак.


      1. zvulon
        01.11.2015 16:23
        +4

        присоединяюсь к вопросу electron vs nw.js


  1. anmipo
    01.11.2015 15:06

    Ещё есть BrowsePass — существенно попроще, но исторически раньше :)

    А зачем в TODO указаны mobile apps? Их же и так на каждую платформу хватает.


    1. btd
      01.11.2015 15:52

      А что лучше использовать для android?


      1. anmipo
        01.11.2015 17:48

        KeePassDroid или KeePass2Android, на вкус и цвет.


        1. 121212121
          01.11.2015 20:05

          Лучше первое.
          KeePass2Android изменяет базу и ее потом не открыть другими средствами.
          Особенно это актуально, если синкается база на нескольких машинах (а именно так и должно быть, чтоб не потерять базу).


          1. skobkin
            01.11.2015 20:22

            Не сталкивался с этим. Правда, я не меняю записи на смарте.


    1. Antelle
      01.11.2015 22:11

      Есть, я даже рассматривал его как вариант форка, но передумал, слишком много переделок.
      Mobile apps указаны, потому что существующие под iOS меня не устраивают (например, нет синка с дропбоксом, это совсем грустно), а хочется уже переползти на keepass, потому что 1password стал требовать с меня $60, при том что я сначала купил приложение за 60, а потом обновился за 20. Там посмотрю, если с PhoneGap оно быстро и просто не полетит, про приложения я скорее всего передумаю, а если полетит — и хорошо.


      1. isden
        02.11.2015 12:03

        > 1password стал требовать с меня $60

        Стойте, а в честь чего он стал требовать еще бабло? Там же разовая оплата софта?


        1. Antelle
          02.11.2015 12:07

          За апдейты просят: багфиксы, секьюрити фиксы, новые операционки. Нет, я не говорю, что это должно быть бесплатным, но 60 баксов — это перебор.


          1. isden
            02.11.2015 12:12

            За апдейты у меня вроде бы не просили еще, в апп сторе они приходят периодически. За другую операционку да, это печально :(


            1. Antelle
              02.11.2015 12:14

              О, вы ещё не попали на эту их замануху. Я уже за 4 (кажется) года попал. Раз в год-два выходит новая мажорная версия, апдейт до неё стоит денег. А старая — да, ваша навечно, но больше не обновится.


              1. isden
                02.11.2015 12:17

                Хм, я покупал изначально v4.4.1 (сейчас инвойс поднял) год с лишним назад. Сейчас стоит 5.4, все обновлялось через апп стор, ниразу дополнительно не платил.


                1. Antelle
                  02.11.2015 12:19

                  У меня был десктоп, mac+windows, страничка проверки возможности апдейта по ключу говорит, что обновиться со скидкой уже нельзя. Мобильный да, кажется обновился в этот раз.


                  1. isden
                    02.11.2015 12:36

                    А, у меня только мак-версия, возможно обновление виндовой хотело денег.
                    К слову, фича-реквест вам — поле для otp, как в 1pass. Фича весьма удобная.


      1. steamoor
        03.11.2015 10:15

        1password for iOS бесплатен: https://support.1password.com/pro-features-faq/
        платить надо только за Pro версию.


      1. steamoor
        03.11.2015 21:18

        я ждал официального аннонса, прежде чем постить это здесь:)
        https://teams.1password.com/

        наша команда наконец то объявила о доступности public beta.

        (Да, я на них работаю и в этой рекламе есть капелька моего шкурного интереса, НО (!) я очень горд, что этот проект готов для публичного использования, последние два года прошли не зря! :) )


  1. Lockal
    01.11.2015 17:11

    Самая быстрая реализация для web, которую я нашёл — это asmCrypto

    Тут пишут, что WebCrypto API в разы (если не на порядок) быстрее asmCrypto.


    1. Antelle
      01.11.2015 22:14

      Попробую обязательно, спасибо что упомянули. Может кстати получиться и не быстрее, потому что здесь задача в том чтобы сделать много итераций шифрования ключа, а не просто последовательно обработать блоб. Проверим, если полетит, интегрирую.


    1. Antelle
      01.11.2015 23:47

      Не взлетело, причём сурово не взлетело, получается что webcrypto медленнее в моём случае в 50 раз. Если кто разбирается в webcrypto — может быть, я что-то делаю не так и можно быстрее? Вот код (на jsbin не положить, secure origin нужен).


      1. Antelle
        01.11.2015 23:59

        Что-то в воскресенье плохо думается под вечер. Вот jsbin. На 10000 циклов от 500мс с webcrypto, а надо 1 000 000 циклов в секунду. Если покажете что не так, буду благодарен.


        1. anmipo
          02.11.2015 00:32

          Я не знаком ни с тем, ни с другим, но в коде для WebCrypto на каждый раунд добавляются накладные расходы на вызов webCrResult, тогда как в asmCrypto transformRounds передаётся куда-то глубже, поближе к коду шифрования.


          1. Antelle
            02.11.2015 00:39

            Да, я как раз писал об этом: asmCrypto я пропатчил таким образом, чтобы количество циклов поддерживалось внутри библиотеки т.е. совсем-совсем близко к asm.js-шифрованию, без копирования буферов. В webcrypto вряд ли вообще когда-нибудь поддержат такой параметр, потому что он странный и в общем случае ненужный. Скорее всего накладные расходы на маршалинг в webcrypto для такой задачи оказались слишком велики. Поэтому таки ждём WASM, он спасёт производительность веба.


            1. anmipo
              02.11.2015 00:59

              К тому же, сравниваются разные режимы AES. Насколько я понимаю, CBC требует больше операций, чем ECB. Вот здесь предлагают варианты как эмулировать KeePass-овский ECB посредством WebCrypto-вского CBC.


              1. Antelle
                02.11.2015 01:07

                Да, ECB самый быстрый шифр (фидбека-то нет). Но если эмулировать другим режимом — затраты на фидбек всё равно в алгоритме будут, просто он не будет учитываться. Я сейчас собрал не рабочий вариант, а прототип для оценки производительности, в рабочем варианте, конечно, надо эмулировать ECB, чтобы шифровалось правильно.
                На два потока разделить можно (равно как можно и в asmCrypto). Важен порядок: у asmCrypto 10мс, у WebCrypto — 500. Пока что не вижу возможности его использования для этой задачи.


                1. anmipo
                  02.11.2015 01:31
                  +1

                  Я о том, что если сравнивать производительность — то в одинаковых условиях. А то в вышеприведённом тесте asmCrypto работает в заведомо более лёгком режиме, чем WebCrypto.

                  Плюс, накладные расходы в WebCrypto можно сократить, делая каждым вызовом эдак с сотню-тысячу раундов как описано во втором ответе на StackOverflow.

                  Если WebCrypto и правда в разы быстрее, как сказал Lockal, усилия могут окупиться, особенно на мобильных девайсах (у меня некоторые юзеры по 10 млн раундов ставят-с...)


                  1. Antelle
                    02.11.2015 01:40

                    Про несколько вызовов в одном — это как раз то, чего хотелось. Попробую завтра.


                  1. Antelle
                    02.11.2015 08:20
                    +1

                    WebCrypto обработал 10M раундов за 1-2 сек способом из второго ответа на SO. Добавлю поддержку обязательно, спасибо большое. Если делать это в 2 потока — можно открыть даже быстрее десктопа.


                    1. vibornoff
                      03.11.2015 12:59
                      +2

                      Спасибо, что такой пиар моей работе сделали, я автор asmcrypto.

                      Что касается webcrypto и AES, чтобы не ходить по граблям, взгляните на мой другой проект, github.com/vibornoff/webcrypto-shim, — там исправлены косяки реализаций в IE и Safari.

                      К сожалению, IE < 11 и Safari < 8 не имеют реализации webcrypto api, поэтому придется остаться на asmcrypto если хотите большей поддрежки старых браузеров.


                      1. Antelle
                        03.11.2015 13:26
                        +1

                        Спасибо! Посмотрю, что за косяки, с webcrypto я пока не работал, поэтому скорее всего тоже напоролся бы. Да, конечно asmCrypto оставлю пока как fallback.


            1. anmipo
              02.11.2015 01:01

              Ну и поскольку шифруются половинки ключа, это дело отлично делится на два параллельных потока ;)


    1. steamoor
      03.11.2015 21:23

      наш продукт использует WebCrypto, очень быстро (с большим количеством итераций).

      Проблема во всех этих реализациях — их поддержке браузерами.


    1. Antelle
      04.11.2015 10:27
      +2

      Сделал WebCrypto, время открытия теперь, если он поддерживается, практически как в KeePass. Опытным путём подобрал количество операций, производимых за один вызов WebCrypto, это где-то 10 000 — 100 000.

      график


      1. alexan
        04.11.2015 13:07

        Интересно было бы на код взглянуть, как вы это сделали.
        Сколько в итоге итераций может сделать браузер (на среднем компе) за секунду, на чистом js?


        1. Antelle
          04.11.2015 13:17

          Вот код, у меня результаты такие: MacBook Pro 2012, 2.5 GHz Intel Core i5 x2 — 11Mrounds/sec, Windows 8, 3.2 GHz Intel Core i7 x4 — 17Mrounds/sec (проверял на Chrome).
          Демо на jsbin, можете проверить у себя, 18M итераций


          1. alexan
            04.11.2015 20:11

            17M 2.2 сек.
            Но не разглядел сходу в коде, это pbkdf алгоритм или нет?


            1. Antelle
              04.11.2015 21:44

              Нет, это алгоритм, используемый KeePass для получения мастер-ключа. Много раз шифруется блок AES-ECB, что аналогично длинной нулевой последовательности AES-CBC.


              1. alexan
                05.11.2015 11:21

                Ясно.
                Но есть ощущение, что таким образом можно делать хороший ключ из (относительно) несложного пароля. Но найденные до ныне js решения pbkdf2 дают не более 10^4 итераций за 1-2 секунды.

                Есть мысли как этот алгоритм для этих целей прикрутить?


                1. Antelle
                  05.11.2015 11:51

                  AES-ом заменить PBKDF2 не получится. А зачем? В WebCrypto есть встроенная поддержка pbkdf2, она нативная и должна работать быстрее.


                  1. alexan
                    05.11.2015 12:05

                    Увы. Вот тут люди делали, как-то мало у них итераций.


                    1. Antelle
                      05.11.2015 12:11

                      А сколько у них получилось? Сам алгоритм ещё сложнее, т.е. 1M pbkdf не получится, даже нативные реализации столько не умеют.


                      1. alexan
                        05.11.2015 12:21

                        Там 1000. Встречал ещё 10000.
                        Интересно какой нынче максимум у нативных за 1-2 сек. Надо ещё покопаться.


                        1. Antelle
                          05.11.2015 12:36

                          Как-то странно. 1M, 1.7s


                          1. alexan
                            05.11.2015 12:50

                            И правда странно.
                            Вот тут ещё пишут что 1М это «annoyingly long». Может ещё под мобильные железки ориентируются.


                            1. Antelle
                              05.11.2015 12:59

                              Смотря в каких целях ещё использовать. Если при открытии странички, или ещё какой-то быстрой операции, где пользователт не предполагает ожидание, то на слабом компе — 1M=3с — это и правда annoingly long, а 100k=300ms — you can notice.


  1. smoked
    01.11.2015 18:28

    Будет ли возможность использовать свои загруженные иконки?


    1. 121212121
      01.11.2015 19:56

      У самого KeePass есть плагин Download favicons как то так называется и он позволяет загружать любые фавиконки. Да что там, в самом Кепасе можно загружать и назначать любые картинки, просто этот плагин частенько упрощает процесс.
      Типа того получится https://i.gyazo.com/1c8ba175e23a963e2bb63f41819d9519.png


    1. Antelle
      01.11.2015 22:16

      Будет обязательно, в TODO у меня это есть, фича полезная, фавиконы для сайтов нужны обязательно. В читалке поддержка иконок есть, нет её только в UI. Засада в чтении формата ico под маком и линкусом, придумаю что-нибудь, как более важное порешаю.


      1. anmipo
        01.11.2015 22:40

        Судя по названию PwCustomIcon.m_pbImageDataPng, в базе иконки хранятся в PNG, так что показывать существующие можно без проблем.


        1. Antelle
          01.11.2015 23:05

          Значит, ещё проще сделать будет, отлично.


  1. TheSteelRat
    01.11.2015 20:06
    +2

    А вот и появился стоящий повод перейти с kdb на kdbx. Спасибо за приложение :).


  1. synchrone
    01.11.2015 21:06
    +2

    Интересно что решили встроить поддержку Dropbox.
    Может тогда уж и протокол KeePassHttp запилить, чтобы интеграция с ChromeIPass \ PassIFox тоже была? Это всё же самый популярный плагин.


    1. Antelle
      01.11.2015 22:20
      +1

      Спасибо за идею, добавлю в TODO, выглядит совсем просто, сделаю. Правда получится только для десктоп-приложения, конечно же.


      1. pletinsky
        05.11.2015 05:23

        Еще здорово было бы поддержку OneDrive. Не все пользуются дропбоксом. Тем более теперь, когда первый в виндовсе по умолчанию стоит.

        Без автозаполнения полей браузера тяжко. А что мешает сделать поддержку ChromeIPass в браузере? Можно же тоже его запихать в фоновую страницу собственного браузерного плагина. Тогда и виндовс приложение не понадобится.


        1. Antelle
          05.11.2015 08:49

          Есть такое, когда-нибудь будет. Если я правильно понял, плагин подключается к http-серверу, поднимаемому из приложения. Вот из веба такой сервер не поднять, там надо по-другому интегрироваться. Посмотрю потом про интеграцию с плагинами, как лучше и правильнее. Два плагина в браузере точно ставиться не будет, это очень плохой экспериенс.


  1. Psychosynthesis
    02.11.2015 00:20
    +1

    По моему классная идея, с удовольствием заменил бы приложение одним html-файлом.

    Сразу к вам фич-реквест: было бы крайне удобно, если бы оно как-то запоминало последний открытый файл, если это возможно сделать вообще.

    Перешёл бы на вас, когда вы доберётесь второй-третьей версии.


    1. Antelle
      02.11.2015 00:43

      Десктоп-приложения запоминают. Веб тоже будет, когда будет реализовано оффлайн-хранилище, чтобы базу из Дропбокса можно было бы сохранить в indexedDb, открыть оффлайн и засинкать, когда появится сеть — туда же будут сохраняться и открытые из файлов. Пока что прорабатываю UX для такого хранилища, чтобы было понятно, не страшно и без принудительных сохранений всего и вся без вопросов.
      Согласен, да хотя бы первой. Сейчас что есть — сыро. Пока очень сыро, сам пользуюсь, натыкаюсь на баги, исправляю.


  1. ComodoHacker
    02.11.2015 10:04

    С защитой данных в контролах понятно, а как насчет расшифрованных данных во внутренних переменных? В дампе найти можно?


    1. Antelle
      02.11.2015 10:21

      Все пароли и кастомные поля, помеченные защищёнными, хранятся заXOR-енными (код) и достаются только когда нужно значение (например, при копировании); по ним ещё предусмотрен поиск без расшифровки значения целиком. Остальное хранится открытым текстом.
      Сейчас в UI есть недоделка: пароли изменяются в обычном инпуте, безопасный контрол сейчас только на экране ввода мастер-пароля, это пока что не успел.


      1. ComodoHacker
        03.11.2015 09:27

        достаются только когда нужно значение

        Вот это значение потом зачищается из памяти или нет? Я не в курсе, как с этим в ноде, но подозреваю, что проблемы есть.
        Если в нужный момент завалить систему с крэшдампом, там будет расшифрованный пароль?


        1. Antelle
          03.11.2015 09:34

          Нет, уже не получится сделать. Значение надо обязательно достать в string, а это память, управляемая GC, к ней нет доступа. Вот когда будет авто-тайп — доставать значение не потребуется, тогда удастся этого избежать.


        1. Antelle
          03.11.2015 09:40

          Вообще сделать, конечно, получится, если захотеть: можно из плюсово аддона залезть внутрь V8::String и зачситить. Скорее всего уже что-то есть такое, надо поискать.


  1. phoenixweiss
    02.11.2015 11:16

    Просто блестящая работа! Это действительно здорово.

    Не совсем по теме — а почему вы .idea не добавили в .gitignore? (Вопрос по репозиторию)


    1. Antelle
      02.11.2015 11:20
      +2

      Спасибо.
      Всегда игнорю из .idea только workspace.xml — чтобы при открытии проекта в идее, в нём уже были настройки, библиотеки, исключения dist-ов и всякое.


      1. phoenixweiss
        02.11.2015 13:40
        +1

        Не спорю что такое решение имеет место быть и удобно пока Вы один, но если вдруг в проект вольются еще разработчики, это решение будет многим мешать.
        Я конечно вряд ли смогу помочь с проектом, но просто для информации говорю.


        1. artspb
          02.11.2015 13:56
          +2

          И чем же оно будет мешать?


          1. Borz
            03.11.2015 00:20

            например разным содержимым файла misc.xml


            1. Antelle
              03.11.2015 09:32
              +1

              Вопрос, игнорить .idea или нет — интересный, конечно. Вот на работе у нас настройки коммитятся, мы к этому пришли, потому что проект настраивать каждый раз было сложно. Тут даже рассказано, как их закоммитить. Кто-то считает, что коммитить их не надо вообще. С другой стороны, это гит, если мешают изменения в каком-то файле, всегда есть git update-index --assume-unchanged


              1. Borz
                03.11.2015 10:59
                +1

                Общие настройки можно частично донести через Settings Repository.

                Я не призываю целиком игнорить .idea/ — просто надо понимать, что будет мешать других разработчикам, а что нет.
                Те же misc.xml и workspace.xml это сугубо персональные файлы.
                Файл modules.xml смешанный — в нём есть как общие, так и персональные настройки.
                А каталог runConfigurations/ и вовсе не должен игнориться — он предназначен для командной работы


                1. Antelle
                  03.11.2015 11:03

                  Я думал, misc.xml как раз не мешает, а workspace.xml и tasks.xml всегда игнорю (по официальному гайду jetbrains, игнорить надо всё кроме workspace.xml и tasks.xml — может гайд устарел?). Спасибо, буду знать.


                  1. Borz
                    03.11.2015 11:25
                    +1

                    Если вы про этот guide, то там в комментариях написаны причины, по которым надо игнорить misc.xml


                    1. Antelle
                      03.11.2015 11:32

                      О, даже в комментариях там уже написали, когда я ещё его читал, там не было, не знал, что его надо игнорить.


  1. Balek
    02.11.2015 11:37

    Такая гора работы проделана. Жаль, что KeePass, а не pass. Я сам всё ещё пользуюсь кипасом, но только по привычке. По-моему, надо переходить на пасс, потому что у него более грамотная архитектура. Разделение по файлам упрощает синхронизацию и даёт возможность расшаривания паролей.


    1. Antelle
      02.11.2015 11:56

      Если будет популярен, в модели всегда можно сделать поддержку другого формата. Хотя я не вижу причин на него переходить. Чем он более грамотный — хранением пароля в отдельном файле? Ну да, когда-то может быть удобно, хотя я думаю, что передача 50..100кб на один пароль (вся база поменяется) в условиях современного интернета жить не мешает.
      В формате есть только пароль, а это значит, что все клиенты должны изобретать собственный формат для цветов, иконок,… — клиент получится или совсем без фич, или со своим форматом, а вот этого уже не хотелось бы.


      1. Balek
        02.11.2015 12:33
        +1

        Отдельные файлы позволяют вносить правки параллельно с нескольких устройств. Например, ноут в оффлайне, я на нём сохраняю новый пароль и выключаю. Даже если база была изменена до его следующего включения, в большинстве случаев всё должно синхронизироваться без вопросов. У официального кипаса, вроде, уже что-то есть на этот счёт, но сторонние клиенты вряд ли это поддерживают.

        А с расшариванием вообще сказка. Каждый файл зашифровывается открытыми ключами тех, кому предоставляем доступ. При этом личные пароли можно вообще не выкладывать на общий ресурс, а расшаривать только отдельную директорию.

        На счёт формата — вы правы. Нужно его развивать пока реализаций не много. Но ведь ограничений никаких нет. Всё, что может KeePass, можно реализовать и в формате pass.

        Не подумайте, что я пришёл сюда критиковать ваш титанический труд. Просто очередная скорбь по тому, что более совершенные технологии часто проигрывают более распространённым.


        1. Antelle
          02.11.2015 12:47

          В кипассе есть мёрж, и я сделаю авто-мёрж при синхронизации, с оффлайном по-другому не получится.
          Про расшаривание согласен, но это уже больше энтерпрайз-фича. С другой стороны в кипасс атомарная единица — файл. Не так гибко, но тоже можно.
          Реализовать можно, но поддерживать будет только реализовавший клиент или тот, кто согласится с принятым форматом. Официально формат не установлен, к сожалению.


  1. kvaps
    02.11.2015 12:44

    Блин, как круто! — все работает, офигенно.

    Добавтье пожалуйста поддержку синхронизации измененных данных при сохранении, если открытый файл был изменен другим человеком/программой.
    И кастомные иконки растровые, что бы значки сайтов отображались.

    А так, чую переезд с официального клиента на ваш вовсе не за горами :)


    1. Antelle
      02.11.2015 12:51
      +2

      В планах есть, для этого нужно реализовать оффлайн + мёрж, сделать не так быстро, но будет, при изменениях в дропбоксе записи на девайсе с открытым файлом будут появляться. Кастомные иконки и их скачивание тоже запланированы, с ними просто.


  1. Slash
    02.11.2015 14:58

    Заметил 2 проблемы: не работает вставка (это фича?); если начать создавать новую запись, переключиться в другое окно чтобы что-то скопировать, вернуться обратно, окно сбросится. :(


    1. Antelle
      02.11.2015 20:06

      Не работает под маком? Это баг, под маком из-за отключённого меню не работают сейчас все шорткаты. Про вернуться в другое окно не думал, интересный казус получился; подумаю, как лучше сохранять значение инпута, не добавляя кнопки Save (сечас по потере фокуса).


      1. Slash
        02.11.2015 20:14

        Спасибо)


  1. Andrii_Z
    03.11.2015 14:21

    Есть ли поддержка аппаратных ключей как второго фактора авторизации? Таких как Yubico Neo.


    1. Antelle
      03.11.2015 14:24

      Нет, вообще не знал, что даже в KeePass так можно. А как это делается — плагином?


      1. Andrii_Z
        03.11.2015 15:24

        Я пользуюсь LastPass, там такое возможно. Если бы кто-то реализовал в открытом продукте — было бы круто.


        1. Antelle
          03.11.2015 15:35

          О, у них даже для node.js либа есть developers.yubico.com/OTP/Libraries/List_of_libraries.html
          Просто достаточно поддержать будет.


          1. synchrone
            03.11.2015 19:16

            А как к этому отнесется keepass? базу-то неплохо бы держать в обратно-совместимом формате.


            1. btd
              03.11.2015 19:22

              Ну так с базой это не связано. Там ключи собираются вместе и затем считается общий хеш, ни как не влияющий на содержание базы. Просто появится еще одни тип ключа.


            1. Antelle
              03.11.2015 19:25

              Я нашёл, там плагин есть какой-то, т.е. уже поддерживает (я правда не изучал, насколько что там как сделано, посмотрю) — вообще keepass поддерживает любых кастомных провайдеров (в виде дополнительного значения в мастер-хэше), так, к примеру, там сделана встроенная привязка к windows-аккаунту.


  1. malex
    04.11.2015 06:58

    В мобильном хроме (samsung s5) — падает после авторизации в дропбоксе.


    1. malex
      04.11.2015 07:04

      Сори стормозил — нужно просто открывать сраницу в режиме полной версии


      1. Antelle
        04.11.2015 08:40

        Странно, и на чём там падать? Теоретически, только размеры отличаются. Вроде не должно, на iOS в хроме всё хорошо и в полной, и в мобильной версии. Как дойдут руки, проверю на мобильных на андроиде.


  1. mikes
    04.11.2015 22:18

    текст немного мелковат в основном окне, возможно ли сделать опцию выбора размера шрифта?


    1. Antelle
      04.11.2015 22:48

      Можно, правда стили пофиксить надо будет. Пожелание в TODO записал, как будет время разобраться, сделаю.


  1. smartov
    06.11.2015 21:22

    Огромная работа, спасибо.

    Не могу сказать что работает без сучка и я даже не знаю почему случались баги.
    Сначала не хотел файл сохраняться на дропбокс (просто висел и всё), только я решил подебажить и начал писать об этом — начал сохраняться. Может запрос не проходил (там есть timeout?)
    Потом Сафари на яблофоне не видела его содержимого. Но после отключения режима Private который может очень мешать — заработало.
    В Хроме на яблофоне вообще не заводится. Всё заканчивается страницей с цветом бэграунда приложения и ничем больше. take.ms/06SAv Даже если запрашивать Desktop page. Но я его не особо использую на мобильном.

    Я просто FYI это всё описываю, думаю вам самому интересно какие бывают проблемы и бывают ли.


    1. Antelle
      06.11.2015 21:48

      С добавлением и редактированием вообще на мобильных сейчас глючно. О проблемах интересно, конечно, буду фиксить по мере возможности, как доберусь до мобильных. На андроиде пока вообще не смотрел даже, заводится ли.
      Хм, в создании группы кнопка OK и правда нужна, спасибо (потому что непонятно, как выйти из диалога), в остальных местах кнопки OK нет принципиально: где можно, сохраняется сразу же, без кнопок.
      А зачем на десктопе дропбокс? Так же и так есть файлы из дропбокса.


      1. smartov
        06.11.2015 21:50

        > Так же и так есть файлы из дропбокса.
        У меня нет. Я не использую десктопный Dropbox. Только Web. Да и в любом случае в синхронизации может быть задержка. Может у меня как раз льётся что-то на дропбокс (если бы я использовал его). И то что я открою с десктопа будет не то же, что сейчас уже на сервере.


        1. Antelle
          06.11.2015 22:00

          Хм, значит имеет смысл не отключать.


      1. smartov
        06.11.2015 21:58

        Пожалуйста, сделайте возможность удалить Attachment из записи. Сейчас можно только всю запись грохнуть.


        1. Antelle
          06.11.2015 21:59
          +1

          Выделить аттачмент, cmd-del


    1. smartov
      06.11.2015 21:49

      Antelle о похоже дело не в timeout. При нажатии Cmd+S (Mac OS) запроса на Dropbox вообще не происходит, хотя «прогресс» показывает.
      Вот видео. После очистки событий в консоли нажимаю Cmd+S dl.dropbox.com/s/796zvtnqrrcvg9x/screencast_2015-11-06_13-47-59.mp4?dl=0


      1. Antelle
        06.11.2015 21:59

        Странно, посмотрю, скорее всего глюка в получении клиента и авторизации.


        1. smartov
          06.11.2015 22:57

          Сама кнопка Sync to Dropbox работает нормально. Cmd-S работает ненормально.


          1. Antelle
            06.11.2015 23:17

            О, тогда всё ясно, спасибо. По Cmd-S будет автосейв всех файлов, там запускается синк с дропбоксом, в котором сейчас баг. Решится уже скоро, как раз сейчас делаю оффлайн и этот самый синк.


  1. smartov
    06.11.2015 21:29

    С добавлением в Safari тоже есть проблемы

    image

    image


  1. smartov
    06.11.2015 21:33

    Практически нигде не предусмотрено кнопок «Ок». Войти на мобильном можно только нажав «Ввод» на выдвижной клавиатуре. Но это ок. А вот группу уже не создашь — ввод на клавиатуре не работает.

    upd: а, так группу и на десктопе с трудом можно создать. Это нужно догадаться что поле работает только на потерю фокуса


  1. smartov
    06.11.2015 21:38

    В Electron приложении для Mac OS почему-то нету возможности открыть с DropBox в принципе