跳到主要内容

铜锁社区开源之夏学生获得“最具潜力奖”

· 阅读需 4 分钟
铜锁PMC

开源之夏是中国科学院软件研究所发起的“开源软件供应链点亮计划”系列暑期活动,旨在鼓励高校学生积极参与开源软件的开发维护,促进优秀开源软件社区的蓬勃发展。活动联合各大开源社区,针对重要开源软件的开发与维护提供项目开发任务,并向全球高校学生开放报名。开源之夏搭建了开源社区和高校开发者的桥梁,培养和发掘更多优秀的开发者,共筑开源软件可靠供应链。

2024年,铜锁开源社区很荣幸再次参加开源之夏活动,一共发布了2个项目,项目简介如下。

项目名称社区导师编程语言仓库地址
铜锁社区项目RustyVault支持prometheus 日志开发王祖熙Rusthttps://github.com/Tongsuo-Project/RustyVault
铜锁密码库Go语言SDK国密算法和协议开发张成龙Golanghttps://github.com/Tongsuo-Project/tongsuo-go-sdk

在项目申请阶段,有多组学生提交项目申请,最终尚同学和张同学通过终选。在项目导师的指导下,明确项目需求,独立完成开发任务。经过几个月的努力,顺利结项,感谢2位同学的辛勤付出。

开源之夏今年有455位同学通过了结项审核,为进一步鼓励这些新生的开源力量,组委会特邀请指导委员会老师从最佳质量奖、突出贡献奖、最快进步奖、最具潜力奖4个方向评选出具有代表性的 20 位优秀学生。其中,尚同学在“RustyVault支持Prometheus日志开发”项目中表现卓越,由于其学习能力强,迅速掌握了Rust语言以及Prometheus的仪表化技术,技术基础扎实,在面对复杂项目时,能运用科学方法解决问题,从众多同学中脱颖而出,荣获“最具潜力奖”。

开源之夏活动已经举办多年,每一届都为开源社区输入新鲜血液。开源社区需要年轻开发者的参与和贡献,而越来越多的学生也逐渐成为了开源社区的主力军。同样,铜锁开源社区需要开源力量,希望更多的学生参与进来,一起构建安全、合规、易用的开源密码库。

铜锁获得商用密码产品认证,符合密码模块二级要求

· 阅读需 8 分钟
Chenglong Zhang
Maintainer of Tongsuo
Xudong Guo
Maintainer of Tongsuo

铜锁在手,合规无忧

近日,蚂蚁集团密码团队基于开源铜锁项目和蚂蚁集团自研的密码卡,获得了国家密码管理局商用密码检测中心颁发的商用密码产品认证证书,符合 GM/T 0028《密码模块安全技术要求》安全二级。采用软硬件结合的方案,即混合软件密码模块,助力用户在国密改造、密评、等保等过程中,更加严谨地满足我国商用密码技术合规的要求。

高级应用安全密码模块(Linux版).jpg

关于商用密码产品认证

我国于 2020 年实施了《中华人民共和国密码法》(以下简称密码法),标志着密码技术的应用进入了依法管理的时代。《密码法》规定,我国的密码实行分类管理,即核心密码、普通密码和商用密码。核心密码和普通密码用于保护国家秘密信息,商用密码则用于保护不属于国家秘密的信息。当前,商用密码已经广泛应用于政务、金融、通信、交通、医疗、能源等各领域,为社会经济发展提供了有力的安全保障。

《密码法》提出推进商用密码检测认证体系建设,制定商用密码检测认证技术规范、规则,鼓励商用密码从业单位自愿接受商用密码检测认证,提升市场竞争力。商用密码检测、认证机构应当依法取得相关资质,并依照法律、行政法规的规定和商用密码检测认证技术规范、规则开展商用密码检测认证。要求对关键信息基础设施的密码应用安全性开展分类分级评估。《网络安全法》也明确规定关键信息基础设施运营者每年要自行或者委托第三方机构对信息系统安全性进行测评。密码应用安全性是测评的一项重要内容,应遵循密码法律法规要求。

商用密码检测机构针对生产厂家提交的商用密码产品,按照不同产品类别的相关要求,进行技术合规方面的多重检测。检测通过后,由检测机构为商用密码产品颁发《商用密码产品认证证书》,其中注明是基于何种产品标准和技术要求进行的检测,例如对于密码模块类型的商用密码产品,则需要符合 GM/T 0028《密码模块安全技术要求》 的各项规范。

《商用密码产品认证证书》是一种资质,表明了获得证书的商用密码产品在经过相关检测机构严格的测试后,在技术正确性、兼容性等方面是符合规范要求的,因此可以为使用该商用密码产品的用户提供更加严谨和正式的监管合规支撑,例如在密评、等保等认证体系中,均对密码相关产品或服务提出了相关资质的要求。

由于业内对于商用密码通常简称为 “国密”,因此《商用密码产品认证证书》也俗称为 “国密资质”

铜锁 + 蚂蚁卡,软硬件结合

关于铜锁

铜锁(Tongsuo)是一个提供现代密码学算法和安全通信协议的开源基础密码库,为存储、网络、密钥管理、隐私计算等诸多业务场景提供底层的密码学基础能力,实现数据在传输、使用、存储等过程中的私密性、完整性和可认证性,为数据生命周期中的隐私和安全提供保护能力。
铜锁开源项目已经由蚂蚁集团完成了向开放原子开源基金会的捐赠,成为基金会的“孵化期”项目,也是基金会唯一的密码学方向开源项目。在基金会孵化过程中,铜锁开源社区先后启动了 “铜锁嵌入式版”“RustyVault密钥管理系统” 两个新项目的开发。蚂蚁集团在铜锁完成捐赠后持续对项目进行投入,成立了铜锁项目管理委员会,引入多家领军企业参与铜锁开源项目的管理,推动铜锁进入到了独立发展的新阶段。

采用软硬件结合的方式申请二级密码模块,利用蚂蚁卡的密码管理能力和高性能优势,结合铜锁密码库提供的算法API、国密安全传输协议(TLCP)和硬件引擎框架,为应用程序提供数据安全能力。

支持常用的国密算法,包括 SM2、SM3 和 SM4。具体的安全功能包括 SM2 加密和解密、SM2 签名和验签、SM3 杂凑、SM4 对称加解密、软件随机数发生器、国密传输 TLCP 通信功能(包括客户端和服务端)。

开源计划

二级密码模块中涉及的软件部分(非密码卡相关代码),会全部回馈给开源社区,将于近期内合并到铜锁开源项目中。 未来,铜锁开源项目也会适配更多的密码硬件,同时也欢迎跟更多的密码设备厂商合作,基于铜锁开源项目和密码硬件,共同打造安全、合规的解决方案,满足企业国密改造、密评、等保等场景下的技术合规要求。

铜锁再次参与开源之夏

· 阅读需 7 分钟
Paul Yang
PMC Member of Tongsuo
Xudong Guo
Maintainer of Tongsuo

🎉 开源之夏2024 🌐 铜锁密码学社区项目现已开放报名!加入我们,一起探索开源软件的无限可能! 在本年度开源之夏活动中,铜锁开源社区共发布了2个项目,涵盖 Golang、C 和 Rust 语言的开发工作,目前正在火热报名中。可以登录开源之夏官网获取项目详细信息:

🔗 开源之夏官网https://summer-ospp.ac.cn/
🔗 开源之夏2024铜锁项目列表https://summer-ospp.ac.cn/org/orgdetail/e4de262f-50b1-4f11-930b-8b8e841de420?lang=zh

什么是开源之夏

📚 开源之夏 是由中国科学院软件研究所“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,培养和发掘更多优秀的开发者,促进优秀开源软件社区的蓬勃发展,助力开源软件供应链建设。 开源之夏联合国内外开源社区,针对重要开源软件的开发与维护提供项目任务,面向全球高校学生开放报名,中选学生将在项目资深开发者(项目导师)的指导下,参与开源贡献,完成开发工作并贡献给开源社区

什么是铜锁

铜锁,全称开放原子铜锁(OpenAtom Tongsuo),是一个关于密码学和数据安全的开源社区,拥有多个密码学开源项目,包括铜锁密码学算法库、铜锁密码库嵌入式版和 RustyVault 机密信息管理软件。铜锁诞生于蚂蚁集团,于2023年完成了向开放原子开源基金会的捐赠,目前是开放原子开源基金会的孵化期项目。铜锁当前由其PMC进行管理,已广泛的应用在互联网、金融、司法、电信等诸多领域中,为存储、网络、密钥管理、隐私计算、区块链、IoT 等诸多业务场景提供底层的密码学基础能力。

社区项目主仓库https://github.com/Tongsuo-Project
开源协议:Apache-2.0
技术领域:密码学、SSL/TLS、PKI、数据安全、密钥管理
编程语言C Java Go Python Rust

开源之夏 2024 之铜锁项目

铜锁密码学开源社区自2023年起参与开源之夏活动,并取得了显著成效,不仅促进了社区的发展,还培养了在校学生的实践能力。2024年,铜锁密码学开源社区再次参与,并发布了两个项目:

一、铜锁密码库 Go 语言 SDK 国密算法和协议开发

Tongsuo-Go-SDK 是铜锁开源社区基于铜锁密码库项目提供的 Golang SDK,目标是为 Golang 开发者提供国密算法和安全传输协议等功能。Tongsuo-Go-SDK 项目已经提供了部分国密算法和安全传输协议功能,需要继续完善。

