Skip to content

Commit

Permalink
RSA deprecation
Browse files Browse the repository at this point in the history
  • Loading branch information
sk-keeper authored and aaunario-keeper committed Nov 8, 2024
1 parent a30f56a commit e6b04e3
Show file tree
Hide file tree
Showing 39 changed files with 2,300 additions and 1,804 deletions.
15 changes: 11 additions & 4 deletions examples/add_user_to_shared_folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,17 @@
if isinstance(manage_records, bool):
arq.manageRecords = folder_pb2.BOOLEAN_TRUE if manage_records else folder_pb2.BOOLEAN_FALSE
public_keys = my_params.key_cache.get(user)
if public_keys and public_keys.rsa:
user_rsa_key = crypto.load_rsa_public_key(public_keys.rsa)
arq.sharedFolderKey = crypto.encrypt_rsa(shared_folder_key, user_rsa_key)
rq.sharedFolderAddUser.append(arq)
if public_keys:
if public_keys.ec:
user_ec_key = crypto.load_ec_public_key(public_keys.ec)
arq.typedSharedFolderKey.encryptedKey = crypto.encrypt_ec(shared_folder_key, user_ec_key)
arq.typedSharedFolderKey.encryptedKeyType = folder_pb2.encrypted_by_public_key_ecc
rq.sharedFolderAddUser.append(arq)
elif not my_params.forbid_rsa and public_keys.rsa:
user_rsa_key = crypto.load_rsa_public_key(public_keys.rsa)
arq.typedSharedFolderKey.encryptedKey = crypto.encrypt_rsa(shared_folder_key, user_rsa_key)
arq.typedSharedFolderKey.encryptedKeyType = folder_pb2.encrypted_by_public_key
rq.sharedFolderAddUser.append(arq)
else:
logging.warning('Add user "%s": User public key is not available', user)

Expand Down
66 changes: 49 additions & 17 deletions keepercommander/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def login(params, new_login=False, login_ui=None):
flow.login(params, new_device=True)


def accept_account_transfer_consent(params):
def accept_account_transfer_consent(params): # type: (KeeperParams) -> bool
share_account_by = params.get_share_account_timestamp()
print(constants.ACCOUNT_TRANSFER_MSG.format(share_account_by.strftime('%a, %b %d %Y')))

Expand All @@ -81,21 +81,45 @@ def accept_account_transfer_consent(params):
answer = input('Do you accept Account Transfer policy? {}: '.format(input_options))
answer = answer.lower()
if answer.lower() == 'accept':
for role in params.settings['share_account_to']:
encoded_public = utils.base64_url_decode(role['public_key'])
public_key = crypto.load_rsa_public_key(encoded_public)
transfer_key = crypto.encrypt_rsa(params.data_key, public_key)
request = {
'command': 'share_account',
'to_role_id': role['role_id'],
'transfer_key': utils.base64_url_encode(transfer_key)
}
communicate(params, request)
return True
ok = True
requests = []
if 'share_account_to' in params.settings:
for role in params.settings['share_account_to']:
request = {
'command': 'share_account',
'to_role_id': role['role_id'],
}
if not params.forbid_rsa and 'public_key' in role:
encoded_public = utils.base64_url_decode(role['public_key'])
public_key = crypto.load_rsa_public_key(encoded_public)
transfer_key = crypto.encrypt_rsa(params.data_key, public_key)
request['transfer_key'] = utils.base64_url_encode(transfer_key)

requests.append(request)
responses = execute_batch(params, requests)
if isinstance(responses, list):
for response in responses:
if response['result_code'] != 'success':
logging.warning('Account Transfer policy acceptance error: %s',
response.get('message') or response['result_code'])
ok = False
if ok and params.forbid_rsa and params.enterprise_ec_key:
try:
share_data_key_with_enterprise(params)
except Exception as e:
logging.warning('Account Transfer policy acceptance error: %s', e)
ok = False
return ok
else:
return False


def share_data_key_with_enterprise(params): # type: (KeeperParams) -> None
rq = enterprise_pb2.EnterpriseUserDataKey()
rq.userEncryptedDataKey = crypto.encrypt_ec(params.data_key, params.enterprise_ec_key)
communicate_rest(params, rq, 'enterprise/set_enterprise_user_data_key')


