TOTP算法实现

news2024/11/20 16:34:16

TOTP算法实现

      • 1 什么是双因子认证(2FA)
      • 2 TOTP原理
        • 2.1 HOTP原理
        • 2.2 TOTP
      • 3 实现
      • 参考文章

最近发现github天天给我发通知要启用双因子认证(2FA),受不了了只能想办法启用了。看到它支持采用基于TOTP算法的应用的认证方式,便学习了一下TOTP算法,基于该原理简单做了一个实现,并成功启用了github的双因子认证。

1 什么是双因子认证(2FA)

双因子认证(2FA)是一种身份验证方法,要求用户提供密码和另一个认证因子或者至少提供两个认证因子(其中一个代替密码),从而提高用户账户的安全性。传统的密码仅为一组静态信息,很容易被窃取,而导致账户被盗用,相对来说,双因子认证比传统密码还多了一个认证步骤,并且一般来说认证因子会比传统密码更难破解、获取(具有时效性),引入双因子认证虽然带来了一定复杂度,但提高了安全性。

采用双因子认证的一个成功登录的流程:
在这里插入图片描述

2 TOTP原理

TOTP(英文全称:Time-Based One-Time Password Algorithm) 是基于时间的一次性密码算法。该算法是在HOTP算法的基础上改进而来,HOTP是一个基于事件的OTP算法,其中的移动因子是一个事件计数器,而TOTP的移动因子是时间值。基于时间的变体提供了短暂的OTP值,有助于增强安全性。

2.1 HOTP原理

HOTP是基于HMAC(采用加密哈希函数进行消息验证的机制)生成一次性密码值的算法。