项目导师:K1
项目编号:24e4d0074
导师邮箱dongbeiouba@gmail.com
编程语言:Golang,C
技术领域:密码学、PKI、SSL/TLS、网络安全、数据安全
项目成果仓库https://github.com/Tongsuo-Project/tongsuo-go-sdk
项目主页https://summer-ospp.ac.cn/org/prodetail/24e4d0074
项目技术要求

  1. 熟悉Golang编程语言开发
  2. 有密码学基础,了解常见密码学算法和协议
  3. 了解开源项目开发流程

希望实现的功能包括

  • SM2加解密
  • 国密证书签发(双证书)
  • TLCP功能完善,包括 SNI、ALPN 和 Session 复用等
  • TLS 1.3 + 商密套件
  • 跨平台支持,以上所有功能需要支持 Linux、MacOS 和 Windows 系统

二、铜锁社区项目 RustyVault 支持 prometheus 日志开发

RustyVault 是铜锁开源社区的生态项目,目标是成为一个完全可控和安全可靠的高性能密钥管理开源软件,已经提供了密钥管理的基础功能,还需要继续完善。

项目导师:金九
导师邮箱wanyco@gmail.com
编程语言:Rust
技术领域:密码学、云原生、Prometheus、Hashicorp Vault、审计
项目成果仓库https://github.com/Tongsuo-Project/RustyVault
项目主页https://summer-ospp.ac.cn/org/prodetail/24e4d0375
项目技术要求

  1. 熟悉 Rust 编程语言开发和 prometheus 日志原理
  2. 有密码学基础,熟悉密钥管理,了解常见密码学算法和协议
  3. 了解开源项目开发流程

希望实现的功能包括

  • 支持 prometheus 日志

重要日期提醒

以下是您需要关注的关键时间节点,确保不错过任何重要机会!

  • 📅 学生报名、导师沟通和项目申请: 04/30 - 06/04
  • 🔍 项目申请审核: 06/05 - 06/25
  • 📢 入选学生项目公布: 06/26

本次铜锁密码学开源社区发布的两个项目,涉及商用密码算法和协议、Rust 语言、云原生体系支持等前沿领域。我们诚挚邀请所有对这些领域感兴趣的开发者前往开源之夏官网获取更多信息。

🌟 铜锁密码学开源社区 期待您的加入,让我们携手推动开源软件的发展,为构建一个更加安全、开放的软件生态贡献我们的力量!

铜锁开源社区管理制度

· 阅读需 13 分钟
Paul Yang
PMC Member of Tongsuo

第一章 总则

第一条 社区名称

铜锁开源社区是归属于开放原子开源基金会的开源项目群。铜锁开源社区中包含有多个开源项目。铜锁开源社区的中文全称为开放原子铜锁,简称铜锁;英文全称为OpenAtom Tongsuo,简称为Tongsuo;Logo为: image.png

第二条 使命和愿景

愿景:成为中国乃至全球最具影响力的密码学基础设施开源社区

使命:基于持续的技术创新,为用户提供先进的密码学技术能力、数据安全能力和监管合规能力

第三条 业务范围

  1. 以密码学算法库为基础,建设密码学基础设施软件,并对市场提供开源供给;
  2. 探索和构建密码行业软件开原生态,促进密码学技术以及相关产业发展;
  3. 支持第三方厂商基于本社区中开源项目的商业化行为。

第二章 社区结构

第四条 社区角色定义

铜锁开源社区的核心是社区中的人。这些不同角色的人们形成了社区的基础、推动了社区的发展、也定义了社区的未来。因此需要对角色进行清晰的定义,并明确其对应的权利和责任。

总体来看,铜锁开源社区中的角色可以划分为“用户”、“维护人”以及“项目管理委员会”。

用户

用户是指下载、编译、安装或使用铜锁开源社区中各开源项目的组织或个人。用户使用铜锁开源社区中各开源项目的方式需要符合各项目“开源许可证”的规定。

用户可以向铜锁开源社区报告bug、提交问题、请求新特性开发、请求帮助和技术支持等,但需要通过铜锁项目管理委员会指定的渠道和方式。同时由于开源项目的特殊性,铜锁项目管理委员会不对上述行为做得到必然回应的承诺。

维护人

维护人是指一些拥有特定铜锁开源项目代码仓库或文档仓库相关权限的技术人员,这里的权限一般指向代码仓库提交代码、评审代码合入请求、文档的创建和编写等。即维护人需要对铜锁各项目以及其相关代码仓库或文档的维护负责,并确保来自铜锁社区的贡献(代码或文档等)与项目利益是一致的。

维护人资格执行邀请制。即按铜锁项目管理委员会流程,经过委员会批准后,赋予维护人相关代码或文档维护权限。同时,铜锁项目管理委员会经合法流程,也可以撤销维护人的代码或文档维护权限。

维护人须认同铜锁开源社区的使命和愿景,并对铜锁开源社区做出技术上的贡献。铜锁开源社区对其维护人进行贡献度的考核,贡献度考核标准为:每个自然季度中,至少提交或评审一次代码合并请求。

项目管理委员会

铜锁项目管理委员会,即Tongsuo Project Management Committee,以下简称为“铜锁PMC”,是铜锁开源社区的最高决策机构,也是代表铜锁对外发声的唯一官方机构。铜锁PMC由个人或组织组成,具体分为:

  1. 代表企业参加铜锁PMC
    1. 需要所在企业的授权书(盖章)
    2. 更换企业代表时需要该企业重新出具授权书
    3. 当企业委派代表出现变化时,如果该企业 3 个月内没有指派新代表,则认为该企业自动放弃 PMC 资格
  2. 代表个人参加铜锁PMC
    1. 个人代表参与 PMC 工作的,需现有成员推荐并获得超过80%的PMC成员认可,方可加入

无论个人PMC还是企业/组织PMC代表均需要按照规定的工作流程,行使其权利并承担相应的责任。

铜锁PMC的工作流程、权利和责任,由本制度的第五条《项目管理委员会的权利和责任》具体制定。

第三章 社区角色的责任和权利

第五条 项目管理委员会的责任和权利

铜锁PMC代表铜锁开源社区的官方声音,并以投票制做为基本的工作方式。铜锁PMC的主要责任和权利有:

  • 需要为铜锁社区带来持续且实际的价值贡献,包括(满足之一即可):
    • 直接资金的捐赠,不少于人民币叁万元/年
    • 投入不少于1个铜锁维护者/年的研发资源用于社区项目的研发、维护工作
    • 实现铜锁在某行业的重大应用和落地
  • 制定项目的战略方向,具体包括:
    • 资金募集
    • 对外关系和生态规划
    • 功能特性规划
    • 里程碑和优先级规划
    • 版本生命周期管理
  • 制定和维护社区的管理制度
  • 管理铜锁PMC成员资格的准入和退出
  • 管理铜锁维护人资格的准入和退出
  • 管理并审计社区的财务
  • 负责社区中项目的法律合规
  • 每个自然年度PMC需要出轮值秘书,负责各类例行工作的组织。其中个人PMC和技术成员不受此约束
  • PMC成员需履行参与投票进行决策的责任,1个自然年内3次不参加表决,则取消PMC资格
  • PMC成员需按要求出席线上例会以及线下例会,连续3次不出席者,取消PMC资格
    • 特殊情况可以请假或委托他人代为参与

第六条 项目管理委员会的工作流程

铜锁PMC采用投票表决行使其权利。投票表决的事项包括但不限于:

  • 关于铜锁PMC成员资格的变化
  • 关于铜锁维护人资格的变化
  • 关于铜锁开源社区管理制度的变更
  • 其他铜锁PMC成员认为有必要决策的事项

铜锁PMC的投票表决事项由铜锁PMC成员发起。合法的投票选项有:

  • 同意
  • 不同意
  • 弃权

每个投票均须不少于70%的PMC成员参与,且有效参与中“同意”票数超过90%则视为表决通过。

每个投票均需要设置投票期限,超期则视为投票无效。

投票可以利用线下会议机会、也可以利用线上工具如钉钉、微信等进行。

铜锁PMC召开月度线上例会,并每个自然季度举办一次线下会议,具体有:

  • 线上例会主要内容
    • 讨论本月需要决策的事项
    • 同步和讨论本月内铜锁开源社区的重要进展,包括研发进展、业务进展等
  • 线下例会主要内容
    • 针对本季度铜锁开源社区各领域工作进行总结
    • 讨论和确定下个季度的工作方向和重点
  • 下上和线下例会的组织,应由年度轮值PMC所出秘书负责

除例会外,铜锁PMC成员可以随时召集发起其认为有必要的会议主题。

铜锁PMC使用独立的知识库系统跟踪和记录会议纪要、投票决议等信息。铜锁PMC所使用的知识库默认不对公众公开。

第七条 铜锁维护人的责任和权利

铜锁维护人团队负责所有和技术相关的决策和管理事项,包括:

  • 需求分析
  • 架构设计
  • 代码实现
  • 测试用例编写
  • 文档编写
  • 代码评审
  • 铜锁基础设施(网站、各类工具等)维护
  • 向铜锁PMC建议新的维护人资格