def get_record_data_json_bytes(data): # type: (dict) -> bytes
"""Get serialized and utf-8 encoded record data with padding"""
data_str = json.dumps(data)
Expand Down Expand Up @@ -342,14 +366,22 @@ def load_team_keys(params, team_uids): # type: (KeeperParams, List[str]
try:
aes = b''
rsa = b''
ec = b''
encrypted_key = utils.base64_url_decode(tk['key'])
if tk['type'] == 1:
key_type = tk['type']
if key_type == 1:
aes = crypto.decrypt_aes_v1(encrypted_key, params.data_key)
elif tk['type'] == 2:
aes = crypto.decrypt_rsa(tk['key'], params.rsa_key2)
elif tk['type'] == 3:
elif key_type == 2:
aes = crypto.decrypt_rsa(encrypted_key, params.rsa_key2)
elif key_type == 3:
aes = crypto.decrypt_aes_v2(encrypted_key, params.data_key)
elif key_type == 4:
aes = crypto.decrypt_ec(encrypted_key, params.ecc_key)
elif key_type == -1:
ec = encrypted_key
elif key_type == -3:
rsa = encrypted_key
params.key_cache[team_uid] = PublicKeys(rsa=rsa, aes=aes)
params.key_cache[team_uid] = PublicKeys(rsa=rsa, aes=aes, ec=ec)
except Exception as e:
logging.debug(e)

Expand Down
9 changes: 7 additions & 2 deletions keepercommander/breachwatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from .commands.helpers.enterprise import user_has_privilege, is_addon_enabled
from .constants import KEEPER_PUBLIC_HOSTS
from . import api, crypto, utils, rest_api, vault
from .proto import breachwatch_pb2, client_pb2, APIRequest_pb2
from .proto import breachwatch_pb2, client_pb2, APIRequest_pb2, enterprise_pb2
from .error import KeeperApiError, CommandError
from .params import KeeperParams
from .vault import KeeperRecord
Expand Down Expand Up @@ -211,7 +211,11 @@ def prepare_security_data():
sec_data.uid = utils.base64_url_decode(record.record_uid)
if record_pw:
rec_sd = prepare_security_data()
sec_data.data = crypto.encrypt_rsa(json.dumps(rec_sd).encode('utf-8'), params.enterprise_rsa_key)
data = json.dumps(rec_sd).encode('utf-8')
if params.forbid_rsa:
sec_data.data = crypto.encrypt_ec(data, params.enterprise_ec_key)
else:
sec_data.data = crypto.encrypt_rsa(data, params.enterprise_rsa_key)

return sec_data

Expand Down Expand Up @@ -248,6 +252,7 @@ def skip_update():
update_rq = APIRequest_pb2.SecurityDataRequest()
rec_sec_data = calculate_security_data()
update_rq.recordSecurityData.append(rec_sec_data)
update_rq.encryptionType = enterprise_pb2.KT_ENCRYPTED_BY_PUBLIC_KEY_ECC if params.forbid_rsa else enterprise_pb2.KT_ENCRYPTED_BY_PUBLIC_KEY
api.communicate_rest(params, update_rq, 'enterprise/update_security_data')

@staticmethod
Expand Down
43 changes: 21 additions & 22 deletions keepercommander/commands/aram.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,34 @@
import base64
import copy
import datetime
import os
import time
import json
import gzip
import hashlib
import hmac
import json
import logging
import os
import platform
import re
import socket
import ssl
import sys
import time
from functools import partial

from typing import Optional, List, Union, Dict, Set, Any, Tuple
from urllib.parse import urlparse

import requests
import socket
import ssl
import hashlib
import hmac

from urllib.parse import urlparse