算法核心参数

  • C:计数器(移动因子),是一个8-byte的值,需要在客户端与服务器之间同步
  • K:密钥,在客户端与服务器之间共享,多个客户端之间的密钥应该保证不同且唯一
  • T:节流参数,T次认证不成功之后服务器将会拒绝来自客户端的请求(本文实现未用到
  • s:再同步参数,服务器将尝试通过多个连续的计数器值验证接收到的身份验证器(本文实现未用到
  • DigitHOTP值位数

算法定义

HOTP(K,C) = Trancate(HMAC-SHA-1(K,C))

其中,Trancate代表将HMAC-SHA-1值转换为HOTP值的函数。KC以及数据值会首先被散列成高阶字节。而由HOTP生成器生成的HOTP值是按照大端模式进行表示的。

算法实现细节

生成HOTP值主要有三个不同的步骤:

  1. HS=HMAC-SHA-1(K,C),采用HMAC-SHA-1生成一个20-byte大小的字符串。
  2. 截取HS的值(采用DT函数截取20-byte中的字串,生成4-byte大小的字符串), Sbits = DT(HS)
  3. 基于第二步提供的字符串,转换为一个整数,并对10^Digit进行取模,便能生成指定Digit位数的HOTP值了。

DT函数:

DT(String) // String = String[0]...String[19] 即20个字节长度的字符串(第一步由HMAC-SHA-1所生成的)
    Let OffsetBits be the low-order 4 bits of String[19] // 最后一个字节的低4位取出来做为待截取字串的索引
    Offset = StToNum(OffsetBits) // 0 <= OffSet <= 15 <= 2^4 = 16,表达范围,从0~15处索引且取4个字节,刚好能覆盖整个20字节长的字符串
    Let P = String[OffSet]...String[OffSet+3] // 取其中四个字节
    Return the Last 31 bits of P  // 最高位被掩码去掉了是因为避免后续因求模运算所导致的有符号与无符号混淆

生成的HOTP值的最小位数为6,也可以是7或者8。 为了安全性着想,可以将其位数设置为7或者更多。

一个例子

假定生成的HOTP值位数为6,例子所给的hmac_result 是由HMAC-SHA-1生成的一个20-byte数组。

DT函数的核心实现:

int offset   =  hmac_result[19] & 0xf ;  // 获取索引 0xf -> 1111
int bin_code = (hmac_result[offset]  & 0x7f) << 24
           | (hmac_result[offset+1] & 0xff) << 16
           | (hmac_result[offset+2] & 0xff) <<  8
           | (hmac_result[offset+3] & 0xff) ; // 取四个字节 并转为 整数

假定hmac_result的内容如下:

   -------------------------------------------------------------
   | Byte Number                                               |
   -------------------------------------------------------------
   |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|
   -------------------------------------------------------------
   | Byte Value                                                |
   -------------------------------------------------------------
   |1f|86|98|69|0e|02|ca|16|61|85|50|ef|7f|19|da|8e|94|5b|55|5a|
   -------------------------------***********----------------++|
  • 最后一个字节,即hmac_result[19]0x5a,低4位就是a(偏移值),那么转为十进制就是 10
  • 从偏移值(offset= 10)开始取四个字节,即0x50ef7f19
  • 由于最高的字节0x50的二进制为0101 0000,经过0x7f掩码后,依然为0x50,故掩码处理最后依然是0x50ef7f19,其整数值为1357872921
  • 1000000(6位,10^6)取模,最后得到的6位HOTP码为872921

用来自HOTP和TOTP算法图解文章的图来描述该流程:
在这里插入图片描述

2.2 TOTP

TOTP算法是HOTP算法基于时间的变体,其中T(从时间戳和步长中导出)将会代替公式中的C。同时,TOTP可以使用HMAC-SHA-256或者HMAC-SHA-512函数,而不再局限于HMAC-SHA-1

算法要求

  • 客户端与服务器必须能够获取当前的Unix时间(自1970年1月1日午夜(UTC)以来经过的秒数)
  • 客户端与服务器必须共享同一个密钥或者生成共享秘密的秘密转换知识
  • 算法必须采用HOTP算法作为关键构建块
  • 客户端和服务器必须使用同样的时间步长X
  • 在每一个客户端中,密钥必须是唯一的
  • 密钥应该使用密钥派生算法随机生成或派生
  • 密钥可以存储在防篡改设备中,并且应该受到保护,防止未经授权的访问和使用。

上述提到的客户端和服务器,可以分别理解为一个生成TOTP码的应用或者是程序,而服务器可以理解成校验器、验证器。

核心参数

  • X:时间步长(秒),默认是30
  • T0:开始计算时间步长的Unix时间,默认是0

算法细节

TOTP = HOTP(K,T)

其中T=(current Unix Time - T0)/X, T是一个整数,该计算采用下限函数。

依然采用来自HOTP和TOTP算法图解文章的图来描述该流程:
在这里插入图片描述
该算法与HOTP算法剩余流程一致。

3 实现

笔者采用C++并结合Crypto++库进行实现,具体源代码详见ToTpAuth,代码量不多。其中部分实现细节,参考了python中的PyOTP库源码。

相对来说,采用C++工作量相比JavaPython更多一些。如果不想实现,可以直接用PyOTP库来写个简单的脚本生成TOTP码即可。

参考文章

  • RFC4226
  • RFC6238
  • 动态密码TOTP
  • HOTP和TOTP算法图解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1017434.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

彻底搞懂线程池原理以及创建方式

1. 为什么要使用线程池 在实际使用中&#xff0c;线程是很占用系统资源的&#xff0c;如果对线程管理不善很容易导致系统问题。因此&#xff0c;在大多数并发框架中都会使用线程池来管理线程&#xff0c;使用线程池管理线程主要有如下好处&#xff1a; 降低资源消耗。通过复用…

Python机器学习实战-建立Gradient Boosting模型预测肾脏疾病(附源码和实现效果)

实现功能 建立Gradient Boosting模型预测肾脏疾病 实现代码 import pandas as pd import warnings warnings.filterwarnings("ignore") pd.set_option(display.max_columns, 26)#读取数据 df pd.read_csv("E:\数据杂坛\datasets\kidney_disease.csv") …

vMAP——论文解析

vMAP: Vectorised Object Mapping for Neural Field SLAM vMAP 是一个物体级稠密图 neural SLAM&#xff0c;每一个物体都用一个 mlp 来表征&#xff0c;而不需要 3D 先验。当 RGB-D 相机在没有任何先验信息的情况下时&#xff0c;vMAP 会即时检测物体 instance&#xff0c;并将…

在Ubuntu 18.04上支持C++17的std::filesystem的方法

在Ubuntu 18.04上通过命令sudo apt install gcc g安装的gcc/g版本为7.5&#xff0c;此版本并不直接支持filesystem&#xff0c;如下图所示&#xff1a; Ubuntu 18.04上的g 7.5支持experimental的filesystem,即std::experimental::filesystem&#xff0c;若想使Ubuntu 18.04支持…

购物系统设计与实现

目 录 1 绪 论 1 1.1 本课题研究的背景和意义 1 1.1.1 本课题研究的背景 1 1.1.2 本课题研究的意义 2 1.1.3 本课题的发展现状及前景 2 1.2 系统的实现任务 7 2 系统概述及实现技术介绍 8 2.1 网上商城简介 8 2.2 相关实现技术介绍 10 2.2.1 JSP语言及其特点 10 2.2.2 Dreamwe…

快速学会搭建微信小程序的基础架构

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 基础架构 构建界面 引入 uni-ui 组件库 组件自动引入 配置TS类型 状态管理 持久化 数据交互 请…

Unity中Shader特性PerRendererData

文章目录 前言一、优化前是对使用了相同材质球的不同物体间shader分别设置&#xff0c;比较消耗性能二、使用[PerRendererData]标签&#xff0c;可以在脚本中使用SetPropertyBlock()对使用同一材质球的不同物体进行修改其Shader属性 前言 Unity中Shader特性PerRendererData 一…

Python学习 -- 常用数据交换格式(CSV、XML、JSON)

数据交换格式是在不同系统之间交换数据时使用的一种标准化格式。在Python中&#xff0c;我们常用的数据交换格式有CSV、XML和JSON。本篇技术博客将介绍这三种数据交换格式的详细使用方法&#xff0c;并提供具体的代码案例&#xff0c;帮助初学者快速掌握这些格式的使用。 CSV&…

第二章 进程与线程 六、线程的实现方式和多线程模型

目录 一、线程的实现方式 1、用户级线程 2、内核级线程 二、多线程模型 注意&#xff1a; 1、一对一模型 &#xff08;1&#xff09;定义: &#xff08;2&#xff09;优点&#xff1a; &#xff08;3&#xff09;缺点&#xff1a; 2、多对一模型 &#xff08;1&…

Linkerd的部署与入门--service mesh初步体验

Linkerd2初探 部署环境Linkerd简介安装Linkerd客户端在k8s上安装Linkerd控制平面&#xff08;服务端&#xff09;实验&#xff1a;数据平面代理注入demo应用安装viz插件&#xff08;可视化面板&#xff09;部署grafana 其他 部署环境 k8s环境: KIND 模拟kubernetes 1.21.1 kub…

【python】使用Reddit API爬取数据

这篇文章介绍如何使用reddit api获数据,文档地址如下:https://www.reddit.com/dev/api/ 首先需要创建应用,页面如下:https://www.reddit.com/prefs/apps 这里name随意填写,reditect uri随意写一个网址 如图所示,创建好应用以后,可以得到CLIENT_ID和SECRET_KEY: 编写代…

线性回归网络

李沐大神的《动手学深度学习》&#xff0c;是我入门机器学习的首课&#xff0c;因此在这里记录一下学习的过程。 线性回归的从零开始实现 线性回归是理解机器学习的基础&#xff0c;它经常用来表示输入和输出之间的关系。   线性回归基于几个简单的假设&#xff1a; 首先&am…

【计算机视觉】Vision and Language Pre-Trained Models算法介绍合集(一)

文章目录 一、ALIGN二、Contrastive Language-Image Pre-training&#xff08;CLIP&#xff09;三、Learning Cross-Modality Encoder Representations from Transformers&#xff08;LXMERT&#xff09;四、BLIP: Bootstrapping Language-Image Pre-training五、Vision-and-La…

Json-Jackson和FastJson

狂神&#xff1a; 测试Jackson 纯Java解决日期格式化 设置ObjectMapper FastJson&#xff1a; 知乎&#xff1a;Jackson使用指南 1、常见配置 方式一&#xff1a;yml配置 spring.jackson.date-format指定日期格式&#xff0c;比如yyyy-MM-dd HH:mm:ss&#xff0c;或者具体的…

机器学习 day35(决策树)

决策树 上图的数据集是一个特征值X采用分类值&#xff0c;即只取几个离散值&#xff0c;同时也是一个二元分类任务&#xff0c;即标签Y只有两个值 上图为之前数据集对应的决策树&#xff0c;最顶层的节点称为根节点&#xff0c;椭圆形节点称为决策节点&#xff0c;矩形节点称…

ffplay源码解析-FrameQueue队列

帧队列架构位置 结构体源码 FrameQueue结构体 /* 这是一个循环队列&#xff0c;windex是指其中的首元素&#xff0c;rindex是指其中的尾部元素. */ typedef struct FrameQueue {Frame queue[FRAME_QUEUE_SIZE]; // FRAME_QUEUE_SIZE 最大size, 数字太大时会占用大量的…

DPDK环境搭建

&#xff08;1&#xff09;虚拟环境&#xff1a;VMware Workstation 16 Pro 网上随便下载一个也行 &#xff08;2&#xff09;操作系统&#xff1a;ubuntu-22.04-beta-desktop-amd64.iso 下载地址&#xff1a;oldubuntu-releases-releases-22.04安装包下载_开源镜像站-阿里云…

Thymeleaf语法详解

目录 一、Thymeleaf介绍 &#xff08;1&#xff09;依赖 &#xff08;2&#xff09;视图 &#xff08;3&#xff09;控制层 二、变量输出 三、操作字符串 四、操作时间 五、条件判断 六、遍历集合 &#xff08;1&#xff09;迭代遍历 &#xff08;2&#xff09;将遍…

Java————数组

1 、数组 数组可以看成是相同类型元素的一个集合&#xff0c; 在内存中是一段连续的空间。 每个空间有自己的编号&#xff0c;其实位置的编号为0&#xff0c;即数组的下标。 数组是引用类型。 1. 数组的创建 T[] 数组名 new T[N];T&#xff1a;表示数组中存放元素的类型 …

Kakfa - Producer机制原理与调优

Producer是Kakfa模型中生产者组件&#xff0c;也就是Kafka架构中数据的生产来源&#xff0c;虽然其整体是比较简单的组件&#xff0c;但依然有很多细节需要细品一番。比如Kafka的Producer实现原理是什么&#xff0c;怎么发送的消息&#xff1f;IO通讯模型是什么&#xff1f;在实…