Je suis en train d'écrire un logiciel qui va interagir avec Bitwarden. Il existe plusieurs clients Bitwarden, mais peu sont suffisamment complet pour mon besoin.
J'ai également trouvé peu de documentation sur comment écrire un client Bitwarden.
La particularité de Bitwarden c'est que toutes la partie cryptographique est réalisé côté client.
Pour être caricatural, le serveur n'est qu'un espace de stockage.
Je n'ai testé ce code que sur l'implémentation Rust de Bitwarden : https://github.com/dani-garcia/bitwarden_rs/.
Dans ce premier billet je vais expliquer comment créer, connecter et valider un compte.
Créer un compte
Créer un compte, signifie d'abord créer deux clés de chiffrement :
- une clé de chiffrement symétrique (pour chiffrer le contenu de son coffre)
- une clé de chiffrement asymétrique (pour chiffrer les clés de chiffrement symétrique des organisations)
Créer une clé de chiffrement symétrique
Le chiffrement symétrique utilise AES en mode CBC.
Pour créer une clé de chiffrement symétrique, rien de bien compliqué. Il faut un nombre aléatoire de bytes de 64 caractères :
from secrets import token_bytes token = token_bytes(64)
Chiffrer la clé symétrique
Ces clés sont stockées sur le serveur, mais elles ne doivent pas y être stockées en clair. Sinon quelqu'un qui accèderait à la base de donnée pourrait accéder à l'ensemble des données.
Cette clé est alors chiffrée à partir de quatre éléments :
- l'adresse courriel de l'utilisateur
- le mot de passe maître de l'utilisateur
- un vecteur d'initialisation
- un nombre d'itération
Les deux premiers éléments sont connus par l'utilisateur :
password = 'oi29ç&&ZIuçàç3' email = 'my_mail@gnunux.info'
Attention, l'adresse courriel doit être en minuscule :
email = email.lower()
Le vecteur d'initialisation est généré :
from secrets import token_bytes iv = token_bytes(16)
Le nombre d'itération peut être récupéré via cURL :
# curl -d '{"email":"my_mail@gnunux.info"}' -H "Content-Type: application/json" -X POST https://localhost:8001/api/accounts/prelogin {"Kdf":0,"KdfIterations":100000}
Donc ici :
iteration = 100000
Attention la longueur de la clé doit être un multiple de 16. Pour compléter la taille du token on va ajouter n fois la longueur de la clé :
// pad_len = 16 - len(token) % 16 padding = bytes(pad_len * pad_len) content = token + padding // À partir de là il est possible de chiffrer la clé :
from hashlib import pbkdf2_hmac, sha256 from hkdf import hkdf_expand from base64 import b64encode master_key = pbkdf2_hmac('sha256', password.encode(), email.encode(), iterations) enc = hkdf_expand(master_key, b'enc', 32, sha256) mac = hkdf_expand(master_key, b'mac', 32, sha256) c = AES.new(enc, AES.MODE_CBC, iv) ct = c.encrypt(content) cmac = hmac_new(mac, iv + ct, sha256)
Les informations indispensables sont alors stockées dans une unique chaine.
Quatre informations sont stockées :
- le type de la clé (pour les clés symétriques ce type est "2")
- le vecteur d'initialisation
- le token chiffré
- le digest pour valider la clé
type = 2 b64_iv = b64encode(iv).decode() b64_ct = b64encode(ct).decode() b64_digest = b64encode(cmac.digest()).decode() encoded_sym_key = f'{type}.{b64_iv}|{b64_ct}|{b64_digest}'
Créer une clé de chiffrement asymétrique
Le chiffrement asymétrique utilise RSA de longueur 2048.
Pour créer une clé de chiffrement asymétrique :
from Crypto.PublicKey import RSA asym_key = RSA.generate(2048) private_key = asym_key.exportKey('DER', pkcs=8) public_key = asym_key.publickey().exportKey('DER')
Chiffrer la clé asymétrique
La clé asymétrique est chiffrée non pas à partir du mot de passe et de l'adresse courriel, mais à partir de la clé symétrique. Si on change de mot de passe, il n'est ainsi nécessaire que de re-chiffrer la clé symétrique.
La clé symétrique est coupée en deux :
enc=token[:32] mac=token[32:]
On génère un vecteur d'initialisation :
iv = token_bytes(16)
On ajoute des caractères pour arriver à un multiple de 16 :
pad_len = 16 - len(private_key) % 16 padding = bytes([ pad_len ] * pad_len) content = private_key + padding
Et on chiffre :
ct = AES.new(enc, AES.MODE_CBC, iv).encrypt(content) cmac = hmac_new(mac, iv + ct, sha256)
On stocke dans une chaine les informations :
type = 2 b64_iv = b64encode(iv).decode() b64_ct = b64encode(ct).decode() b64_digest = b64encode(cmac.digest()).decode() encoded_asym_key = f'{type}.{b64_iv}|{b64_ct}|{b64_digest}'
Création du compte
Il nous manque une information pour créer le compte, le hash du mot de passe.
Ce hash servira à Bitwarden de valider la connexion de l'utilisateur.
Pour créer le hash :
hash_password = b64encode(pbkdf2_hmac('sha256', master_key, password.encode(), 1)).decode()
Nous pouvons ainsi construire le payload de la fonction de création du compte :
data = {'name': 'Mon nom', 'email': email, 'masterPasswordHash': hash_password, 'masterPasswordHint': None, 'key': encoded_sym_key, 'kdf': 0, 'kdfIterations': iterations, 'referenceId': None, 'keys': { 'publicKey': public_key, 'encryptedPrivateKey': encoded_asym_key } }
Il faut faire un POST avec ce payload sur l'URL https://localhost:8001/api/accounts/register pour créer l'utilisateur.
Se connecter au compte
Pour se connecter au compte nous avons besoin :
- de l'adresse courriel
- du hash du mot de passe (voir au-dessus)
- d'un identifiant de client
L'identifiant n'est qu'un UUID, il devra être conservé dans la configuration du client. Sinon, à chaque nouvelle connexion un courriel sera envoyé pour prévenir qu'un nouveau client se connecte à notre compte.
Pour générer un UUID, rien de plus simple :
from uuid import uuid4 uuid = uuid4()
Nous avons toutes les informations pour créer notre payload :
data = {'grant_type': 'password', 'username': email, 'password': hash_password, 'scope': 'api offline_access', 'client_id': 'desktop', 'device_type': 7, 'device_identifier': uuid, 'device_name': 'my_client', }
Il faut faire un POST avec ce payload sur l'URL ''https://localhost:8001/identity/connect/token' pour se connecter à l'utilisateur.
Il faut conserver les informations de connexion. Nous verrons plus tard à quoi cela servira.
Valider le compte
Un compte est valide quelques jours. Il faut donc rapidement valider le compte.
Pour valider un compte il faut soit :
- cliquer sur le lien envoyé par mail
- créer un JWT valide
Attention, pour créer un JWT valide, il faut avoir accès à la clé privée de Bitwarden_rs.
Pour valider le compte il nous faut l'ID utilisateur. Pour cela nous allons récupérer la clé "access_token" retourné lors de la connexion de l'utilisateur.
Ce "access_token" est un JWT.
from jwt import decode as jwt_decode user_id = jwt_decode(login['access_token'], algorithm="RS256", verify=False)['sub']
Nous pouvons créer le payload :
from time import time now = int(time()) data = {'nbf': now, 'exp': now + 432000, 'iss': 'https://localhost:8001|verifyemail', 'sub': user_id}
Il faut générer le JWT à partir de ce payload :
with open('/var/lib/bitwarden_rs/rsa_key.der', 'rb') as private_key_fh: private_key = RSA.importKey(private_key_fh.read()).exportKey('PEM') jwt = jwt_encode(data, private_key, algorithm="RS256").decode()
Enfin nous pouvons faire le payload de validation :
data = {'userId': user_id, 'token': jwt}
Il faut faire un POST avec ce payload sur l'URL ''https://localhost:8001/api/accounts/verify-email-token' pour valider l'utilisateur. Dans l'entête il faut rajouter les informations d'authentification :
{'Authorization': f'Bearer {login["access_token"]}'}
La suite
Nous verrons par la suite comment créer un "identifiant", une "organisation" et une "collection".
1 De прогон хрумером что это -
Статейные и ссылочные прогоны Xrumer, GSA
В наше время, практически каждый человек пользуется интернетом.
С его помощью можно найти любую информацию из
различных интернет-источников и поисковых систем.
Для кого-то собственный сайт — это хобби.
Однако, большинство используют разработанные проекты для заработка и привлечение прибыли.
У вас есть собственный сайт и вы хотите
привлечь на него максимум посетителей, но не знаете с чего начать?
Заказать Прогон Хрумером и ГСА можно в телеграмм логин @pokras777 здесь наша группа в телеграмм https://t.me/+EYh48YzqWf00OTYy или
в скайпе pokras7777
2 De забор работа цена за погонный -
Забор из металлопрофиля или профлиста — это практичное и долговечное решение для
ограждения территории. Такой забор обеспечивает надежную защиту, препятствуя несанкционированному проникновению и обеспечивая конфиденциальность.
Профнастил, из которого часто изготавливают заборы, обладает высокой устойчивостью
к атмосферным воздействиям и механическим нагрузкам.
Кроме того, забор из профнастила легко монтируется и имеет
доступную цену. Если вы хотите получить качественный забор из профнастила под ключ, важно обратиться к опытным специалистам, которые смогут выполнить работы профессионально
и эффективно. При выборе забора
из профлиста или металлопрофиля следует учесть не только цену,
но и качество материалов, дизайн и общую надежность конструкции, чтобы быть
уверенным в долговечности и безопасности вашего забора.
3 De сетка 3д для забора цена -
3D заборы обладают рядом преимуществ.
Во-первых, они создают эффект объемности и глубины благодаря своей трехмерной структуре,
что придает уникальный внешний вид всему забору.
Во-вторых, такие заборы обеспечивают дополнительную приватность и
безопасность, так как плотная
структура 3D забора предотвращает проникновение посторонних и
обеспечивает ограниченный обзор снаружи.
4 De база форумов 2023 -
Предлагаем вам приобрести Свежая база форумов
2023 Апрель для хрумера и гса В БАЗЕ
222 тыс сайтов прочеканых на дубли, и
200 ok и по признакам на форумы в базе
чисто одни форумы рушки и
не рушки по желанию можем прочекать
на доменные зоны (если кому-нибудь
надо) База собиралось и фильтровалась
в течение трех месяцев.
ДЛЯ СВЯЗИ С НАМИ ПИШИТЕ В СКАЙП ЛОГИН ЛОГИН POKRAS7777
ЛИБО В ТЕЛЕГРАММ