from .transfer_account import EnterpriseTransferUserCommand
from ..display import bcolors
from .helpers import audit_report
from .enterprise_common import EnterpriseCommand
from .base import user_choice, suppress_exit, raise_parse_exception, dump_report_data, Command, field_to_title
from .enterprise_common import EnterpriseCommand
from .helpers import audit_report
from .transfer_account import EnterpriseTransferUserCommand
from .. import api, vault, record_management
from ..constants import EMAIL_PATTERN
from ..display import bcolors
from ..error import CommandError
from ..params import KeeperParams
from ..proto import enterprise_pb2
from ..constants import EMAIL_PATTERN
from ..sox import sox_data, get_prelim_data, is_compliance_reporting_enabled, get_sox_database_name, \
get_compliance_data, get_node_id
from ..sox.sox_data import RebuildTask
Expand Down Expand Up @@ -713,7 +711,7 @@ def convert_event(self, props, event):
def export_events(self, props, events):
url = "https://{0}.ods.opinsights.azure.com/api/logs?api-version=2016-04-01".format(props['wsid'])
data = json.dumps(events)
dt = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
dt = datetime.datetime.now(datetime.timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT')
shared_key = self.build_shared_key(props['wsid'], props['wskey'], len(data), dt)
headers = {
"Authorization": "SharedKey {0}".format(shared_key),
Expand Down Expand Up @@ -1170,7 +1168,7 @@ def convert_value(field, value, **kwargs):
if isinstance(value, str):
return value
if isinstance(value, (int, float)):
dt = datetime.datetime.utcfromtimestamp(int(value)).replace(tzinfo=datetime.timezone.utc).astimezone(tz=None)
dt = datetime.datetime.fromtimestamp(int(value), tz=datetime.timezone.utc)
rt = kwargs.get('report_type') or ''
if rt in {'day', 'week'}:
dt = dt.date()
Expand All @@ -1180,7 +1178,7 @@ def convert_value(field, value, **kwargs):
dt = dt.strftime('%Y-%m-%d @%H:00')
return dt
elif field in {"first_created", "last_created"}:
return datetime.datetime.utcfromtimestamp(int(value)).replace(tzinfo=datetime.timezone.utc).astimezone(tz=None)
return datetime.datetime.fromtimestamp(int(value), tz=datetime.timezone.utc)
return value

DimensionCache = {}
Expand Down Expand Up @@ -1342,7 +1340,7 @@ def filter_rows(rows, search_pattern):
rq['timezone'] = tt[0]
else:
now = time.time()
utc_offset = datetime.datetime.fromtimestamp(now) - datetime.datetime.utcfromtimestamp(now)
utc_offset = datetime.datetime.fromtimestamp(now) - datetime.datetime.fromtimestamp(now, ts=datetime.timezone.utc)
hours = (utc_offset.days * 24) + int(utc_offset.seconds / 60 / 60)
rq['timezone'] = hours

Expand Down Expand Up @@ -1993,10 +1991,11 @@ def transfer_accounts(from_users, to_user, dryrun=False):
if dryrun:
cmd_status = 'dry run'
else:
pub_key = self.get_public_key(params, target)
if pub_key:
api.load_user_public_keys(params, [target], False)
target_pub_keys = params.key_cache.get(target)
if target_pub_keys:
for email in [u.get('username') for u in from_users]:
result = EnterpriseTransferUserCommand.transfer_user_account(params, email, target, pub_key)
result = EnterpriseTransferUserCommand.transfer_user_account(params, email, target, target_pub_keys)
if result:
affected += 1

Expand Down
2 changes: 1 addition & 1 deletion keepercommander/commands/automator.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ def execute(self, params, **kwargs): # type: (KeeperParams, **any) -> any
encrypted_ec_private_key = crypto.encrypt_ec(ec_private_key, automator_public_key)
rq.encryptedEccEnterprisePrivateKey = encrypted_ec_private_key

if 'rsa_encrypted_private_key' in keys:
if not params.forbid_rsa and 'rsa_encrypted_private_key' in keys:
encrypted_rsa_private_key = utils.base64_url_decode(keys['rsa_encrypted_private_key'])
rsa_private_key = crypto.decrypt_aes_v2(encrypted_rsa_private_key, params.enterprise['unencrypted_tree_key'])
encrypted_rsa_private_key = crypto.encrypt_ec(rsa_private_key, automator_public_key)
Expand Down
17 changes: 0 additions & 17 deletions keepercommander/commands/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,6 @@ def execute(self, params, **kwargs):

print('\n'.join(f' {k} {v}' for k, v in record_names.items()))
else:
rq = {
'command': 'sync_down',
'revision': 0,
'include': ['non_shared_data', 'explicit']
}
rs = api.communicate(params, rq)
nsd = {x['record_uid']: x['data'] for x in rs.get('non_shared_data', [])}

records = []
for record_uid in record_uids:
convert_result = ConvertCommand.convert_to_record_type_data(record_uid, params, type_info)
Expand Down Expand Up @@ -251,15 +243,6 @@ def execute(self, params, **kwargs):
rc.record_file.append(rf)
rc.data = crypto.encrypt_aes_v2(api.get_record_data_json_bytes(v3_data), record_key)

# Non shared data
if record_uid in nsd:
try:
non_shared_data = utils.base64_url_decode(nsd[record_uid])
non_shared_data = crypto.decrypt_aes_v1(non_shared_data, params.data_key)
rc.non_shared_data = crypto.encrypt_aes_v2(non_shared_data, params.data_key)
except Exception as e:
logging.debug('Non shared data conversion failed for record %s: ', record_uid, e)

# Get share folder of the record so that we can convert the Record Folder Key
shared_folders = find_parent_top_folder(params, record_uid)

Expand Down
Loading

0 comments on commit e6b04e3

Please sign in to comment.