def reset_user_invalid_keys(user, admin_rsa): """ Legacy, recrypts all encrypted objects with user's new pubkey(s) """ otype, oid = ContentType.objects.get_for_model(user), user.id ipubs = OwnerPublicKey.inactive.filter(owner_type=otype, owner_id=oid, active=False) for ipub in ipubs: adminclearkey = PKCS1_OAEP.new(admin_rsa).decrypt(b64decode(ipub.admincipher)) # import inactive key irsa = import_rsa(ipub.adminscopy, adminclearkey) # get matching active public key for user apub = OwnerPublicKey.objects.get(owner_type=otype, owner_id=oid, keytype=ipub.keytype, active=True) # import new active publickey apublickey = import_rsa(apub.publickey) # update encrypted objects encrypting via new active public key for encobj in EncryptedObject.objects.filter(opub=ipub): clearkey = PKCS1_OAEP.new(irsa).decrypt(b64decode(encobj.cipherkey)) encobj.cipherkey = b64encode(PKCS1_OAEP.new(apublickey).encrypt(clearkey)) encobj.opub = apub # point encobj's opub to new pubkey encobj.save() apubs = OwnerPublicKey.objects.filter_pubkey(owner=user) # reset user's private keys outside their own pubkeys if any, eg. practice locations privs = UserPrivateKey.objects.filter( user=user, credtype=CRED_WEBAPP).exclude(opub__in=apubs) reset_keys(privs, admin_rsa) # and remove inactive public keys ipubs.delete()
def reset_user_invalid_keys(user, admin_rsa): """ Legacy, recrypts all encrypted objects with user's new pubkey(s) """ otype, oid = ContentType.objects.get_for_model(user), user.id ipubs = OwnerPublicKey.inactive.filter(owner_type=otype, owner_id=oid, active=False) for ipub in ipubs: adminclearkey = PKCS1_OAEP.new(admin_rsa).decrypt( b64decode(ipub.admincipher)) # import inactive key irsa = import_rsa(ipub.adminscopy, adminclearkey) # get matching active public key for user apub = OwnerPublicKey.objects.get(owner_type=otype, owner_id=oid, keytype=ipub.keytype, active=True) # import new active publickey apublickey = import_rsa(apub.publickey) # update encrypted objects encrypting via new active public key for encobj in EncryptedObject.objects.filter(opub=ipub): clearkey = PKCS1_OAEP.new(irsa).decrypt(b64decode( encobj.cipherkey)) encobj.cipherkey = b64encode( PKCS1_OAEP.new(apublickey).encrypt(clearkey)) encobj.opub = apub # point encobj's opub to new pubkey encobj.save() apubs = OwnerPublicKey.objects.filter_pubkey(owner=user) # reset user's private keys outside their own pubkeys if any, eg. practice locations privs = UserPrivateKey.objects.filter( user=user, credtype=CRED_WEBAPP).exclude(opub__in=apubs) reset_keys(privs, admin_rsa) # and remove inactive public keys ipubs.delete()
def upgrade_admin_reset_key(creds): """ Upgrade admin reset rsa keypair storing in _admin_reset.py. Upgraded public rsa keypart is stored in standard PEM format, encrypted private rsa keypart stored AES encrypted using CBC mode in base64 with no pickle. """ aes = AES.new(strengthen_key(creds)) admin_kp = aes.decrypt(b64decode(settings.CRYPTO_ADMIN_KEYPAIR)) try: admin_rsa = loads(admin_kp) admin_rsa._randfunc = os.urandom # pickled diffs pycrypto 2.3<-->2.6 # public key exported PEM format pub = export_rsa(admin_rsa.publickey()) priv = export_rsa(admin_rsa, strengthen_key(creds)) # put file in parent MHLogin directory fname = join(settings.INSTALLATION_PATH, '_admin_reset.py') f = open(fname, 'wb') f.write(ADMIN_RESET_PY % { 'admin_public_rsa': pub, 'admin_rsa': priv, }) f.close() # sanity check upgrade: from MHLogin._admin_reset import ADMIN_RESET_PUBLIC_RSA, ADMIN_RESET_ENCD_RSA upgrade_rsa_pub = import_rsa(ADMIN_RESET_PUBLIC_RSA) assert upgrade_rsa_pub.publickey() == admin_rsa.publickey( ), "public key mismatch" upgrade_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, strengthen_key(creds)) assert upgrade_rsa == admin_rsa, "rsa key mismatch" sys.stderr.write("Admin RSA reset keys upgraded in: %s\n" % fname) except UnpicklingError: sys.stderr.write("Invalid password.\n")
def upgrade_admin_reset_key(creds): """ Upgrade admin reset rsa keypair storing in _admin_reset.py. Upgraded public rsa keypart is stored in standard PEM format, encrypted private rsa keypart stored AES encrypted using CBC mode in base64 with no pickle. """ aes = AES.new(strengthen_key(creds)) admin_kp = aes.decrypt(b64decode(settings.CRYPTO_ADMIN_KEYPAIR)) try: admin_rsa = loads(admin_kp) admin_rsa._randfunc = os.urandom # pickled diffs pycrypto 2.3<-->2.6 # public key exported PEM format pub = export_rsa(admin_rsa.publickey()) priv = export_rsa(admin_rsa, strengthen_key(creds)) # put file in parent MHLogin directory fname = join(settings.INSTALLATION_PATH, '_admin_reset.py') f = open(fname, 'wb') f.write(ADMIN_RESET_PY % {'admin_public_rsa': pub, 'admin_rsa': priv, }) f.close() # sanity check upgrade: from MHLogin._admin_reset import ADMIN_RESET_PUBLIC_RSA, ADMIN_RESET_ENCD_RSA upgrade_rsa_pub = import_rsa(ADMIN_RESET_PUBLIC_RSA) assert upgrade_rsa_pub.publickey() == admin_rsa.publickey(), "public key mismatch" upgrade_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, strengthen_key(creds)) assert upgrade_rsa == admin_rsa, "rsa key mismatch" sys.stderr.write("Admin RSA reset keys upgraded in: %s\n" % fname) except UnpicklingError: sys.stderr.write("Invalid password.\n")
def test_key_pair_fetch(self): """ Just for fun, normally using RSA for AES key en/decryption. """ cleartext = 'Something fishy...' user = MHLUser.objects.get(username=self.provider.user.username) opub = OwnerPublicKey.objects.get_pubkey(owner=user) upriv = UserPrivateKey.objects.get_privkey(user=user, opub=opub) # we store keys in PEM format so import pubkey = import_rsa(upriv.opub.publickey) rsa = import_rsa(upriv.privatekey, strengthen_key(settings.SECRET_KEY if upriv.gfather else 'healme')) # pubkey enc/dec low level, PKCS1_OAEP w/RSA in production ciphertext = pubkey.encrypt(cleartext, os.urandom)[0] decrypted_text = rsa.decrypt(ciphertext) self.assertEqual(cleartext, decrypted_text)
def reset_private_keys(request): """ This view takes the KMS administrator password, and for each user whose password was reset, generates new private keys. :param request: The HTTP request :type request: django.core.handlers.wsgi.WSGIRequest :returns: django.http.HttpResponse -- The webpage """ context, resp = get_context(request), None log_qs = PasswordResetLog.objects.filter(reset=True, resolved=False).values('user') reset_users = context['reset_users'] = MHLUser.objects.filter(pk__in=log_qs) if request.method == 'POST': form = context['form'] = PasswordForm(request.POST) if form.is_valid(): try: from MHLogin._admin_reset import ADMIN_RESET_ENCD_RSA creds = strengthen_key(form.cleaned_data['password']) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) except ValueError: msg = _('Invalid password for admin reset key.') form._errors['password'] = form.error_class([msg]) else: for user in reset_users: reset_user_invalid_keys(user, admin_rsa) PasswordResetLog.objects.filter(reset=True, resolved=False, user=user).update(resolved=True, servicer=request.user, servicer_ip=request.META['REMOTE_ADDR'], resolution_timestamp=datetime.datetime.now()) resp = render_to_response('PrivateKeyResetComplete.html', context) else: context['form'] = PasswordForm() return resp or render_to_response('PrivateKeyReset.html', context)
def test_key_pair_fetch(self): """ Just for fun, normally using RSA for AES key en/decryption. """ cleartext = 'Something fishy...' user = MHLUser.objects.get(username=self.provider.user.username) opub = OwnerPublicKey.objects.get_pubkey(owner=user) upriv = UserPrivateKey.objects.get_privkey(user=user, opub=opub) # we store keys in PEM format so import pubkey = import_rsa(upriv.opub.publickey) rsa = import_rsa( upriv.privatekey, strengthen_key(settings.SECRET_KEY if upriv.gfather else 'healme')) # pubkey enc/dec low level, PKCS1_OAEP w/RSA in production ciphertext = pubkey.encrypt(cleartext, os.urandom)[0] decrypted_text = rsa.decrypt(ciphertext) self.assertEqual(cleartext, decrypted_text)
def test_static_reset(self): # new way will obsolete test_gen_new_keys when rm #2115 in cleartext = "We never really grow up, we only learn how to act in public." c = self.client # after successful login should get re-direct to / response = c.post('/login/', {'username': '******', 'password': '******'}) c.COOKIES = {'ss': response.cookies['ss'].value} c.user = authenticate(username='******', password='******') c.user = MHLUser.objects.get(id=c.user.id) opub = OwnerPublicKey.objects.get_pubkey(owner=c.user) msg = encrypt_object(SecureTestMessage, {}, cleartext, opub) c.user.set_password('gorilla') c.user.save() creds = strengthen_key(ADMIN_PASSWORD) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) privs = UserPrivateKey.objects.filter(user=c.user, credtype=CRED_WEBAPP) reset_keys(privs, admin_rsa, 'gorilla') c.logout() response = c.post('/login/', {'username': '******', 'password': '******'}) c.COOKIES = {'ss': response.cookies['ss'].value} decrypted_cleartext = decrypt_object(c, msg) self.assertEqual(decrypted_cleartext, cleartext) msg.id += 1 with self.assertRaises(KeyInvalidException): decrypt_object(c, msg) response = c.logout()
def test_static_reset(self): # new way will obsolete test_gen_new_keys when rm #2115 in cleartext = "We never really grow up, we only learn how to act in public." c = self.client # after successful login should get re-direct to / response = c.post('/login/', { 'username': '******', 'password': '******' }) c.COOKIES = {'ss': response.cookies['ss'].value} c.user = authenticate(username='******', password='******') c.user = MHLUser.objects.get(id=c.user.id) opub = OwnerPublicKey.objects.get_pubkey(owner=c.user) msg = encrypt_object(SecureTestMessage, {}, cleartext, opub) c.user.set_password('gorilla') c.user.save() creds = strengthen_key(ADMIN_PASSWORD) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) privs = UserPrivateKey.objects.filter(user=c.user, credtype=CRED_WEBAPP) reset_keys(privs, admin_rsa, 'gorilla') c.logout() response = c.post('/login/', { 'username': '******', 'password': '******' }) c.COOKIES = {'ss': response.cookies['ss'].value} decrypted_cleartext = decrypt_object(c, msg) self.assertEqual(decrypted_cleartext, cleartext) msg.id += 1 with self.assertRaises(KeyInvalidException): decrypt_object(c, msg) response = c.logout()
def handle(self, *args, **options): if (len(args) != 1): sys.stderr.write("Admin password required.\n") self.print_help(sys.argv[1], "") else: from MHLogin._admin_reset import ADMIN_RESET_ENCD_RSA try: force = options.pop('force', False) logoff = options.pop('logoff', False) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, strengthen_key(args[0])) migrate_admin_objectkeys(admin_rsa, force) # Remove active sessions, users need to log in. During initial migration # rsa keys are grandfathered since we do not know users's password. # And we don't want specialized code in KMS checking for this single # use-case where keys are grandfathered while user logged in. if logoff: Session.objects.filter( expire_date__gte=datetime.now()).delete() SmartPhoneAssn.objects.filter(is_active=True).update( is_active=False) except ValueError: sys.stderr.write("Invalid password.\n")
def test_recovery_key_generation(self): c = self.client recovery = generate_recovery(self.adminguy, None) self.assertTrue(recovery != None) # all keys still g'fathered so true response = c.post('/login/', { 'username': '******', 'password': '******' }) self.assertEqual(response.status_code, 302) # just extra verification we can get user from django auth user = authenticate(username='******', password='******') # verify we are logged in self.assertEqual(c.session['_auth_user_id'], user.id) # get admin reset private key creds = strengthen_key(ADMIN_PASSWORD) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) recovery = generate_recovery(self.adminguy, admin_rsa) self.assertTrue(recovery != None) recovery = generate_recovery(self.adminguy, None) self.assertTrue(recovery == None) # not all keys g'fathered now # test recovery on regular user recovery = generate_recovery(self.provider, admin_rsa) self.assertTrue(recovery != None) # logout. cleanup response = c.logout() self.assertTrue('_auth_user_id' not in c.session)
def create_rsakeypair(user, keytype=RSA_PRIMARY, credtype=CRED_WEBAPP, creds=None): """ Create rsa key pair - public and private, grandfathered if no credentials """ from MHLogin._admin_reset import ADMIN_RESET_PUBLIC_RSA # create RSA public and private key instances rsa = RSA.generate(2048, os.urandom) # encrypt a copy of rsa key with admin reset pubkey adminclearkey = os.urandom(32) adminpub = import_rsa(ADMIN_RESET_PUBLIC_RSA) admincipher = b64encode(PKCS1_OAEP.new(adminpub).encrypt(adminclearkey)) adminscopy = export_rsa(rsa, adminclearkey) # now create pub/priv pair, encrypting with creds (password, pin, etc.) opub = OwnerPublicKey.objects.create( owner_id=user.id, owner_type=ContentType.objects.get_for_model(user), adminscopy=adminscopy, admincipher=admincipher, publickey=export_rsa(rsa.publickey()), keytype=keytype) upriv = UserPrivateKey.objects.create( user=user, opub=opub, credtype=credtype, privatekey=export_rsa(rsa, strengthen_key(creds or settings.SECRET_KEY)), gfather=False if creds else True) return opub, upriv
def test_gen_new_keys(self): cleartext = "A sql query walks in to a bar, approaches two tables and asks 'may I join you?'" c = self.client # after successful login should get re-direct to / response = c.post('/login/', {'username': '******', 'password': '******'}) c.COOKIES = {'ss': response.cookies['ss'].value} self.assertEqual(response.status_code, 302) # just extra verification we can get user from django auth c.user = authenticate(username='******', password='******') c.user = MHLUser.objects.get(id=c.user.id) msg = encrypt_object(SecureTestMessage, {}, cleartext) gen_keys_for_users(msg, [c.user], None, c, ivr=True) generate_new_user_keys(c.user, 'monkey') c.user.set_password('monkey') c.user.save() # logout. cleanup response = c.logout() # simulate admin update invalid keys creds = strengthen_key(ADMIN_PASSWORD) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) reset_user_invalid_keys(MHLUser.objects.get(id=c.user.id), admin_rsa) # login with new password and verify we can decrypt message response = c.post('/login/', {'username': '******', 'password': '******'}) c.COOKIES = {'ss': response.cookies['ss'].value} self.assertEqual(response.status_code, 302) # just extra verification we can get user from django auth c.user = authenticate(username='******', password='******') # verify we are logged in self.assertEqual(c.session['_auth_user_id'], c.user.id) decrypted_cleartext = decrypt_object(c, msg) self.assertEqual(decrypted_cleartext, cleartext) response = c.logout()
def admin_decrypt_cipherkey(admin_creds, obj): """ ADMIN UTILITY, TODO: move this to black box, rm #2115 """ clearkey = None ct = ContentType.objects.get_for_model(obj) encobj = EncryptedObject.objects.filter(object_type=ct, object_id=obj.pk) if encobj.exists(): from MHLogin._admin_reset import ADMIN_RESET_ENCD_RSA admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, strengthen_key(admin_creds)) # get first match since we are decrypting admincipher opub = encobj[0].opub # decrypt adminscopy rsa key for this encd object adminclearkey = PKCS1_OAEP.new(admin_rsa).decrypt(b64decode(opub.admincipher)) rsa = import_rsa(opub.adminscopy, adminclearkey) # finally, decrypt encobj's cipherkey with decrypted admins rsa copy clearkey = PKCS1_OAEP.new(rsa).decrypt(b64decode(encobj[0].cipherkey)) return clearkey
def reset_keys(uprivs_qry, admin_rsa, creds=None): """ For rm #450, static reset user keys. Obsoletes reset_user_invalid_keys() and generate_new_user_keys() when black box in office and rm #2115 done. """ for priv in uprivs_qry: clearkey = PKCS1_OAEP.new(admin_rsa).decrypt(b64decode(priv.opub.admincipher)) rsa = import_rsa(priv.opub.adminscopy, clearkey) priv.privatekey = export_rsa(rsa, strengthen_key(creds or settings.SECRET_KEY)) priv.gfather = False if creds else True priv.save()
def recrypt_ivr_key_via_web_creds(user, request, new_pin): """ decrypt special web enc'd ivr key then recrypt pin enc'd ivr key with new pin """ opub = OwnerPublicKey.objects.get_pubkey(owner=user, keytype=RSA_IVR) uprivw = UserPrivateKey.objects.get_privkey(user, opub, CRED_WEBAPP, gfather=False) rsa = import_rsa(uprivw.privatekey, get_user_key(request)) # ivr pin may still be g'fathered uprivi = UserPrivateKey.objects.get_privkey(user, opub, CRED_IVRPIN) uprivi.privatekey = export_rsa(rsa, strengthen_key(new_pin)) uprivi.gfather = False uprivi.save()
def admin_decrypt_cipherkey(admin_creds, obj): """ ADMIN UTILITY, TODO: move this to black box, rm #2115 """ clearkey = None ct = ContentType.objects.get_for_model(obj) encobj = EncryptedObject.objects.filter(object_type=ct, object_id=obj.pk) if encobj.exists(): from MHLogin._admin_reset import ADMIN_RESET_ENCD_RSA admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, strengthen_key(admin_creds)) # get first match since we are decrypting admincipher opub = encobj[0].opub # decrypt adminscopy rsa key for this encd object adminclearkey = PKCS1_OAEP.new(admin_rsa).decrypt( b64decode(opub.admincipher)) rsa = import_rsa(opub.adminscopy, adminclearkey) # finally, decrypt encobj's cipherkey with decrypted admins rsa copy clearkey = PKCS1_OAEP.new(rsa).decrypt(b64decode(encobj[0].cipherkey)) return clearkey
def reset_keys(uprivs_qry, admin_rsa, creds=None): """ For rm #450, static reset user keys. Obsoletes reset_user_invalid_keys() and generate_new_user_keys() when black box in office and rm #2115 done. """ for priv in uprivs_qry: clearkey = PKCS1_OAEP.new(admin_rsa).decrypt( b64decode(priv.opub.admincipher)) rsa = import_rsa(priv.opub.adminscopy, clearkey) priv.privatekey = export_rsa( rsa, strengthen_key(creds or settings.SECRET_KEY)) priv.gfather = False if creds else True priv.save()
def test_login_and_rsa_key_import_export(self): c = self.client response = c.post('/login/', { 'username': '******', 'password': '******' }) self.assertEqual(response.status_code, 302) # just extra verification we can get user from django auth user = authenticate(username='******', password='******') user = MHLUser.objects.get(id=user.id) # verify we are logged in self.assertEqual(c.session['_auth_user_id'], user.id) # These should match: self.assertEqual(get_user_key(c, response.cookies['ss'].value), strengthen_key('healme')) # query our KeyPair we created in setup opub = OwnerPublicKey.objects.get_pubkey(owner=user) upriv = UserPrivateKey.objects.get_privkey(user=user, opub=opub) devnull.write(unicode(upriv)) # simulate __unicode__ # grab pubkey from db its in RSA export format but double test import/export pub_key_from_db = export_rsa( import_rsa(upriv.opub.publickey).publickey()) # grab our binary user credential creds = get_user_key(c, response.cookies['ss'].value) # properly import rsa_key = import_rsa(upriv.privatekey, creds) # verify we can properly create RSA objs by importing pub_key_from_key = export_rsa(rsa_key.publickey()) # verify match self.assertEqual(pub_key_from_db, pub_key_from_key) # now test decryption of key with admin credentials creds = strengthen_key(ADMIN_PASSWORD) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) adminclear = PKCS1_OAEP.new(admin_rsa).decrypt( b64decode(upriv.opub.admincipher)) rsa_key = import_rsa(upriv.opub.adminscopy, adminclear) # verify match self.assertEqual(pub_key_from_db, export_rsa(rsa_key.publickey())) # now logout, we can alternatively call c.post('/logout/') response = c.logout() self.assertTrue('_auth_user_id' not in c.session)
def test_rekey_reset(self): cleartext = "Who is the best super hero of all time?" c = self.client response = c.post('/login/', { 'username': '******', 'password': '******' }) self.assertEqual(response.status_code, 302) # just extra verification we can get user from django auth user = authenticate(username='******', password='******') user = MHLUser.objects.get(id=user.id) # verify we are logged in self.assertEqual(c.session['_auth_user_id'], user.id) # get users' public key opub = OwnerPublicKey.objects.get_pubkey(owner=user) # encrypt_object encrypts w/user pub key msg = encrypt_object(SecureTestMessage, {}, cleartext, opub) # encrypt_object() creates EncryptedObject encobj = EncryptedObject.objects.get_object(msg, opub) # test decryption of keys with user's credentials when not grandfathered upriv = UserPrivateKey.objects.get(user=user, opub=opub) clearkey = encobj.decrypt_cipherkey(upriv, strengthen_key('healme')) # now call the object's decrypt method decrypted_cleartext = msg.decrypt(c, clearkey) # verify they do match after decryption self.assertTrue(decrypted_cleartext == cleartext) # get admin reset key creds = strengthen_key(ADMIN_PASSWORD) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) privs = UserPrivateKey.objects.filter(user=user, credtype=CRED_WEBAPP) reset_keys(privs, admin_rsa) # fetch the encrypted object again but decrypt with initial settings encobj = EncryptedObject.objects.get_object(msg, opub) # test decryption with initial credentials when grandfathered upriv = UserPrivateKey.objects.get(user=user, opub=opub) clearkey = encobj.decrypt_cipherkey( upriv, strengthen_key(settings.SECRET_KEY)) # now call the object's decrypt method decrypted_cleartext = msg.decrypt(c, clearkey) # verify they do match after decryption self.assertTrue(decrypted_cleartext == cleartext) # now logout, we can alternatively call c.post('/logout/') response = c.logout() self.assertTrue('_auth_user_id' not in c.session) # try resetting admins keys privs = UserPrivateKey.objects.filter(user=self.adminguy, credtype=CRED_WEBAPP) reset_keys(privs, admin_rsa) # test str rep of encobj self.assertEqual( unicode(encobj), u"SecureTestMessage object %s, key type: %s" % (user, RSA_TYPES[encobj.opub.keytype]), encobj)
def generate_recovery(user, admin_rsa=None): """ Generate recovery key: must provide admin creds or all keys g'fathered """ recovery_key, recovery = os.urandom(32), None if admin_rsa: # generate recovery regardless of g'fathered state when given admin_rsa for priv in UserPrivateKey.objects.filter(user=user): clearkey = PKCS1_OAEP.new(admin_rsa).decrypt(b64decode(priv.opub.admincipher)) rsa = import_rsa(priv.opub.adminscopy, clearkey) priv.recovery = export_rsa(rsa, recovery_key) priv.save() recovery = b16encode(recovery_key) elif not UserPrivateKey.objects.filter(user=user, gfather=False).exists(): # no admin (e.g. user signup) but all keys must be g'fathered initial_creds = strengthen_key(settings.SECRET_KEY) for priv in UserPrivateKey.objects.filter(user=user): rsa = import_rsa(priv.privatekey, initial_creds) priv.recovery = export_rsa(rsa, recovery_key) priv.save() recovery = b16encode(recovery_key) if not recovery: logger.critical("Recovery key not created for user: %s" % user) return recovery # send to user, don't store anywhere
def recrypt_keys(uprivs_qry, old_creds, new_creds, strengthd=False): """ Recrypt keys when user changes password/pin or un-grandfathered. :param strengthd: Handle annoying corner case, IVR does not ask for old pin when user wants to change pin via IVR, we have only userkey, i.e. strengthen_key(pin). TODO: Redmine so behavior is same as web/app. :raises: ValueError if old_creds incorrect """ old_creds = old_creds if strengthd else strengthen_key(old_creds) new_creds = new_creds if strengthd else strengthen_key(new_creds) for priv in uprivs_qry: rsa = import_rsa(priv.privatekey, old_creds) priv.privatekey = export_rsa(rsa, new_creds) priv.gfather = False priv.save()
def generate_recovery(user, admin_rsa=None): """ Generate recovery key: must provide admin creds or all keys g'fathered """ recovery_key, recovery = os.urandom(32), None if admin_rsa: # generate recovery regardless of g'fathered state when given admin_rsa for priv in UserPrivateKey.objects.filter(user=user): clearkey = PKCS1_OAEP.new(admin_rsa).decrypt( b64decode(priv.opub.admincipher)) rsa = import_rsa(priv.opub.adminscopy, clearkey) priv.recovery = export_rsa(rsa, recovery_key) priv.save() recovery = b16encode(recovery_key) elif not UserPrivateKey.objects.filter(user=user, gfather=False).exists(): # no admin (e.g. user signup) but all keys must be g'fathered initial_creds = strengthen_key(settings.SECRET_KEY) for priv in UserPrivateKey.objects.filter(user=user): rsa = import_rsa(priv.privatekey, initial_creds) priv.recovery = export_rsa(rsa, recovery_key) priv.save() recovery = b16encode(recovery_key) if not recovery: logger.critical("Recovery key not created for user: %s" % user) return recovery # send to user, don't store anywhere
def create_default_keys(user, webcreds=None, ivrcreds=None): """ Helper to create standard user setup: 1. Web/App RSA key encrypted w/web credentials (password) 2. IVR RSA key encrypted w/ivr credentials (pin) 3. Same IVR RSA key encrypted w/web credentials """ # 1. Web/App RSA key encrypted w/web credentials create_rsakeypair(user, RSA_PRIMARY, CRED_WEBAPP, webcreds) # 2. IVR RSA key encrypted w/IVR PIN credentials ipub, ipriv = create_rsakeypair(user, RSA_IVR, CRED_IVRPIN, ivrcreds) # 3. Same IVR key, this is so users can change a forgotten pin via web rsa = import_rsa(ipriv.privatekey, strengthen_key(ivrcreds or settings.SECRET_KEY)) UserPrivateKey.objects.create(user=user, opub=ipub, credtype=CRED_WEBAPP, privatekey=export_rsa(rsa, strengthen_key(webcreds or settings.SECRET_KEY)), gfather=False if webcreds else True)
def test_login_and_rsa_key_import_export(self): c = self.client response = c.post('/login/', {'username': '******', 'password': '******'}) self.assertEqual(response.status_code, 302) # just extra verification we can get user from django auth user = authenticate(username='******', password='******') user = MHLUser.objects.get(id=user.id) # verify we are logged in self.assertEqual(c.session['_auth_user_id'], user.id) # These should match: self.assertEqual(get_user_key(c, response.cookies['ss'].value), strengthen_key('healme')) # query our KeyPair we created in setup opub = OwnerPublicKey.objects.get_pubkey(owner=user) upriv = UserPrivateKey.objects.get_privkey(user=user, opub=opub) devnull.write(unicode(upriv)) # simulate __unicode__ # grab pubkey from db its in RSA export format but double test import/export pub_key_from_db = export_rsa(import_rsa(upriv.opub.publickey).publickey()) # grab our binary user credential creds = get_user_key(c, response.cookies['ss'].value) # properly import rsa_key = import_rsa(upriv.privatekey, creds) # verify we can properly create RSA objs by importing pub_key_from_key = export_rsa(rsa_key.publickey()) # verify match self.assertEqual(pub_key_from_db, pub_key_from_key) # now test decryption of key with admin credentials creds = strengthen_key(ADMIN_PASSWORD) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) adminclear = PKCS1_OAEP.new(admin_rsa).decrypt(b64decode(upriv.opub.admincipher)) rsa_key = import_rsa(upriv.opub.adminscopy, adminclear) # verify match self.assertEqual(pub_key_from_db, export_rsa(rsa_key.publickey())) # now logout, we can alternatively call c.post('/logout/') response = c.logout() self.assertTrue('_auth_user_id' not in c.session)
def test_rekey_reset(self): cleartext = "Who is the best super hero of all time?" c = self.client response = c.post('/login/', {'username': '******', 'password': '******'}) self.assertEqual(response.status_code, 302) # just extra verification we can get user from django auth user = authenticate(username='******', password='******') user = MHLUser.objects.get(id=user.id) # verify we are logged in self.assertEqual(c.session['_auth_user_id'], user.id) # get users' public key opub = OwnerPublicKey.objects.get_pubkey(owner=user) # encrypt_object encrypts w/user pub key msg = encrypt_object(SecureTestMessage, {}, cleartext, opub) # encrypt_object() creates EncryptedObject encobj = EncryptedObject.objects.get_object(msg, opub) # test decryption of keys with user's credentials when not grandfathered upriv = UserPrivateKey.objects.get(user=user, opub=opub) clearkey = encobj.decrypt_cipherkey(upriv, strengthen_key('healme')) # now call the object's decrypt method decrypted_cleartext = msg.decrypt(c, clearkey) # verify they do match after decryption self.assertTrue(decrypted_cleartext == cleartext) # get admin reset key creds = strengthen_key(ADMIN_PASSWORD) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) privs = UserPrivateKey.objects.filter(user=user, credtype=CRED_WEBAPP) reset_keys(privs, admin_rsa) # fetch the encrypted object again but decrypt with initial settings encobj = EncryptedObject.objects.get_object(msg, opub) # test decryption with initial credentials when grandfathered upriv = UserPrivateKey.objects.get(user=user, opub=opub) clearkey = encobj.decrypt_cipherkey(upriv, strengthen_key(settings.SECRET_KEY)) # now call the object's decrypt method decrypted_cleartext = msg.decrypt(c, clearkey) # verify they do match after decryption self.assertTrue(decrypted_cleartext == cleartext) # now logout, we can alternatively call c.post('/logout/') response = c.logout() self.assertTrue('_auth_user_id' not in c.session) # try resetting admins keys privs = UserPrivateKey.objects.filter(user=self.adminguy, credtype=CRED_WEBAPP) reset_keys(privs, admin_rsa) # test str rep of encobj self.assertEqual(unicode(encobj), u"SecureTestMessage object %s, key type: %s" % (user, RSA_TYPES[encobj.opub.keytype]), encobj)
def create_rsakeypair(user, keytype=RSA_PRIMARY, credtype=CRED_WEBAPP, creds=None): """ Create rsa key pair - public and private, grandfathered if no credentials """ from MHLogin._admin_reset import ADMIN_RESET_PUBLIC_RSA # create RSA public and private key instances rsa = RSA.generate(2048, os.urandom) # encrypt a copy of rsa key with admin reset pubkey adminclearkey = os.urandom(32) adminpub = import_rsa(ADMIN_RESET_PUBLIC_RSA) admincipher = b64encode(PKCS1_OAEP.new(adminpub).encrypt(adminclearkey)) adminscopy = export_rsa(rsa, adminclearkey) # now create pub/priv pair, encrypting with creds (password, pin, etc.) opub = OwnerPublicKey.objects.create(owner_id=user.id, owner_type=ContentType.objects.get_for_model(user), adminscopy=adminscopy, admincipher=admincipher, publickey=export_rsa(rsa.publickey()), keytype=keytype) upriv = UserPrivateKey.objects.create(user=user, opub=opub, credtype=credtype, privatekey=export_rsa(rsa, strengthen_key(creds or settings.SECRET_KEY)), gfather=False if creds else True) return opub, upriv
def create_default_keys(user, webcreds=None, ivrcreds=None): """ Helper to create standard user setup: 1. Web/App RSA key encrypted w/web credentials (password) 2. IVR RSA key encrypted w/ivr credentials (pin) 3. Same IVR RSA key encrypted w/web credentials """ # 1. Web/App RSA key encrypted w/web credentials create_rsakeypair(user, RSA_PRIMARY, CRED_WEBAPP, webcreds) # 2. IVR RSA key encrypted w/IVR PIN credentials ipub, ipriv = create_rsakeypair(user, RSA_IVR, CRED_IVRPIN, ivrcreds) # 3. Same IVR key, this is so users can change a forgotten pin via web rsa = import_rsa(ipriv.privatekey, strengthen_key(ivrcreds or settings.SECRET_KEY)) UserPrivateKey.objects.create(user=user, opub=ipub, credtype=CRED_WEBAPP, privatekey=export_rsa( rsa, strengthen_key(webcreds or settings.SECRET_KEY)), gfather=False if webcreds else True)
def reset_private_keys(request): """ This view takes the KMS administrator password, and for each user whose password was reset, generates new private keys. :param request: The HTTP request :type request: django.core.handlers.wsgi.WSGIRequest :returns: django.http.HttpResponse -- The webpage """ context, resp = get_context(request), None log_qs = PasswordResetLog.objects.filter(reset=True, resolved=False).values('user') reset_users = context['reset_users'] = MHLUser.objects.filter( pk__in=log_qs) if request.method == 'POST': form = context['form'] = PasswordForm(request.POST) if form.is_valid(): try: from MHLogin._admin_reset import ADMIN_RESET_ENCD_RSA creds = strengthen_key(form.cleaned_data['password']) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) except ValueError: msg = _('Invalid password for admin reset key.') form._errors['password'] = form.error_class([msg]) else: for user in reset_users: reset_user_invalid_keys(user, admin_rsa) PasswordResetLog.objects.filter( reset=True, resolved=False, user=user).update( resolved=True, servicer=request.user, servicer_ip=request.META['REMOTE_ADDR'], resolution_timestamp=datetime.datetime.now()) resp = render_to_response('PrivateKeyResetComplete.html', context) else: context['form'] = PasswordForm() return resp or render_to_response('PrivateKeyReset.html', context)
def handle(self, *args, **options): if (len(args) != 1): sys.stderr.write("Admin password required.\n") self.print_help(sys.argv[1], "") else: from MHLogin._admin_reset import ADMIN_RESET_ENCD_RSA try: force = options.pop('force', False) logoff = options.pop('logoff', False) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, strengthen_key(args[0])) migrate_admin_objectkeys(admin_rsa, force) # Remove active sessions, users need to log in. During initial migration # rsa keys are grandfathered since we do not know users's password. # And we don't want specialized code in KMS checking for this single # use-case where keys are grandfathered while user logged in. if logoff: Session.objects.filter(expire_date__gte=datetime.now()).delete() SmartPhoneAssn.objects.filter(is_active=True).update(is_active=False) except ValueError: sys.stderr.write("Invalid password.\n")
def test_gen_new_keys(self): cleartext = "A sql query walks in to a bar, approaches two tables and asks 'may I join you?'" c = self.client # after successful login should get re-direct to / response = c.post('/login/', { 'username': '******', 'password': '******' }) c.COOKIES = {'ss': response.cookies['ss'].value} self.assertEqual(response.status_code, 302) # just extra verification we can get user from django auth c.user = authenticate(username='******', password='******') c.user = MHLUser.objects.get(id=c.user.id) msg = encrypt_object(SecureTestMessage, {}, cleartext) gen_keys_for_users(msg, [c.user], None, c, ivr=True) generate_new_user_keys(c.user, 'monkey') c.user.set_password('monkey') c.user.save() # logout. cleanup response = c.logout() # simulate admin update invalid keys creds = strengthen_key(ADMIN_PASSWORD) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) reset_user_invalid_keys(MHLUser.objects.get(id=c.user.id), admin_rsa) # login with new password and verify we can decrypt message response = c.post('/login/', { 'username': '******', 'password': '******' }) c.COOKIES = {'ss': response.cookies['ss'].value} self.assertEqual(response.status_code, 302) # just extra verification we can get user from django auth c.user = authenticate(username='******', password='******') # verify we are logged in self.assertEqual(c.session['_auth_user_id'], c.user.id) decrypted_cleartext = decrypt_object(c, msg) self.assertEqual(decrypted_cleartext, cleartext) response = c.logout()
def test_recovery_key_generation(self): c = self.client recovery = generate_recovery(self.adminguy, None) self.assertTrue(recovery != None) # all keys still g'fathered so true response = c.post('/login/', {'username': '******', 'password': '******'}) self.assertEqual(response.status_code, 302) # just extra verification we can get user from django auth user = authenticate(username='******', password='******') # verify we are logged in self.assertEqual(c.session['_auth_user_id'], user.id) # get admin reset private key creds = strengthen_key(ADMIN_PASSWORD) admin_rsa = import_rsa(ADMIN_RESET_ENCD_RSA, creds) recovery = generate_recovery(self.adminguy, admin_rsa) self.assertTrue(recovery != None) recovery = generate_recovery(self.adminguy, None) self.assertTrue(recovery == None) # not all keys g'fathered now # test recovery on regular user recovery = generate_recovery(self.provider, admin_rsa) self.assertTrue(recovery != None) # logout. cleanup response = c.logout() self.assertTrue('_auth_user_id' not in c.session)