如何让py-scrypt的"simple password verifier"示例函数工作?



我正在使用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工作。

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

UnicodeDecodeError Exception 的解释

原因1

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

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

同样在 Python 的 UnicodeHOWTO 部分Python 的 Unicode 支持 --> 字符串类型,它写道

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

errors 参数指定输入字符串无法时的响应 根据编码的规则进行转换。此法律价值 参数是"严格的"(引发UnicodeDecodeError异常), "替换"(使用 U+FFFD,替换字符),"忽略"(离开 Unicode 结果中的字符),或"反斜杠替换" (插入 \xNN 转义序列)。

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

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

然而,进一步检查 Python 对 str 类的解释,我发现解释是这样的:

如果对象是字节(

或字节数组)对象,则 str(字节, 编码, errors)等效于bytes.decode(encoding,errors)。

这意味着没有在str(bytes, encoding)中定义error参数,这个str类默认返回bytes.decode(encoding, errors='strict'),并在str字符串映射到Unicode字符时返回UnicodeDecodeError异常。

原因2

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

最新更新