铜锁维护人资格一般由铜锁PMC成员提名并经过投票表决通过生效,铜锁维护人根据职责范围以及其工作的方向,对铜锁社区中的1个或多个代码仓库拥有写权限。

铜锁维护人需要持续的对铜锁社区的相关开源项目做出贡献,否则其维护人资格经由铜锁PMC决议后,可能会被移除。被认可的工作贡献包括:

  • 每个季度至少1次的代码提交,或
  • 每个季度至少1次的代码评审,或
  • 每个季度通过铜锁规定的渠道进行至少1次的社区答疑或互动

第八条 铜锁开源社区开源项目代码准入要求

铜锁开源社区中存在多个密码学或安全相关的开源项目,因此如何确保项目高水平的代码质量是我们关心的重点。为此,铜锁PMC制定了代码准入要求如下:

  • 每个代码合入请求(Pull Request)需要经过至少2个铜锁维护人的代码评审并均处同意意见;
  • 如代码合入请求由铜锁维护人发起,则只需要再经过1个铜锁维护人的评审并处同意意见即可;
  • 合入的代码需要有对应的测试用例
  • 合入的代码需要有完善的文档(如API或使用教程)

铜锁维护人应当严格遵守并执行上述代码准入要求,并对自己拥有写权限的代码仓库的最终软件质量负责。

第四章 社区财务制度、法务合规和运营管理

铜锁密码学开源社区的财务、法务和运营管理制度遵从开放原子开源基金会的相关管理要求。

铜锁支持SM2两方门限解密算法

· 阅读需 7 分钟
Chenglong Zhang
Maintainer of Tongsuo

前言

在SM2两方门限签名算法的基础上,铜锁进一步支持了SM2两方门限解密算法。

标准SM2解密算法中输入密文和私钥,即可解密出明文。而在SM2两方门限的情况下,因为没有完整的私钥,或者说一把钥匙变成两把钥匙,即两个参与方,Alice和Bob,各自持有私钥分量,当需要进行SM2解密的时候,需要在Alice和Bob共同参与,通过一轮交互之后,即分别输入Alice和Bob各自的私钥分量,才能将密文还原成明文。

通过SM2两方门限解密算法可以提升密钥的安全性,即降低单一密钥泄漏的风险。同时也可以应用于需要两方或多个参与方共同参与计算,且对数据隐私有严格要求的场景中,例如跨机构数据分析等。

在基于铜锁源代码进行构建时,可以按需开启SM2两方门限算法的开关,即:

./config enable-sm2_threshold

算法原理

SM2门限密钥生成算法,参与方A生成自己的私钥分量d1,参与方B生成自己的私钥分量d2,然后分别推导公钥分量P1 = [d1^(-1)] * G,P2 = [d2^(-1)] * G,并将公钥分量发送给对方,参与方A推导完整公钥P = [d1^(-1)] * P2 - G,参与方B推导完整公钥P = [d2^(-1)] * P1 - G,双方推导出的完整公钥相等。详情可以参考之前的文章《铜锁支持SM2两方门限签名算法》。

加密算法与标准的SM2加密算法一致,即使用完整的SM2公钥加密即可,算法详情可以参考《GB/T 32918.4——2016 信息安全技术 SM2椭圆曲线公钥密码算法》第四部分:公钥加密算法。

SM2两方门限解密算法

sm2-threshod-decrypt-algorithm

算法过程:

  1. 参与方A收到SM2加密的密文后,首先解码出C1,(SM2密文结构为C1 || C3 || C2),然后生成随机数w,w ∈ [1, n-1]。计算T1 = [w] * C1。
  2. 将T1发送给参与方B。
  3. 参与方B计算T2 = [d2^(-1)] * T1,d2为参与方B的私钥分量。
  4. 将T2发送给参与方A。
  5. 参与方A计算(x2, y2) = [w^(-1)*d1^(-1)] * T2 - C1,d1为参与方A的私钥分量;计算 t = KDF(x2 || y2, klen),计算明文M = C2 ⊕ t,其中C2来自于SM2密文。

铜锁SM2门限解密API

SM2门限解密涉及3步,每一步对应铜锁的一个API,API接口原型如下,关于参数、返回值和使用说明,可以参考铜锁项目源代码和铜锁帮助文档。

int SM2_THRESHOLD_decrypt1(const unsigned char *ct, size_t ct_len, BIGNUM **w,
EC_POINT **T1);
int SM2_THRESHOLD_decrypt2(const EVP_PKEY *key, const EC_POINT *T1,
EC_POINT **T2);
int SM2_THRESHOLD_decrypt3(const EVP_PKEY *key, const unsigned char *ct,
size_t ct_len, const BIGNUM *w, const EC_POINT *T2,
unsigned char **pt, size_t *pt_len);

命令行实战

首先生成SM2门限密钥,包括私钥分量、公钥分量和完整公钥。

# Alice创建私钥分量,推导公钥分量
tongsuo genpkey -algorithm ec -pkeyopt ec_paramgen_curve:sm2 -out A-sm2.key
tongsuo sm2_threshold -derive -inkey A-sm2.key -pubout A-pub.key

# Bob创建私钥分量,推导公钥分量
tongsuo genpkey -algorithm ec -pkeyopt ec_paramgen_curve:sm2 -out B-sm2.key
tongsuo sm2_threshold -derive -inkey B-sm2.key -pubout B-pub.key

# Alice推导完整的公钥
tongsuo sm2_threshold -derive -inkey A-sm2.key -peerkey B-pub.key -pubout ABpub.key

# Bob推导完整的公钥
tongsuo sm2_threshold -derive -inkey B-sm2.key -peerkey A-pub.key -pubout BApub.key

然后使用SM2公钥加密。

echo "hello tongsuo" > plain.txt

# 使用完整的SM2公钥加密
tongsuo pkeyutl -encrypt -pubin -inkey ABpub.key -in plain.txt -out cipher.txt

最后演示SM2门限密钥解密过程。

# 门限解密第1步:Alice输入密文,生成随机数w和点T1
tongsuo sm2_threshold -decrypt1 -in cipher.txt -newrand w.bin -newpoint T1.bin

# 门限解密第2步:Bob输入自己的私钥分量和点T1,生成点T2
tongsuo sm2_threshold -decrypt2 -inkey B-sm2.key -pointin T1.bin -pointout T2.bin

# 门限解密第3步:Alice输入自己的私钥分量、第一步生成的随机数w、点T2,以及密文,解密出明文。
tongsuo sm2_threshold -decrypt3 -inkey A-sm2.key -randin w.bin -pointin T2.bin -in cipher.txt -out plain2.txt

性能测试

SM2两方门限解密相比标准的SM2解密算法,交互次数多了2次,核心运算多了2次非固定点的点乘运算。在不考虑网络交互消耗的情况下,使用铜锁speed测试标准的SM2加解密和SM2门限解密,测试环境和测试数据如下。

测试环境:

  • 操作系统:macOS 13.6.3
  • 处理器:Apple M2 Pro
  • 内存:16G
  • 编译器:Apple clang version 14.0.3 (clang-1403.0.22.14.1) / Target: arm64-apple-darwin22.6.0
块大小(Bytes)SM2加密SM2解密SM2门限解密
1621.09k41.77k12.91k
6484.82k167.39k51.61k
128168.98k331.68k102.70k
256334.60k650.22k204.20k
512654.60k1244.61k402.90k
10241258.11k2309.84k785.52k

根据测试数据可以看出,SM2门限解密算法相对于标准SM2解密算法性能下降2/3左右,即标准的SM2解密性能是SM2门限解密算法的三倍左右。

同时使用性能分析工具输出火焰图,如下图所示。可以看到主要的性能消耗在EC_POINT_mul(),即3次椭圆曲线的点乘运算。通过实际的火焰图展示,可以验证speed性能测试数据和性能消耗的理论分析基本一致。

flame

关于性能的一些数据

· 阅读需 3 分钟
Paul Yang
PMC Member of Tongsuo

选择版本:铜锁master,铜锁8.3,OpenSSL 3.0

性能指标:SM2加解密、签名验签、密钥生成、SM3哈希、SM4的ECB和CBC模式加解密

测试数据大小:1MB 的随机数据

测试环境:macOS 11.7 / 2.4 GHz Quad-Core Intel Core i5 / 16 GB 2133 MHz LPDDR3

测试程序:master/examples目录下的方式进行测试(见:Tongsuo/examples/perf at master · Tongsuo-Project/Tongsuo),单进程测试

铜锁 master铜锁8.3OpenSSL 3.0
sm2-enc: 285 Mbpssm2-enc: 281 Mbpssm2-enc: 282 Mbps
sm2-dec: 291 Mbpssm2-dec: 289 Mbpssm2-dec: 296 Mbps
sm2-sign: 2506/ssm2-sign: 2364/ssm2-sign: 2484/s
sm2-verify: 2772/ssm2-verify: 2673/ssm2-verify: 2810/s
sm2-keygen: 1973/ssm2-keygen: 2038/ssm2-keygen: 1968/s
sm3-hash: 1863 Mbpssm3-hash: 1826 Mbpssm3-hash: 1908 Mbps
sm4-ecb-enc: 908 Mbpssm4-ecb-enc: 906 Mbpssm4-ecb-enc: 902 Mbps
sm4-cbc-enc: 864 Mbpssm4-cbc-enc: 855 Mbpssm4-cbc-enc: 854 Mbps
sm4-ecb-dec: 919 Mbpssm4-ecb-dec: 913 Mbpssm4-ecb-dec: 905 Mbps
sm4-cbc-dec: 921 Mbpssm4-cbc-dec: 915 Mbpssm4-cbc-dec: 917 Mbps

