pyauth / pyotp

Python One-Time Password Library
https://pyauth.github.io/pyotp/
Other
3.01k stars 324 forks source link

I encountered a very confusing problem。 #172

Closed zhenghaiyang closed 3 weeks ago

zhenghaiyang commented 3 weeks ago

The dynamic password generated by the otplib.totp package of Node.js is inconsistent with the dynamic password generated by pyotp.totp。

The Secret settings are the same is all f8a82760-577b-4225-adfa-89f021b7ec93, the digest is: sha-1, and the interval is: 30. base32Encode is all 7CUCOYCXPNBCLLP2RHYCDN7MSM. But the encrypted dynamic password is inconsistent.

I know that pytop's time takes seconds. node.js takes milliseconds. But they are all at the same time, and theoretically the encrypted dynamic passwords should be consistent.

why is that?

finally thank You。

peterthomassen commented 3 weeks ago

Please provide code to reproduce a particular value, and explain what value it should be instead.

zhenghaiyang commented 3 weeks ago

Please provide code to reproduce a particular value, and explain what value it should be instead.

Node.js

Code

` const { v4: uuidv4, parse: uuidParse } = require("uuid");

const base32 = require("base32.js");

const { totp } = require("otplib");

// 将 UUID 转换为字节数组
const userKey = "f8a82760-577b-4225-adfa-89f021b7ec93"

const uuidBytes = uuidParse(userKey);

// 使用 Base32 编码
const encoder = new base32.Encoder({ type: "rfc4648", lc: false });

const base32Encode = encoder.write(uuidBytes).finalize();

const userKeyBase32Encode = base32Encode.replace(/=+$/, ""); // 去掉填充字符

const syncPassword = totp.generate(userKeyBase32Encode)

`

Python

Code

` import pyotp

import uuid

import base64

self.userKey = "f8a82760-577b-4225-adfa-89f021b7ec93"

uuid_bytesStr = uuid.UUID(self.userKey)

uuid_bytes = uuid.UUID(self.userKey).bytes

userKey_base32_encode = base64.b32encode(uuid_bytes)

base32_str = userKey_base32_encode.decode('utf-8').rstrip('=')

totp = pyotp.TOTP(base32_str, digest='sha1', interval=30)

otp_for_specified_time = totp.now()

`

The six-digit dynamic passwords returned by these two pieces of code are different. I checked the API documentation and found that "pyotp" is the unit of seconds. "otplib" is the unit of milliseconds. I understand that since we are getting the current time, both seconds and milliseconds can indicate the current time. Why are the values ​​obtained different?

peterthomassen commented 3 weeks ago

Thank you. (The example could be simplified by setting userKeyBase32Encode and base32_str directly to some value, instead of going through the UUID magic. The result of that magic is 7CUCOYCXPNBCLLP2RHYCDN7MSM.)

I find that pyotp generates the same result as other OTP generators, such as this one: https://it-tools.tech/otp-generator

Indeed, the result of otplib's totp.generate does not match. However, it does match when using otplib's authenticator.generate. I'm not sure why that is, but this is the wrong place to figure this out. :-)

FWIW, this has been raised there four years ago, without an answer: https://github.com/yeojz/otplib/issues/439

@kislyuk Let's close this.

kislyuk commented 3 weeks ago

Thanks for investigating @peterthomassen