python使用sm3实现otp【动态口令】

部署运行你感兴趣的模型镜像
"""
Author: tanglei
DateTime:2024-11-18 完成
微信:ciss_cedar
欢迎一起学习
"""

import re
from datetime import datetime

from sm3_apply  import sm3_hash


#key 16进制 16字节密钥
#T-参与运算的时间因子(5.2.1的计算结果);
#C-参与运算的事件因子,表示为一个4字节整数; 2^32
#Q --鉴别双方通过协商输入的挑战因子(或其他类型参与运算的因子),使用ASCII码表示
#N 动态口令长度最小为6
#Period otp 周期,60代表60秒有效
def gen_otp(Key,T,C,Q,N,Period=60):
    """
    :param key:key 16进制 16字节密钥
    :param T:T-参与运算的时间因子,传入当前时间
    :param C:参与运算的事件因子,表示为一个4字节整数; 2^32,
    :param Q:鉴别双方通过协商输入的挑战因子可以和平台协调一个随机数
    :param N:动态口令长度最小为6
    :param Period:otp 周期,60代表60秒有效
    :return:
    """
    t = calculate_t(T,Period)
    id = calculate_id(t,C,Q)
    s = calculate_s(Key, id)
    od = calculate_od(s)
    p = calculate_p(od,N)
    return p

def calculate_t(input_time:datetime,Period=60):
    # 获取当前时间的UTC秒数(从某个固定时间点,通常是1970年1月1日00:00:00开始)

    c_seconds=input_time.timestamp()
    c_seconds=int(c_seconds)

    # 由于我们只需要一个周期内的递增值,我们可以对当前时间秒数进行模运算
    # 这里我们假设period是60秒或30秒等,确保它不大于60秒
    t = c_seconds // Period  # 使用整除来获取当前周期内的“序号”

    # 如果需要,可以对time_factor进行进一步处理,比如转换为8字节整数格式(这里简单返回)
    return t

def calculate_id(T=None, C=None, Q=None):

    if C is not None and len(str(C))==0:
        C=None
    if Q is not None and len(str(Q))==0:
        Q=None

    if T is None and C is None:
        raise ValueError("At least one of T or C must be provided.")

        # Convert integers to byte representation
    if C is not None:
        C_bytes = C.to_bytes(4, 'big')  # 4 bytes for a 32-bit integer
    else:
        C_bytes = b''

        # Ensure Q is a string and contains at least 4 characters
    if Q is not None:
        if not isinstance(Q, str) or len(Q) < 4:
            raise ValueError("Q must be a string with at least 4 characters.")
            # Convert Q to byte representation using ASCII
        #将难以识别的字符(例如空格)或容易混淆的字符(例如数字1和字母1、数字0和字母O、字符’和字符、。替换为空字符
        pattern = re.compile(r"[ 1I0O'、]", flags=re.IGNORECASE)
        Q = pattern.sub('', Q)

        Q_bytes = Q.encode('ascii')
    else:
        Q_bytes = b''

        # Convert T to byte representation (assuming it's a string and using UTF-8 encoding)
    if T is not None:
        T=str(T)
        T_bytes = T.encode('utf-8')
    else:
        T_bytes = b''

        # Combine the bytes
    combined_bytes = T_bytes + C_bytes + Q_bytes

    # Pad with '0' (null bytes) to ensure the length is at least 16 bytes (128 bits)
    while len(combined_bytes) < 16:
        combined_bytes += b'\x00'

        # Ensure the length is exactly 16 bytes by truncating if necessary
    id = combined_bytes[:16]

    # Convert the final byte sequence back to a hexadecimal string for easy viewing (optional)
    # final_hex = combined_bytes.hex()

    return id

def calculate_s(key,id):
    key=bytes.fromhex(key)
    input=key+id
    input=input.hex()
    s=sm3_hash(input)
    return s

def calculate_od(s:str):
    """
    8个整数相加,并截断为32比特。
    """
    # 计算8个整数的和
    s1 = int(s[0:8],16)
    s2 = int(s[8:16],16)
    s3 = int(s[16:24],16)
    s4 = int(s[24:32],16)
    s5 = int(s[32:40],16)
    s6 = int(s[40:48],16)
    s7 = int(s[48:56],16)
    s8 = int(s[56:64],16)

    sum_s = s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8
    # 截断为32比特
    od = sum_s % (2 ** 32)
    return od

def calculate_p(od, n):
    """
    生成动态口令
    od (int): 一次性中间值,n (int): 动态口令的位数,应不小于6
    返回:str: 动态口令(十进制数字字符串)
    """
    if n < 6:
        raise ValueError("动态口令的位数应不小于6")

        # 计算动态口令
    password = od % (10 ** n)

    # 将数字转换为字符串并返回
    return f"{password:0{n}d}"

def main():
    # 示例使用
    period = 60  # 口令变化周期,单位为秒
    input_time=datetime.now()
    t = calculate_t(input_time,period)
    print(f't={t}')

    # Example usage:
    T = t#"202310011230"  # Example time factor
    C = 1357924681  # Example event factor 事件因子
    Q = "ABCD1234"  # Example challenge factor 挑战因子

    id = calculate_id(T=T, C=C, Q=Q)
    print(f'id={id.hex()}')  # Print in hexadecimal for easy verification

    # pattern = re.compile(r"[ 1I0O'、]", flags=re.IGNORECASE)
    # Q = pattern.sub('', Q)
    # print(Q)

    key='2934412A66B7A186DC35DC40E926F9EE'
    s=calculate_s(key,id)
    print(f's={s}')

    od=calculate_od(s)
    print(f'od={od}')
    n=6
    p=calculate_p(od, n)
    print(f'p={p}')

    T=datetime.now()
    N=6
    otp=gen_otp(key,T,C,Q,N,Period=60)
    print(f'otp={otp}')
if __name__ == '__main__':
    main()

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值