【附录:基于apps/speed的部分测试】

SM4-CBC
铜锁master:
The 'numbers' are in 1000s of bytes per second processed.
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes
sm4 113645.27k 114115.80k 115176.88k 114105.11k 113833.18k 112978.60k
tongsuo-8.3:
The 'numbers' are in 1000s of bytes per second processed.
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes
sm4 19246.65k 49904.77k 80437.00k 100238.64k 106778.20k 106173.78k
openssl-3.0:
The 'numbers' are in 1000s of bytes per second processed.
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes
SM4-CBC 105532.58k 110182.79k 109867.86k 111192.36k 110362.62k 110890.64k

SM2门限的性能数据

以下单位均为:次/秒

关闭SM2优化

普通SM2:

  • keygen:5069
  • 签名:5859
  • 验签:6154

SM2门限:

  • keygen:1850
  • 签名:4967
  • 验签:5019

开SM2优化

普通SM2:

  • keygen:22560
  • 签名:20327
  • 验签:11325

SM2门限:

  • keygen:5980
  • 签名:8031
  • 验签:8308

铜锁支持SM2两方门限签名算法

· 阅读需 19 分钟
Chenglong Zhang
Maintainer of Tongsuo
Paul Yang
PMC Member of Tongsuo

1 背景

数字签名是一种用于验证数据完整性和身份认证的算法。该算法基于非对称加密技术,使用签名者的私钥对数据进行签名,得到一个仅能由私钥持有者生成的签名值。验证者可以使用签名者公开的公钥对签名进行解密和验证,以确认数据的完整性和发送者的身份。自第一个签名算法RSA被提出以来,数字签名技术已经广泛应用于网络通信、证书签发、区块链等领域,成为保障信息安全的重要方法。

SM2数字签名算法是国家密码管理局于2010年发布的椭圆曲线公钥密码算法SM2的一部分,其安全性基于求解有限域上椭圆曲线离散对数问题的困难性。SM2算法已成为我国公钥算法标准GM/T 0003.2-2012,并进入国际标准ISO/IEC 14888-3:2016中。与其他数字签名算法类似,私钥泄漏是SM2数字签名算法在实际应用中的一大挑战。具体来说,一旦用户的私钥泄漏,攻击者便可以任意地伪造数字签名,窃取用户信息或伪造用户身份,对信息系统的数据安全造成极大的损害。

基于上述挑战,门限签名(Threshold Signature)的思想应运而生。门限签名算法的特点在于,不再构建一个完整的私钥,而是将私钥拆分为多个部分私钥,由不同参与方独立持有。执行签名运算时,必须由一定数量的私钥持有者参与才能够生成完整的签名值。更进一步地,由用户和可信中心共同参与的两方门限签名算法是较为常见的一种技术方案。该方案由用户和半可信的中心服务器分别掌握一份部分私钥,签名计算过程由用户和中心服务器共同参与。该方案不仅能够提升攻击者窃取密钥的难度,中心服务器也不能单独地使用部分私钥完成数字签名计算,降低了中心服务器被攻击时的私钥泄漏风险。

我们基于林璟锵老师的研究成果《SM2数字签名算法的两方门限计算方案框架》,在铜锁中实现了SM2两方门限签名算法(点击“阅读原文”跳转到对应代码)。该算法基于乘法拆分,参与方分别构造部分私钥,基于对手方的部分公钥构造完整公钥并公开;参与方独立构造签名随机数,经组合后与部分私钥和消息摘要共同完成数字签名的协作计算。该算法无需同态加密等其他多方安全计算技术参与,通信轮次少(1轮通信),在实现难度、性能表现方面具有优势。

2 算法原理

SM2两方门限签名算法要求,任意单一参与方无法独立完成密钥生成和签名计算,且任意单一参与方无法从交互过程中获取完整的签名私钥或另一部分私钥的任何信息。

为了达成以上要求,2个参与方应分别持有 1 个用于协作计算签名的部分私钥。在签名计算的过程中,为了保护各自的部分私钥,2个参与方应独立生成1个部分签名随机数,然后通过特定的方式协作生成完整签名随机数以计算签名值。签名过程不应泄漏关于部分签名随机数的任何信息,使任意参与方无法获取完整签名随机数,杜绝利用完整签名随机数推导出完整私钥的可能性。满足上述安全要求的SM2两方门限签名算法主要步骤如下:

2.1 密钥生成

与标准的SM2算法略有不同,本算法将SM2数字签名过程中关于私钥的表达式(1+d)1(1+d)^{-1}视为完整私钥并进行拆分。设两个参与方为A,BA,B,记SM2椭圆曲线阶数为nn,生成元为GG,持有的部分私钥分别为d1,d2d_1, d_2,那么在乘法拆分下,完整私钥与d1,d2d_1, d_2之间的关系满足:

(1+d)1=d1d2(1+d)^{-1} = d_1 * d_2

d=d11d211d = d_1^{-1}*d_2^{-1} - 1,完整公钥PK=[d]G=d11d21GGP_K = [d] * G = d_1^{-1}*d_2^{-1} * G - G。此时,采用如下方式计算参与方A,BA,B的部分私钥:

  1. 参与方AA使用安全的随机数生成源,随机生成部分私钥d1[1,n1]d_1 \in [1,n-1]
  2. 参与方BB使用安全的随机数生成源,随机生成部分私钥d2[1,n1]d_2 \in [1,n-1]

为了获得完整公钥PKP_K供验签时使用,参与方A,BA,B需要通过 1轮通信以构造PKP_K

  1. 参与方AA计算部分公钥P1=d11GP_1 = d_1^{-1} * G,并发送给参与方BB
  2. 参与方BB计算部分公钥P2=d21GP_2 = d_2^{-1} * G,并发送给参与方AA
  3. 参与方AA使用获得的部分公钥P2P_2,计算完整公钥PK=d11P2G=d11d21GGP_K = d_1 ^{-1} * P_2 - G = d_1^{-1}*d_2^{-1} * G - G
  4. 参与方BB使用获得的部分公钥P1P_1,计算完整公钥PK=d21P1G=d11d21GGP_K = d_2 ^{-1} * P_1 - G = d_1^{-1}*d_2^{-1} * G - G

基于椭圆曲线离散对数问题的困难性可知,任意参与方无法通过另一参与方发送的部分公钥推导出相应的部分私钥,因此密钥生成过程不会造成部分私钥的泄漏。

2.2 门限签名

在标准SM2算法中,签名者需要生成一个随机数kk以计算随机椭圆曲线点Q=(x1,y1)=[k]GQ= (x_1,y_1) = [k]*G。如果随机数kk仅由一方生成,那么在计算签名参数ss时会造成部分私钥的泄漏。因此,随机数kk需要由两方协作生成,且签名过程不应泄漏随机数的信息,步骤如下:

  1. 参与方AA计算消息mm的摘要ee
  2. 参与方AA随机生成部分签名随机数w1[1,n1]w_1 \in [1,n-1],计算Q1=[w1]GQ_1 = [w_1] * G,将摘要eeQ1Q_1发送给参与方BB
  3. 参与方BB随机生成部分签名随机数w2[1,n1]w_2 \in [1,n-1],计算完整的随机椭圆曲线点Q=[w2]G+d21Q1Q = [w_2]*G + d_2^{-1} * Q_1
  4. 参与方BB计算r=(x1+e)modqr = (x_1 + e) \mod qs1=d2(r+w2)modqs_1 = d_2 * (r + w_2) \mod q,将(r,s1)(r,s1)包装为部分签名sig1sig_1发送给参与方A。
  5. 参与方AA计算s=(d1(s1+w1)r)modqs = (d_1 * (s_1 + w_1) -r)\mod q(r,s)(r,s)即为最终的门限签名结果。

上述签名过程需要 1轮通信以协作生成门限签名值。观察到,在步骤2、3中,随机数w1,w2w_1,w_2以基点乘法的形式对外公开。由于椭圆曲线离散对数问题的困难性,攻击者无法通过公开信息获取随机数w1,w2w_1,w_2;在步骤4、5中,与部分私钥d1,d2d_1,d_2相关的表达式均包含2个无法被攻击者或另一参与方掌握的未知数,从这 表达式中获取部分私钥d1,d2d_1,d_2和随机数w1,w2w_1,w_2的难度等同于穷举,在SM2参数非常大的条件下,可视为无法通过常规计算得出。综上所述,门限签名的过程不会造成随机数与部分私钥的泄漏。

2.3 验签

**本算法的验签过程与标准SM2算法完全一致。**任何人都可以通过公开的完整公钥PKP_K验证门限签名的合法性。完整的签名过程可参见国标GM/T 0003.2-2012.2 的相关内容。

3 功能实现

根据第二章所述的算法原理,结合铜锁项目的实际架构,我们抽象出如下图所示的关于SM2门限密钥对生成和SM2门限签名的流程图:

生成SM2门限密钥对流程图

图1:生成SM2门限密钥对流程图

计算SM2门限签名值流程图

图2:计算SM2门限签名值流程图

