def create(self, cb, pwd, user, host, char_classes, size=0): if set(char_classes) - {'u', 'l', 's', 'd'}: raise ValueError("error: rules can only contain ulsd.") try: size = int(size) except: raise ValueError("error: size has to be integer.") self.namesite = {'name': user, 'site': host} rules = sum(1 << i for i, c in enumerate(('u', 'l', 's', 'd')) if c in char_classes) # pack rule rule = struct.pack('>H', (rules << 7) | (size & 0x7f)) # encrypt rule sk = self.getkey() rk = pysodium.crypto_generichash(sk, self.getsalt()) nonce = pysodium.randombytes(pysodium.crypto_secretbox_NONCEBYTES) rule = nonce + pysodium.crypto_secretbox(rule, nonce, rk) b, c = sphinxlib.challenge(pwd) message = b''.join([ CREATE, self.getid(host, user), c, rule, pysodium.crypto_sign_sk_to_pk(sk) ]) self.doSphinx(message, b, pwd, cb)
def change(self, cb, pwd, user, host): b, c = sphinxlib.challenge(pwd) self.namesite={'name': user, 'site': host} message = b''.join([CHANGE, self.getid(host, user), c]) self.doSphinx(message, host, b, pwd, cb)
def delete(s, pwd, user, host): # run sphinx to recover rwd for authentication id = getid(host, user) r, alpha = sphinxlib.challenge(pwd) msg = b''.join([DELETE, id, alpha]) s.send(msg) # alpha rwd = auth(s, id, pwd, r) # delete user from user list for this host # a malicous server could correlate all accounts on this services to this users here # first query user record for this host id = getid(host, '') s.send(id) # wait for user blob bsize = s.recv(2) bsize = struct.unpack('!H', bsize)[0] if bsize == 0: # this should not happen, it means something is corrupt print("error: server has no associated user record for this host") return blob = s.recv(bsize) # todo handle this if blob == b'fail': return blob = decrypt_blob(blob) users = set(blob.decode().split('\x00')) # todo/fix? we do not recognize if the user is already included in this list # this should not happen, but maybe it's a sign of corruption? users.remove(user) blob = ('\x00'.join(sorted(users))).encode() # notice we do not add rwd to encryption of user blobs blob = encrypt_blob(blob) bsize = len(blob) if bsize >= 2**16: raise ValueError("error: blob is bigger than 64KB.") blob = struct.pack("!H", bsize) + blob blob = sign_blob(blob, id, b'') s.send(blob) if b'ok' != s.recv(2): return clearmem(rwd) return True
def doSphinx(s, op, pwd, user, host): id = getid(host, user) r, alpha = sphinxlib.challenge(pwd) msg = b''.join([op, id, alpha]) s.send(msg) if op != GET: # == CHANGE, UNDO, COMMIT # auth: do sphinx with current seed, use it to sign the nonce auth(s, id, pwd, r) resp = s.recv(32 + RULE_SIZE) # beta + sealed rules if resp == b'\x00\x04fail' or len(resp) != 32 + RULE_SIZE: raise ValueError("error: sphinx protocol failure.") beta = resp[:32] rules = resp[32:] rwd = sphinxlib.finish(pwd, r, beta, id) try: classes, size = unpack_rule(rules) except ValueError: return if op != GET: # == CHANGE, UNDO, COMMIT # in case of undo/commit we also need to rewrite the rules and pub auth signing key blob if op in {UNDO, COMMIT}: sk, pk = get_signkey(id, rwd) clearmem(sk) rule = encrypt_blob(pack_rule(classes, size)) # send over new signed(pubkey, rule) msg = b''.join([pk, rule]) msg = sign_blob(msg, id, rwd) s.send(msg) if s.recv(2) != b'ok': print( "ohoh, something is corrupt, and this is a bad, very bad error message in so many ways" ) return ret = bin2pass.derive(pysodium.crypto_generichash(PASS_CTX, rwd), classes, size).decode() clearmem(rwd) return ret
def create(s, pwd, user, host, char_classes, size=0): # 1st step OPRF on the new seed id = getid(host, user) r, alpha = sphinxlib.challenge(pwd) msg = b''.join([CREATE, id, alpha]) s.send(msg) # wait for response from sphinx server beta = s.recv(32) if beta == b'\x00\x04fail': raise ValueError("error: sphinx protocol failure.") rwd = sphinxlib.finish(pwd, r, beta, id) # second phase, derive new auth signing pubkey sk, pk = get_signkey(id, rwd) clearmem(sk) try: size = int(size) except: raise ValueError("error: size has to be integer.") rule = encrypt_blob(pack_rule(char_classes, size)) # send over new signed(pubkey, rule) msg = b''.join([pk, rule]) msg = sign_blob(msg, id, rwd) s.send(msg) # add user to user list for this host # a malicous server could correlate all accounts on this services to this users here update_rec(s, host, user) ret = bin2pass.derive(pysodium.crypto_generichash(PASS_CTX, rwd), char_classes, size).decode() clearmem(rwd) return ret