1

我正在使用提供的示例脚本py-scrypt来构建一个简单的密码验证器。下面是我的测试脚本。

测试脚本:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import scrypt
import os

def hash2_password(a_secret_message, password, maxtime=0.5, datalength=64):
    #return scrypt.encrypt(a_secret_message, password, maxtime=maxtime)
    return scrypt.encrypt(os.urandom(datalength), password, maxtime=maxtime)

def verify2_password(data, password, maxtime=0.5):
    try:
        secret_message = scrypt.decrypt(data, password, maxtime)
        print('\nDecrypted secret message:', secret_message)
        return True
    except scrypt.error:
        return False


password2 = 'Baymax'
secret_message2 = "Go Go"
data2 = hash2_password(secret_message2, password2, maxtime=0.1, datalength=64)
print('\nEncrypted secret message2:')
print(data2)

password_ok = verify2_password(data2, password2, maxtime=0.1)
print('\npassword_ok? :', password_ok)

问题: 我经常收到错误消息,例如:

Traceback (most recent call last):
  File "~/example_scrypt_v1.py", line 56, in <module>
    password_ok = verify2_password(data2, password2, maxtime=0.1)
  File "~/example_scrypt_v1.py", line 43, in verify2_password
    secret_message = scrypt.decrypt(data, password, maxtime)
  File "~/.local/lib/python3.5/site-packages/scrypt/scrypt.py", line 188, in decrypt
    return str(out_bytes, encoding)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xca in position 0: invalid continuation byte

最后几行变化为例如:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xaf in position 3: invalid start byte

或者

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xee in position 1: invalid continuation byte

或没有错误信息但返回 False

password_ok? : False

当我评论return scrypt.encrypt(os.urandom(datalength), password, maxtime=maxtime)删除随机秘密消息生成器并取消评论return scrypt.encrypt(a_secret_message, password, maxtime=maxtime)以使用非随机秘密消息时,该功能verify2_password起作用。

问题:如何让随机秘密消息元素起作用?是什么导致它的失败?

4

1 回答 1

0

UnicodeDecodeError 异常说明

原因一

我想我明白为什么 Scrypt 发布UnicodeDecodeError. 引用Python 的 UnicodeDecodeError

UnicodeDecodeError 通常在从特定编码解码 str 字符串时发生。由于编码仅将有限数量的 str 字符串映射到 unicode 字符,因此 str 字符的非法序列将导致特定于编码的 decode() 失败。

同样在Python 的 Unicode HOWTO部分Python's Unicode Support --> The String Type中,它写道

此外,可以使用字节的 decode() 方法创建一个字符串。此方法采用编码参数,例如 UTF-8,以及可选的错误参数

errors 参数指定当输入字符串无法根据编码规则转换时的响应。此参数的合法值是 'strict'(引发 UnicodeDecodeError 异常)、'replace'(使用 U+FFFD、REPLACEMENT CHARACTER)、'ignore'(仅将字符留在 Unicode 结果之外)或 'backslashreplace'(插入一个 \xNN 转义序列)。

简而言之,每当 Python 的.decode()方法无法将str字符串映射到 unicode 字符时,并且当它使用strict参数时,该.decode()方法将返回UnicodeDecodeError异常。

我试图.decode().decrypt()方法中找到方法py-scrypt/scrypt/scrypt.py。最初,我找不到它。对于 Python3,.decrypt()方法返回语句为: return str(out_bytes, encoding)

但是,进一步检查 Python 对str类的解释,我发现解释说:

如果 object 是字节(或 bytearray)对象,则 str(bytes, encoding, errors) 等价于 bytes.decode(encoding, errors)。

这意味着如果不定义 in 中的error参数str(bytes, encoding),则这个 str 类默认返回bytes.decode(encoding, errors='strict')并在UnicodeDecodeError未能将str字符串映射到 unicode 字符时返回异常。

原因二

在“简单密码验证器”示例中,inputScrypt.encrypt() 的参数被定义为os.urandom(datalength)返回一个<class 'bytes'>. 当它<class 'bytes'>被加密并随后被 解密时Scrypt.decrypt(),返回的解密值也必须是 a <class 'bytes'>。根据.decrypt()方法的 doc_string ,对于 Python3,如果使用 encoding 编码,此方法将返回一个 str 实例。如果encoding=None,它将返回一个字节实例。Script.decrypt()默认为inencoding='utf-8'函数verify2_password()Script.decrypt()尝试返回 a 会<class str>导致UnicodeDecodeError.

py-scrypt中给出的“简单密码验证器”示例脚本的解决方案:

  1. verify_password()函数应包含参数encoding=None
  2. scrypt.decrypt()应该包含参数encoding=encoding

修改后的示例脚本:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import scrypt
import os

def encrypt_password(password, maxtime=0.5, datalength=64):
    passphrase = os.urandom(datalength)
    print('\npassphrase = ', passphrase, type(passphrase))
    return scrypt.encrypt(passphrase, password, maxtime=maxtime)

def verify_password(encrpyted_passphrase, password, maxtime=0.5, encoding=None):
    try:
        passphrase = scrypt.decrypt(encrpyted_passphrase, password, maxtime,
                                    encoding=encoding)
        print('\npassphrase = ', passphrase, type(passphrase))
        return True
    except scrypt.error:
        return False


password = 'Baymax'
encrypted_passphrase = encrypt_password(password, maxtime=0.5, datalength=64)
print('\nEncrypted PassPhrase:')
print(encrypted_passphrase)

password_ok = verify_password(encrypted_passphrase, password, maxtime=0.5)
print('\npassword_ok? :', password_ok)
于 2018-03-22T17:15:46.667 回答