根据上述流程图,铜锁在SM2两方门限算法的实现过程中对算法框架进行了合理拆分,旨在令用户调用单个函数即可完成流程图中的某一完整阶段。具体来说:

  1. 生成部分密钥对时,用户使用现有EVP相关函数生成部分私钥,然后调用SM2_THRESHOLD_derive_partial_pubkey函数生成部分公钥;
  2. 收到另一方的部分公钥后调用SM2_THRESHOLD_derive_complete_pubkey生成完整密钥对;计算签名值时,根据数据类型的不同两个参与方交替调用流式处理SM2_THRESHOLD_sign1_init``SM2_THRESHOLD_sign1_update``SM2_THRESHOLD_sign1_final或一次性处理SM2_THRESHOLD_sign1_oneshot进行步骤1的处理;
  3. 最后使用SM2_THRESHOLD_sign2SM2_THRESHOLD_sign3函数完成协作签名的生成。

具体的函数用途说明、参数和返回值定义,可以通过点击“阅读原文”跳转到对应Pull Request后,查看代码中的头文件获得。本文中仅给出本次增加的全部API原型:

EVP_PKEY *SM2_THRESHOLD_derive_partial_pubkey(const EVP_PKEY *key);

EVP_PKEY *SM2_THRESHOLD_derive_complete_pubkey(const EVP_PKEY *self_key,
const EVP_PKEY *peer_pubkey);

int SM2_THRESHOLD_sign1_oneshot(const EVP_PKEY *pubkey,
const EVP_MD *type,
const uint8_t *id,
const size_t id_len,
const uint8_t *msg, size_t msg_len,
uint8_t *digest, size_t *dlen);

int SM2_THRESHOLD_sign1_init(EVP_MD_CTX *ctx, const EVP_MD *digest,
const EVP_PKEY *pubkey, const uint8_t *id,
const size_t id_len);

int SM2_THRESHOLD_sign1_update(EVP_MD_CTX *ctx, const uint8_t *msg,
size_t msg_len);

int SM2_THRESHOLD_sign1_final(EVP_MD_CTX *ctx, uint8_t *digest,
size_t *dlen);

int SM2_THRESHOLD_sign2(const EVP_PKEY *key,
const EVP_PKEY *peer_Q1,
uint8_t *digest, size_t dlen,
unsigned char **sig, size_t *siglen);

int SM2_THRESHOLD_sign3(const EVP_PKEY *key, const EVP_PKEY *temp_key,
const unsigned char *sig2, size_t sig2_len,
unsigned char **sig, size_t *siglen);

4 配置与使用

在铜锁中,SM2两方门限签名功能需通过配置选项enable-sm2_threshold开启。另外,如果系统支持,建议开启铜锁针对64位平台实现的SM2性能优化开关enable-ec_sm2p_64_gcc_128

./config --prefix=/opt/tongsuo -Wl,-rpath,/opt/tongsuo/lib enable-sm2_threshold enable-ec_sm2p_64_gcc_128

为了降低门限签名的使用门槛,铜锁相应地实现了SM2两方门限签名的命令行工具,通过命令行就可以非常方便地体验和测试SM2两方门限签名。

我们接下来用一个例子来演示命令行工具的用法。假设参与的两方为Alice和Bob,双方均持有自己的部分私钥,并最终协同生成完整签名。具体步骤是:

4.1 门限密钥对生成

  1. Alice和Bobo各生成自己的部分私钥,分别存储至文件A.key``B.key中,并基于部分私钥导出对应的部分公钥,存储在A.pubB.pub
Alice:
genpkey -algorithm "ec" -pkeyopt "ec_paramgen_curve:sm2" -out A.key
sm2_threshold -derive -inkey A.key -pubout A.pub

Bob:
genpkey -algorithm "ec" -pkeyopt "ec_paramgen_curve:sm2" -out B.key
sm2_threshold -derive -inkey B.key -pubout B.pub
  1. Alice将自己的部分公钥A.pub发送给Bob;Bob将自己的部分公钥B.pub发送给Alice。然后Alice和Bob可以合成完整公钥pubkey.pem,后续使用此完整公钥进行验签。
Alice:
tongsuo sm2_threshold -inkey A.key -peerkey B.pub -pubout pubkey.pem

Bob:
tongsuo sm2_threshold -inkey B.key -peerkey A.pub -pubout pubkey.pem
  1. 至此,Alice和Bob完成了相关密钥的准备工作,可以协同进行门限签名。

4.2 两方门限签名计算

  1. 假设Alice是门限签名的发起方,那么她需要生成一个随机内容的文件file做为原文:
dd if=/dev/urandom of=file bs=1024 count=1
  1. Alice首先执行门限签名的第一步,即针对待签名内容file进行摘要计算并生成一个临时的私钥tempA.key以及对应的临时公钥tempA.pub。然后Alice将此命令输出的摘要值保存为dgst文件并将其和tempA.pub一同发送给Bob。
tongsuo sm2_threshold -sign1 -newkey tempA.key -pubout tempA.pub -pubin -inkey pubkey.pem -in file
  1. Bob在收到dgsttempA.pub后,开始执行第二步,即使用Alice的临时公钥tempA.pub和自己的部分私钥B.key,基于消息的摘要值dgst,生成部分签名值partial_sig.txt。然后Bob将partial_sig.txt发送给Alice:
tongsuo sm2_threshold -sign2 -inkey B.key -temppeerkey tempA.pub -digest dgst -sigform hex -out partial_sig.txt
  1. Alice在收到部分签名值partial_sig.txt后,执行第三步,即使用自己的部分私钥A.key,临时私钥tempA.key和部分签名值partial_sig.txt,生成完整签名值,并输出至文件final_sig.txt
tongsuo sm2_threshold -sign3 -inkey A.key -sigform hex -sigfile partial_sig.txt -tempkey tempA.key -out final_sig.txt
  1. 任何人可以使用完整公钥,对文件file和签名final_sig.txt,验证签名结果。此步骤和验证标准SM2签名值无异,因此就不再赘述,读者可自行查阅相关文档。

5 性能指标

与标准SM2数字签名算法相比,铜锁实现的SM2两方门限签名算法需要2个参与方共同完成密钥对的生成和签名值的计算,涉及到的算法流程更加复杂,所需的椭圆曲线运算较标准SM2算法更多。具体来看,在密钥对生成环节,SM2两方门限签名算法相较于标准算法平均每个密钥对需要多执行1次曲线点乘和1次模逆元计算;在协同签名环节,计算每个签名值需要多执行1次曲线点乘和1次曲线基点乘计算,但可以省去1次模逆元计算;验签过程与标准算法一致。

从上述分析结果来看,SM2两方门限签名算法的验签性能应与标准算法接近,签名和密钥生成的性能会有一定的退化,且稍弱于验签性能。为了验证上述判断,铜锁测试了在开启SM2-64位平台优化下的SM2两方门限签名算法性能(纯算法性能,不包含通信开销),并与标准SM2数字签名算法进行比较,得到测试结果如下表所示:

签名验签密钥对生成
SM2两方门限签名算法7473/s10579/s7317/s
标准SM2算法(无优化)5970/s6176/s5161/s
标准SM2算法(有优化)23277/s12193/s15773/s

测试结果与预期一致,验签性能与标准SM2算法接近,密钥对生成和签名性能略低于验签性能。不过,所实现的SM2两方门限算法的性能依然要好于无优化的标准SM2算法,相比使用同态加密的一些门限方案性能优势更大,能够满足大多数情况下的生产环境需求。

铜锁获2023开源创新榜“优秀开源项目

· 阅读需 6 分钟
Paul Yang
PMC Member of Tongsuo

tongsuo_cert.jpg 2023年12月15日,由中国科协科学技术传播中心中国计算机学会中国通信学会中国科学院软件研究所共同主办,CSDN 承办的 2023 开源创新榜专家评审会在国家科技传播中心成功举办。评委会主任、中国计算机学会开源发展委员会主任王怀民院士,评委会副主任、中国科协科学技术传播中心副主任陈锐,评委会副主任、中国通信学会副理事长兼秘书长张延川,评委会副主任、中国科学院软件研究所所长赵琛与来自全国学会、大学、科研院所、企业、开源基金会、行业联盟等二十多位开源专家共同参与了本届榜单评审工作,会议由陈锐主持。

2023 年开源创新榜相较往年有以下几个变化:

一是进一步提升权威性,主办单位新加入中国计算机学会、中国通信学会、中国科学院软件研究所,四家主办单位优势互补,共同推动榜单策划、征集申报、专家评审等工作重点。

二是进一步提升公信力,由王怀民院士担任评委会主任,指导组建了结构更加科学、领域更加全面的评审专家库,从中提名形成最终评审专家。

三是进一步提升专业度,围绕项目、社区、人物三大类别,四家主办单位打磨了更加客观、严谨、贴合实际的评审标准和更加开放、公平、科学的评审办法,在征集过程中公开标准细节,接受社会的意见反馈,形成良性循环。

评审委员会主任王怀民院士指出,人类文明和科技文明发展中,一项成果得以记录、传播、共享才对推动社会进步有价值,开源是群体智慧的现代表征,在当下推动高质量发展、高水平安全具有重要现实意义。通过开源创新榜征集评选工作,可以挖掘和推广我国在开源技术领域的优秀成果和先进经验,为一线科技工作者及其创新成果创造更多展示、交流、推广的机会,希望大家共同努力,将开源创新榜打造成为业界最最权威、最典型和最具影响力的标杆。

评委会最终评选出优秀开源项目 20 个,开放原子开源基金会旗下“孵化期”开源项目“铜锁开源密码学算法库”入选其中:

Screen Shot

铜锁(Tongsuo)是一个提供现代密码学算法和安全通信协议的开源基础密码库,为存储、网络、密钥管理、隐私计算等诸多业务场景提供底层的密码学基础能力,实现数据在传输、使用、存储等过程中的私密性、完整性和可认证性,为数据生命周期中的隐私和安全提供保护能力。

铜锁于2020年10月开源,已获得国家密码管理局商用密码检测中心颁发的商用密码产品认证证书,符合GM/T 0028《密码模块安全技术要求》的安全一级要求,助力用户在密改、密评、等保检查等过程中,更加严谨地满足商用密码技术合规的要求。 当前,铜锁开源项目已经由蚂蚁集团完成了向开放原子开源基金会的捐赠,成为基金会的“孵化期”项目,也是基金会唯一的密码学方向开源项目。在基金会孵化过程中,铜锁开源社区先后启动了“铜锁嵌入式版”和“RustyVault密钥管理系统”两个新项目的开发,已从单一开源项目发展为项目群。蚂蚁集团在铜锁完成捐赠后持续对项目进行投入,成立了铜锁项目管理委员会,引入多家领军企业参与铜锁开源项目的管理,推动铜锁进入到了独立发展的新阶段。

铜锁的商用密码产品认证证书

· 阅读需 1 分钟
Paul Yang
PMC Member of Tongsuo

本页面提供铜锁全部的商用密码产品认证证书(即俗称的国密资质)高清版本下载,铜锁的用户可以自由取用。

Android:BabaSSL移动端软件密码模块

证书编号:GM003312220220743

PDF格式: BabaSSL移动端软件密码模块.pdf

PNG格式 BabaSSL移动端软件密码模块.png

iOS:BabaSSL IOS端软件密码模块

证书编号:GM003312220230052

PDF格式: BabaSSL IOS端软件密码模块.pdf

PNG格式: Page 0001.png

Linux:应用安全软件密码模块(Linux版)

证书编号:GM003312220230044

PDF格式: 应用安全软件密码模块(Linux版).pdf

PNG格式: Page 0001.png

Tongsuo 支持半同态加密算法 Paillier

· 阅读需 16 分钟
Jin Jiu
Maintainer of Tongsuo

背景

《Tongsuo 支持半同态加密算法 EC-ElGamal》中,已经阐述了同态和半同态加密算法的背景和原理,可以移步查阅,总之,同态算法在隐私计算领域有着重要的作用,目前应用比较广泛的是 Paillier 和 EC-ElGamal 半同态加密算法,这两个算法都只支持加法同态,其接口也类似,只是原理和性能不一样,Paillier 是基于复合剩余类的困难性问题(大数分解难题)的公钥加密算法,有点类似 RSA,而 EC-ElGamal 是基于椭圆曲线数学理论的公钥加密算法,其安全性理论上要比 Paillier 要更好,但其性能各有优劣,EC-ElGamal 的加密和密文加法性能要比 Paillier 好,而 Paillier 的解密和密文标量乘法性能要比 EC-ElGamal 好,而且稳定,EC-ElGamal 的解密性能与解密的数字大小有关系,数字越大可能需要解密的时间越长,这与 EC-ElGamal 解密用到的解密表有关系,而 Paillier 的解密就没有这个问题,可以根据自己的业务特点选择使用 Paillier 还是 EC-ElGamal。

Paillier 原理

密钥生成

  1. 随机选择两个大素数 p,qp,q,满足 gcd(pq,(p1)(q1))=1gcd(pq, (p-1)(q-1))=1,且满足 p 和 q 的长度相等;
  2. 计算 n=pqn=pq以及λ=lcm(p1,q1)\lambda=lcm(p-1, q-1)lcmlcm 表示最小公倍数;
  3. 随机选择整数 gZn2g\gets \mathbb{Z}_{n^2}^*,一般gg的计算公式如下:
    1. 随机选择整数 kZnk \in \mathbb{Z}_{n}^*
    2. 计算:g=1+kng=1+kn,为了简化和提高性能,kk一般选1,g=1+ng=1+n
  4. 定义 L 函数:L(x)=x1nL(x)=\frac{x-1}{n},计算:μ=(L(gλ mod n2))1 mod n\mu=(L(g^\lambda \space mod \space n^2))^{-1} \space mod \space n
  5. 公钥:(n, g)(n, \space g),私钥:(λ, μ)(\lambda, \space \mu)

加密

  1. 明文 m,满足 n<m<n-n < m < n
  2. 选择随机数 r,满足 0r<n0 \leq r<nrZnr\in \mathbb{Z}_n^*
  3. 计算密文:c=gmrn mod n2c=g^mr^n\space mod\space n^2

解密

  1. 密文 c,满足 cZn2c\in \mathbb{Z}_{n^2}^*
  2. 计算明文:m=L(cλ mod n2)×μ mod nm=L(c^\lambda \space mod \space n^2) \times \mu \space mod \space n

密文加法

  1. 密文:c1 and c2c_1 \space and \space c_2,计算:c=c1×c2 mod n2c=c_1 \times c_2 \space mod\space n^2cc 就是密文加法的结果

密文减法

  1. 密文:c1 and c2c_1 \space and \space c_2,计算:c=c1c2 mod n2c=\frac{c_1}{c_2} \space mod\space n^2cc 就是密文减法的结果

密文标量乘法

  1. 密文:c1c_1,明文标量:aa,计算:c=c1a mod n2c=c_1^a \space mod\space n^2cc 就是密文标量乘法的结果

正确性

加解密正确性

公式推导需要用到 Carmichael 函数和确定合数剩余的公式,下面简单说明一下:

  • Carmichael 函数
    1. n=pqn=pq,其中:p,qp,q为大素数
    2. 欧拉函数:ϕ(n)\phi(n),Carmichael函数:λ(n)\lambda(n)
    3. ϕ(n)=(p1)(q1)\phi(n)=(p-1)(q-1)λ(n)=lcm(p1,q1)\lambda(n)=lcm(p-1, q-1)时,其中:Zn2=ϕ(n2)=nϕ(n)\left|\mathbb{Z}_{n^2}^*\right|=\phi(n^2)=n\phi(n)。对于任意 wZn2w \in \mathbb{Z}_{n^2}^*,有如下性质:{wλ=1 mod nwnλ=1 mod n2\begin{cases} w^\lambda=1\space mod\space n \\ w^{n\lambda}=1\space mod\space n^2 \end{cases}
  • 判定合数剩余
    1. 判定合数剩余类问题是指n=pqn=pq,其中:p,qp,q为大素数,任意给定 yZn2y \in \mathbb{Z}_{n^2}^*,使得 z=yn mod n2z=y^n\space mod\space n^2,则说 zz是模n2n^2的第nn次剩余
    2. nn项剩余的集合是Zn2\mathbb{Z}_{n^2}^*的一个ϕ(n)\phi(n)阶乘法子集
    3. 每个第nn项剩余zz都正好拥有nnnn阶的根,其中只有一个是严格小于nn的(即 zn mod n\sqrt[n]{z}\space mod\space n
    4. nn项剩余都可以写成 (1+n)x=1+xn mod n2(1+n)^x=1+xn\space mod\space n^2的形式
  • 正确性验证
cλ mod n2=(gmrn)λ mod n2=gmλrnλ mod n2=gmλ mod n2=(1+n)mλ mod n2=1+nmλ mod n2\begin{align} c^\lambda\space mod\space n^2 & =(g^mr^n)^\lambda\space mod\space n^2 \\ & =g^{m\lambda}r^{n\lambda}\space mod\space n^2 \\ & =g^{m\lambda}\space mod\space n^2 \\ & =(1+n)^{m\lambda}\space mod\space n^2 \\ & =1+nm\lambda\space mod\space n^2 \\ \end{align} gλ mod n2=(1+n)λ mod n2=1+nλ mod n2\begin{align} g^\lambda\space mod\space n^2 & =(1+n)^\lambda\space mod\space n^2 \\ & =1+n\lambda\space mod\space n^2 \\ \end{align} L(cλ mod n2)=cλ mod n21n=1+nmλ mod n21n=mλ mod n2L(c^\lambda\space mod\space n^2)=\frac{c^\lambda\space mod\space n^2-1}{n}=\frac{1+nm\lambda\space mod\space n^2-1}{n}=m\lambda\space mod\space n^2 L(gλ mod n2)=gλ mod n21n=1+nλ mod n21n=λ mod n2L(g^\lambda\space mod\space n^2)=\frac{g^\lambda\space mod\space n^2-1}{n}=\frac{1+n\lambda\space mod\space n^2-1}{n}=\lambda\space mod\space n^2

解密:

m=L(cλ mod n2)×μ mod n=L(cλ mod n2)L(gλ mod n2) mod n=mλ mod n2λ mod n2 mod n=m mod n=m\begin{align} m & = L(c^\lambda \space mod \space n^2) \times \mu \space mod \space n \\ & = \frac{L(c^\lambda \space mod \space n^2)}{L(g^\lambda \space mod \space n^2)}\space mod \space n \\ & = \frac{m\lambda\space mod\space n^2}{\lambda\space mod\space n^2}\space mod \space n \\ & = m\space mod \space n \\ & = m \end{align}

密文加法正确性

Encrypt(m1)=c1Encrypt(m_1)=c_1Encrypt(m2)=c2Encrypt(m_2)=c_2

Decrypt(c)=Decrypt(c1×c2 mod n2)=Decrypt((gm1r1n×gm2r2n) mod n2)=Decrypt((gm1+m2(r1+r2)n) mod n2)=Decrypt((gm1+m2rn) mod n2)=m1+m2\begin{align} Decrypt(c) & = Decrypt(c_1 \times c_2 \space mod\space n^2) = Decrypt((g^{m_1}r_1^n\times g^{m_2}r_2^n)\space mod\space n^2) \\ & = Decrypt((g^{m_1+m_2}(r_1+r_2)^n)\space mod\space n^2)=Decrypt((g^{m_1+m_2}{r}^n)\space mod\space n^2)=m_1+m_2\\ \end{align}

密文减法正确性

Encrypt(m1)=c1Encrypt(m_1)=c_1Encrypt(m2)=c2Encrypt(m_2)=c_2

Decrypt(c)=Decrypt(c1c2 mod n2)=Decrypt(gm1r1ngm2r2n mod n2)=Decrypt((gm1r1n×(gm2r2n)1) mod n2)=Decrypt((gm1r1n×gm2r2n mod n2)=Decrypt((gm1m2(r1r21)n) mod n2)=Decrypt((gm1m2rn) mod n2)=m1m2\begin{align} Decrypt(c) & = Decrypt(\frac{c_1}{c_2} \space mod\space n^2) = Decrypt(\frac{g^{m_1}r_1^n}{ g^{m_2}r_2^n}\space mod\space n^2) \\ & = Decrypt((g^{m_1}r_1^n\times (g^{m_2}r_2^n)^{-1})\space mod\space n^2) = Decrypt((g^{m_1}r_1^n\times g^{-m_2}r_2^{-n}\space mod\space n^2) \\ & = Decrypt((g^{m_1-m_2}(r_1r_2^{-1})^n)\space mod\space n^2)=Decrypt((g^{m_1-m_2}{r}^n)\space mod\space n^2)=m_1-m_2\\ \end{align}

密文标量乘法正确性

Encrypt(m1)=c1Encrypt(m_1)=c_1

Decrypt(c)=Decrypt(c1a mod n2)=Decrypt((gm1rn)a mod n2)=Decrypt((gam1(ra)n) mod n2)=Decrypt((gam1r1n) mod n2)=am1\begin{align} Decrypt(c) & = Decrypt(c_1^a \space mod\space n^2) = Decrypt((g^{m_1}r^n)^a\space mod\space n^2) \\ & = Decrypt((g^{am_1}(r^a)^n)\space mod\space n^2)=Decrypt((g^{am_1}r_1^n)\space mod\space n^2)=am_1\\ \end{align}

算法实现

接口定义

  • 对象相关接口

    • 公/私钥对象:PAILLIER_KEY,该对象用来保存 paillier 公钥和私钥的基本信息,比如p,q,n,g,λ,μp,q,n,g,\lambda,\mu等信息,私钥保存所有字段,公钥只保存 n,gn,g,其他字段为空或者0。相关接口如下:

      // 创建 PAILLIER_KEY 对象
      PAILLIER_KEY *PAILLIER_KEY_new(void);

      // 释放 PAILLIER_KEY 对象
      void PAILLIER_KEY_free(PAILLIER_KEY *key);

      // 拷贝 PAILLIER_KEY 对象,将 src 拷贝到 dest 中
      PAILLIER_KEY *PAILLIER_KEY_copy(PAILLIER_KEY *dest, PAILLIER_KEY *src);

      // 复制 PAILLIER_KEY 对象
      PAILLIER_KEY *PAILLIER_KEY_dup(PAILLIER_KEY *key);

      // 将 PAILLIER_KEY 对象引用计数加1,释放 PAILLIER_KEY 对象时若引用计数不为0则不能释放其内存
      int PAILLIER_KEY_up_ref(PAILLIER_KEY *key);

      // 生成 PAILLIER_KEY 对象中的参数,bits 为随机大素数 p、q 的二进制位长度
      int PAILLIER_KEY_generate_key(PAILLIER_KEY *key, int bits);

      // 获取 key 的类型:公钥 or 私钥
      // PAILLIER_KEY_TYPE_PUBLIC 为私钥,PAILLIER_KEY_TYPE_PRIVATE 为私钥
      int PAILLIER_KEY_type(PAILLIER_KEY *key);
    • 上下文对象:PAILLIER_CTX,该对象用来保存公私钥对象以及一些其他内部用到的信息,是 paillier算法其他接口的第一个参数。相关接口如下:

      // 创建 PAILLIER_CTX 对象,key 为 paillier 公钥或者私钥,threshold 为支持最大的数字阈值,加密场景可设置为0,解密场景可使用默认值:PAILLIER_MAX_THRESHOLD
      PAILLIER_CTX *PAILLIER_CTX_new(PAILLIER_KEY *key, int64_t threshold);

      // 释放 PAILLIER_CTX 对象
      void PAILLIER_CTX_free(PAILLIER_CTX *ctx);

      // 拷贝 PAILLIER_CTX 对象,将 src 拷贝到 dest 中
      PAILLIER_CTX *PAILLIER_CTX_copy(PAILLIER_CTX *dest, PAILLIER_CTX *src);

      // 复制 PAILLIER_CTX 对象
      PAILLIER_CTX *PAILLIER_CTX_dup(PAILLIER_CTX *src);
    • 密文对象:PAILLIER_CIPHERTEXT,该对象是用来保存 paillier 加密后的结果信息,用到PAILLIER_CIPHERTEXT的地方,可调用如下接口:

      // 创建 PAILLIER_CIPHERTEXT 对象
      PAILLIER_CIPHERTEXT *PAILLIER_CIPHERTEXT_new(PAILLIER_CTX *ctx);

      // 释放 PAILLIER_CIPHERTEXT 对象
      void PAILLIER_CIPHERTEXT_free(PAILLIER_CIPHERTEXT *ciphertext);
  • 加密/解密接口

    // 加密,将明文 m 进行加密,结果保存到 PAILLIER_CIPHERTEXT 对象指针 out 中
    int PAILLIER_encrypt(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *out, int32_t m);

    // 解密,将密文 c 进行解密,结果保存到 int32_t 指针 out 中
    int PAILLIER_decrypt(PAILLIER_CTX *ctx, int32_t *out, PAILLIER_CIPHERTEXT *c);
  • 密文加/减/标量乘运算接口

    // 密文加,r = c1 + c2
    int PAILLIER_add(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
    PAILLIER_CIPHERTEXT *c1, PAILLIER_CIPHERTEXT *c2);

    // 密文标量加,r = c1 * m
    int PAILLIER_add_plain(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
    PAILLIER_CIPHERTEXT *c1, int32_t m);

    // 密文减,r = c1 - c2
    int PAILLIER_sub(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
    PAILLIER_CIPHERTEXT *c1, PAILLIER_CIPHERTEXT *c2);

    // 密文标量乘,r = c * m
    int PAILLIER_mul(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
    PAILLIER_CIPHERTEXT *c, int32_t m);
  • 编码/解码接口

同态加密涉及到多方参与,可能会需要网络传输,这就需要将密文对象PAILLIER_CIPHERTEXT编码后才能传递给对方,对方也需要解码得到PAILLIER_CIPHERTEXT对象后才能调用其他接口进行运算。 接口如下:

// 编码,将密文 ciphertext 编码后保存到 out 指针中,out 指针的内存需要提前分配好;
// 如果 out 为 NULL,则返回编码所需的内存大小;
// flag:标志位,预留,暂时没有用
size_t PAILLIER_CIPHERTEXT_encode(PAILLIER_CTX *ctx, unsigned char *out,
size_t size,
const PAILLIER_CIPHERTEXT *ciphertext,
int flag);

// 解码,将长度为 size 的内存数据 in 解码后保存到密文对象 r 中
int PAILLIER_CIPHERTEXT_decode(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
unsigned char *in, size_t size);

以上所有接口详细说明请参考 paillier API 文档:https://www.yuque.com/tsdoc/api/slgr6f

核心实现

  • Paillier Key

    Paillier 不像 EC-ElGamal,EC-ElGamal 在 Tongsuo 里面直接复用 EC_KEY 即可,Paillier Key 在 Tongsuo 里面则需要实现一遍,主要功能有:公/私钥的生成、PEM 格式存储、公/私钥解析和文本展示,详情请查阅代码:crypto/paillier/paillier_key.c、crypto/paillier/paillier_asn1.c、crypto/paillier/paillier_prn.c

  • Paillier 加解密、密文运算

    Paillier 的加解密和密文运算算法非常简单,主要是大数的模幂运算,使用 Tongsuo 里面的 BN 相关接口就可以,需要注意的是,负数的加密/解密用到模逆运算,不能直接按公式计算(c=gmrn mod n2c=g^mr^n\space mod\space n^2),这是因为 Openssl 的接口BN_mod_exp没有关注指数(上面公式的 m)是不是负数,如果是负数的话需要做一次模逆运算:m=k,c=gmrn mod n2=gkrn mod n2=(gk)1rn mod n2m=-k,c=g^mr^n\space mod\space n^2=g^{-k}r^n\space mod\space n^2=(g^k)^{-1}r^n\space mod\space n^2,这里计算出 gkg^k之后做一次模逆运算(BN_mod_inverse)再与rnr^n相乘;解密的时候,需要检查是否检查了阈值(PAILLIER_MAX_THRESHOLD),超出则说明是负数,需要减去 n 才得到真正的结果。 密文减法也需要用到模逆运算,通过密文减法的公式(c=c1c2 mod n2=c1c21 mod n2c=\frac{c_1}{c_2} \space mod\space n^2=c_1c_2^{-1}\space mod\space n^2)得知,c2c_2需要进行模逆运算(BN_mod_inverse)再与c1c_1相乘。 详情请查阅代码:crypto/paillier/paillier_crypt.c

  • Paillier 命令行

    为了提高 Paillier 的易用性,Tongsuo 实现了如下 paillier 子命令:

    $ /opt/tongsuo-debug/bin/openssl paillier -help
    Usage: paillier [action options] [input/output options] [arg1] [arg2]

    General options:
    -help Display this summary

    Action options:
    -keygen Generate a paillier private key
    -pubgen Generate a paillier public key
    -key Display/Parse a paillier private key
    -pub Display/Parse a paillier public key
    -encrypt Encrypt a number with the paillier public key, usage: -encrypt 99, 99 is an example number
    -decrypt Decrypt a ciphertext using the paillier private key, usage: -decrypt c1, c1 is an example ciphertext
    -add Paillier homomorphic addition: add two ciphertexts, usage: -add c1 c2, c1 and c2 are tow example ciphertexts, result: E(c1) + E(c2)
    -add_plain Paillier homomorphic addition: add a ciphertext to a plaintext, usage: -add_plain c1 99, c1 is an example ciphertext, 99 is an example number, result: E(c1) + 99
    -sub Paillier homomorphic subtraction: sub two ciphertexts, usage: -sub c1 c2, c1 and c2 are tow example ciphertexts, result: E(c1) - E(c2)
    -mul Paillier homomorphic scalar multiplication: multiply a ciphertext by a known plaintext, usage: -mul c1 99, c1 is an example ciphertext, 99 is an example number, result: E(c1) * 99

    Input options:
    -in val Input file
    -key_in val Input is a paillier private key used to generate public key

    Output options:
    -out outfile Output the paillier key to specified file
    -noout Don't print paillier key out
    -text Print the paillier key in text
    -verbose Verbose output

    Parameters:
    arg1 Argument for encryption/decryption, or the first argument of a homomorphic operation
    arg2 The second argument of a homomorphic operation

    主要命令有:

    • keygen:生成 paillier 私钥
    • pubgen:用 paillier 私钥生成公钥
    • key:文本显示 paillier 私钥
    • pub:文本显示 paillier 公钥
    • encrypt:对数字进行加密,输出 paillier 加密的结果,需要通过参数-key_in参数指定 paillier 公钥文件路径,如果加密负数则需要将-_代替,因为-会被 openssl 解析成预定义参数了(下同)
    • decrypt:对 paillier 密文进行解密,输出解密结果,需要通过-key_in参数指定 paillier 私钥文件路径
    • add:对两个 paillier 密文进行同态加法操作,输出同态加法密文结果,需要通过参数-key_in参数指定 paillier 公钥文件路径
    • add_plain:将 paillier 密文和明文相加,输出同态加法密文结果,需要通过参数-key_in参数指定 paillier 公钥文件路径
    • sub:对两个 paillier 密文进行同态减法操作,输出同态减法密文结果,需要通过参数-key_in参数指定 paillier 公钥文件路径
    • mul:将 paillier 密文和明文相乘,输出同态标量乘法密文结果,需要通过参数-key_in参数指定 paillier 公钥文件路径

    通过以上命令即可在命令行进行 Paillier 算法实验,降低入门门槛,详情请查阅代码:apps/paillier.c

    另外还实现了 paillier 的 speed 命令,可以进行性能测试,详情请查阅代码:apps/speed.c

    用法&例子

    demo 程序

    paillier_test.c
    #include <stdio.h>
    #include <time.h>
    #include <openssl/paillier.h>
    #include <openssl/pem.h>

    #define CLOCKS_PER_MSEC (CLOCKS_PER_SEC/1000)

    int main(int argc, char *argv[])
    {
    int ret = -1;
    int32_t r;
    clock_t begin, end;
    PAILLIER_KEY *pail_key = NULL, *pail_pub = NULL;
    PAILLIER_CTX *ctx1 = NULL, *ctx2 = NULL;
    PAILLIER_CIPHERTEXT *c1 = NULL, *c2 = NULL, *c3 = NULL;
    FILE *pk_file = fopen("pail-pub.pem", "rb");
    FILE *sk_file = fopen("pail-key.pem", "rb");

    if ((pail_pub = PEM_read_PAILLIER_PublicKey(pk_file, NULL, NULL, NULL)) == NULL)
    goto err;
    if ((pail_key = PEM_read_PAILLIER_PrivateKey(sk_file, NULL, NULL, NULL)) == NULL)
    goto err;

    if ((ctx1 = PAILLIER_CTX_new(pail_pub, PAILLIER_MAX_THRESHOLD)) == NULL)
    goto err;
    if ((ctx2 = PAILLIER_CTX_new(pail_key, PAILLIER_MAX_THRESHOLD)) == NULL)
    goto err;

    if ((c1 = PAILLIER_CIPHERTEXT_new(ctx1)) == NULL)
    goto err;
    if ((c2 = PAILLIER_CIPHERTEXT_new(ctx1)) == NULL)
    goto err;

    begin = clock();
    if (!PAILLIER_encrypt(ctx1, c1, 20000021))
    goto err;
    end = clock();
    printf("PAILLIER_encrypt(20000021) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);

    begin = clock();
    if (!PAILLIER_encrypt(ctx1, c2, 500))
    goto err;
    end = clock();
    printf("PAILLIER_encrypt(500) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);

    if ((c3 = PAILLIER_CIPHERTEXT_new(ctx1)) == NULL)
    goto err;

    begin = clock();
    if (!PAILLIER_add(ctx1, c3, c1, c2))
    goto err;
    end = clock();
    printf("PAILLIER_add(C2000021,C500) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);

    begin = clock();
    if (!(PAILLIER_decrypt(ctx2, &r, c3)))
    goto err;
    end = clock();
    printf("PAILLIER_decrypt(C20000021,C500) result: %d, cost: %lfms\n", r, (double)(end - begin)/CLOCKS_PER_MSEC);

    begin = clock();
    if (!PAILLIER_mul(ctx1, c3, c2, 800))
    goto err;
    end = clock();
    printf("PAILLIER_mul(C500,800) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);

    begin = clock();
    if (!(PAILLIER_decrypt(ctx2, &r, c3)))
    goto err;
    end = clock();
    printf("PAILLIER_decrypt(C500,800) result: %d, cost: %lfms\n", r, (double)(end - begin)/CLOCKS_PER_MSEC);


    printf("PAILLIER_CIPHERTEXT_encode size: %zu\n", PAILLIER_CIPHERTEXT_encode(ctx2, NULL, 0, NULL, 1));

    ret = 0;
    err:
    PAILLIER_KEY_free(pail_key);
    PAILLIER_KEY_free(pail_pub);
    PAILLIER_CIPHERTEXT_free(c1);
    PAILLIER_CIPHERTEXT_free(c2);
    PAILLIER_CIPHERTEXT_free(c3);
    PAILLIER_CTX_free(ctx1);
    PAILLIER_CTX_free(ctx2);
    fclose(sk_file);
    fclose(pk_file);
    return ret;
    }

编译和运行

先确保 Tongsuo 开启 paillier,如果是手工编译 Tongsuo,可参考如下编译步骤:

# 下载代码
git clone git@github.com:Tongsuo-Project/Tongsuo.git


# 编译参数需要加上:enable-paillier
./config --debug no-shared no-threads enable-paillier --strict-warnings -fPIC --prefix=/opt/tongsuo

# 编译
make -j

# 安装到目录 /opt/tongsuo
sudo make install

编译 demo 程序

gcc -Wall -g -o paillier_test ./paillier_test.c -I/opt/tongsuo/include -L/opt/tongsuo/lib -lssl -lcrypto

生成 Paillier 公私钥

# 先生成私钥
/opt/tongsuo/bin/openssl paillier -keygen -out pail-key.pem
# 用私钥生成公钥
/opt/tongsuo/bin/openssl paillier -pubgen -key_in ./pail-key.pem -out pail-pub.pem

运行结果

$ ./paillier_test
PAILLIER_encrypt(20000021) cost: 3.202000ms
PAILLIER_encrypt(500) cost: 0.442000ms
PAILLIER_add(C2000021,C500) cost: 0.047000ms
PAILLIER_decrypt(C20000021,C500) result: 20000521, cost: 0.471000ms
PAILLIER_mul(C500,800) cost: 0.056000ms
PAILLIER_decrypt(C500,800) result: 400000, cost: 0.464000ms
PAILLIER_CIPHERTEXT_encode size: 0