#377 Linux 相关标准

2020-03-09
  • Linux Standard Base (LSB)
  • Filesystem Hierarchy Standard (FHS)
  • Application Programming Interface (API) Standards
  • Single UNIX Specification version 4, The Open Group
  • Single UNIX Specification version 3, The Open Group
  • Single UNIX Specification version 2, The Open Group
  • DWARF Standards
  • DWARF Version 4
  • DWARF Version 3
  • DWARF Version 2.0
  • ELF and ABI Standards
  • Tool Interface Standard (TIS) Portable Formats Specification, version 1.1
  • Tool Interface Standard (TIS) Portable Formats Specification, version 1.2
  • System V ABI Edition 4.1
  • System V ABI - DRAFT 24 April 2001
  • Processor Specific ELF documents
  • System V Application Binary Interface Intel386 Architecture Processor Supplment, Fourth Edition
  • Intel Itanium Processor specific ABI
  • System V Application Binary Interface x86-64 Architecture Processor Supplement Draft Version 0.95 | v0.98 | v0.99
  • System V Application Binary Interface MIPS RISC Processor Supplment, 3rd Edition
  • System V Application Binary Interface PowerPC Processor Supplment. This is one of two known PPC ELF standards. This one was developed by SunSoft. The two versions are not identical, and applications can only conform to one or the other, while it seems possible for an implementation to support both simulaneously.
  • ARM Processor Supplment
  • Processor-Specific ELF Supplement for PA-RISC
  • Motorola 8 and 16 bit Embedded ABI
  • Application Binary Interface (ABI) Specifications/Standards
  • gLSB v1.2, Linux Standard Base
  • archLSB-IA32 v1.2, Linux Standard Base
  • archLSB-PPC32 v1.2, Linux Standard Base
  • 3DNow! (AMD K6 & Athlon), AMD
  • AMD Extensions to the 3DNow1 and MMX Instruction Set, AMD
  • Linux for S/390 (32bit) ELF ABI Supplement, IBM
  • Linux for zSeries (64bit) ELF ABI Supplement, IBM
  • C++ ABI for Itanium, v1.86
  • C++ ABI for Itanium, v1.83
  • C++ ABI for Itanium, v1.75
  • Intel Itanium Processor-Specific ABI
  • Related ABI Projects
  • Moblin 1.9.0 spec
  • Moblin 1.9.1 spec

参考资料与拓展阅读

#376 丰田生产方式与精益生产

2020-03-07

丰田生产方式(Toyota Production System,简称 TPS)是一种以精益生产为核心的生产方式,由日本丰田汽车公司所创立。TPS 的目标是通过消除浪费、提高效率和质量,实现生产过程的最优化,从而提高企业的竞争力。

TPS 的核心思想是“精益生产”,即通过消除浪费来提高生产效率和质量。TPS 将浪费分为七种类型,包括:

  1. 过产:生产过多的产品,导致库存过剩。
  2. 等待:生产过程中的等待时间,如等待零部件、等待机器维修等。
  3. 运输:产品在生产过程中的运输过程中产生的浪费。
  4. 过程:生产过程中的不必要的动作和步骤。
  5. 库存:过多的库存会占用空间、增加成本,并可能导致产品过期或损坏。
  6. 过度加工:对产品进行不必要的加工,增加成本和时间。
  7. 缺陷:产品出现缺陷,需要进行返工或废品处理。

TPS 通过消除这些浪费,实现生产过程的最优化。具体来说,TPS 采用了以下几种方法:

  1. 一次性流程:生产过程中,每个工人只负责一道工序,避免了多次加工和运输。
  2. 拉动生产:根据客户需求,生产所需的产品数量,避免了过产和库存过剩。
  3. 精益生产:通过不断改进生产过程,消除浪费,提高效率和质量。
  4. Jidoka:自动化生产过程中,发现异常情况时,自动停机,避免生产缺陷产品。
  5. Kaizen:不断改进生产过程,提高效率和质量。

TPS 的成功得益于其对生产过程的精细管理和不断改进的精神。TPS 的思想已经被广泛应用于各个领域,成为了一种重要的管理理念。

#375 浏览器端存储

2020-03-02

早期只有一种浏览器存储方式,就是万维网早期,由网景公司设计,加入了 HTTP 1.0 的 Cookie。
HTTP5 的时代,一次性加入了三种 API,分别是 Web Storage,IndexedDB,Web SQL。

  • Cookie:通过小型文本文件,在浏览器端存储一些字符类型 key-value 数据,支持设置一些属性。
  • 有单个 Cookie 的大小限制,也有 Cookie 总数限制。这个因浏览器不同而不同(大小尽可能控制在 4KB 以内)。
  • 每一次 HTTP 调用都会自动带上,发送给服务器端。
  • Web Storage:可以存储大量字符类型的 key-value 数据。
  • localStorage:没有时间限制。
  • sessionStorage:会话结束时自动清除。
  • IndexedDB:NoSQL 数据库,可以存储大量的结构化数据。API 相对复杂一丢丢
  • 功能强大,甚至支持事务和索引。
  • 异步 API。
  • Web SQL:基于 SQL 的浏览器端数据库,后来被废弃。

Cookie

Set-Cookie: name=value; expires=Mon, 21 Oct 2019 07:28:00 GMT; path=/; domain=.example.com; secure; HttpOnly
  • expires 过期时间,GMT 格式。如果不设置该属性,则 Cookie 的生命周期为当前会话,即关闭浏览器后 Cookie 就会被删除。
  • path 路径,表示该 Cookie 归属于哪个路径。默认为当前页面的路径。
  • domain 域名,表示该 Cookie 归属于哪个域名。默认为当前页面的域名。
  • secure 只能通过 HTTPS 协议传输,不能通过 HTTP 协议传输。
  • HttpOnly 只在网络传输时使用,不能通过 JavaScript 访问。

Web Storage

localStorage.setItem("key", "value");
var value = localStorage.getItem("key");

sessionStorage.setItem("key", "value");
var value = sessionStorage.getItem("key");
  • setItem(key, value) 存储数据
  • getItem(key) 读取数据
  • removeItem(key) 删除数据
  • clear() 清空数据 (删除所有的键值对)
  • key(index) 获取键名 (根据索引获取对应的键名)

IndexedDB

没有研究过。

// 打开数据库
var request = indexedDB.open("myDatabase", 1);

// 创建对象仓库
request.onupgradeneeded = function (event) {
  var db = event.target.result;
  var objectStore = db.createObjectStore("users", { keyPath: "id" });
  objectStore.createIndex("name", "name", { unique: false });
  objectStore.createIndex("age", "age", { unique: false });
};

// 存储数据
request.onsuccess = function (event) {
  var db = event.target.result;
  var transaction = db.transaction(["users"], "readwrite");
  var objectStore = transaction.objectStore("users");
  var user = { id: 1, name: "张三", age: 20 };
  var request = objectStore.add(user);
  request.onsuccess = function (event) {
    console.log("数据存储成功");
  };
};

// 读取数据
request.onsuccess = function (event) {
  var db = event.target.result;
  var transaction = db.transaction(["users"], "readonly");
  var objectStore = transaction.objectStore("users");
  var index = objectStore.index("name");
  var request = index.get("张三");
  request.onsuccess = function (event) {
    var user = event.target.result;
    console.log(user);
  };
};

#373 Git Hooks

2020-02-24

https://mp.weixin.qq.com/s/67qBDteTmHROeMwOBUeyaw
如何通过 Git 和 Husky 添加提交钩子并实现代码任务自动化

钩子 时机 用途
pre-commit 提交之前 代码检查
prepare-commit-msg 提交信息生成之前 生成提交信息
commit-msg 提交信息保存之前 检验提交信息
post-commit 提交之后 通知,自动测试,CI 等
pre-push push 之前 代码检查,测试,编译打包
applypatch-msg 生成补丁时 验证补丁信息
fsmonitor-watchman 文件系统监视器发现变化时 触发版本控制操作
pre-applypatch 应用补丁之前 验证补丁信息
pre-merge-commit 合并之前 检查将要合并的分支是否符合要求
pre-rebase rebase 操作之前 -
push-to-checkout - -
pre-receive 接受提交之前 代码检查,校验权限
post-receive 接受提交之后 通知,自动测试,CI 等
update 更新操作之前(分支、Tag) 提供从旧版本到新版本的改动列表供用户审核
post-update 更新操作之后(分支、Tag) 通知,自动测试,CI 等

示例

pre-receive

#!/usr/bin/env python

"""
每个人都只能提交代码到 username-date-branchName
username 是 git 用户名
date 是 mmdd 日期
branch 是分支描述,支持小写字母、数字、横杠,2 到 16 个字符
"""

import re
import subprocess
import sys

# 获取提交者的用户名
author = subprocess.check_output(['git', 'config', 'user.name']).decode().strip()

# 获取提交的分支名称
branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).decode().strip()

# 定义分支名称的正则表达式
branch_pattern = r'^%s-\d{4}-[a-z0-9\-]{2,}$' % (author,)

# 检查分支名称是否符合正则表达式
if not re.match(branch_pattern, branch):
    print('Error: Branch name "{}" does not match the required pattern "{}"'.format(branch, branch_pattern), file=sys.stderr)
    sys.exit(1)

# 解析日期并检查其是否合法
try:
    _, date_str, _ = branch.split('-')
    month, day = int(date_str[:2]), int(date_str[2:])
    if month < 1 or month > 12 or day < 1 or day > 31:
        raise ValueError
except ValueError:
    print('Error: Invalid date format in branch name "{}"'.format(branch), file=sys.stderr)
    sys.exit(1)

#371 邮件安全:SPF

2020-02-11

简介

SMTP 会话中,发行方会通过 MAIL FROM 命令告诉收信方,这封邮件是谁发出的。

但是,由于邮件系统的设计上没有考虑如何对垃圾邮件进行有效防范,收信方需要一个机制来判断这封邮件的来源是否可靠。因此,人们提出了 SPF 方案:

  1. 每个邮件服务都需要为自己的发信域名在 DNS 中配置一下 SPF 记录(TXT 类型),记录值是出信 IP。
  2. 收信方拿到 MAIL FROM 地址之后,使用发信方 IP 地址和发信域 DNS 中记录的 IP 地址做一个比对。如果匹配,说明来源正确,否则,这封邮件可以被视为垃圾邮件,直接返回投递失败(硬退)。

该方案 2006 年提交到 Network Working Group 成为草案,然后 2014 年成为建议标准

注意:SPF 只是验证了邮件确实是从发信域所在网络投递出来的,但是不能防止邮件伪造和欺诈。应该结合其他技术,如 DKIM(DomainKeys Identified Mail)和 DMARC(Domain-based Message Authentication, Reporting, and Conformance),一起增强电子邮件的安全性。

SPF 记录示例

dig +short txt qq.com
"v=spf1 include:spf.mail.qq.com -all"

dig +short txt spf.mail.qq.com
"v=spf1 include:qq-a.mail.qq.com include:qq-b.mail.qq.com include:qq-c.mail.qq.com include:biz-a.mail.qq.com include:biz-b.mail.qq.com include:biz-c.mail.qq.com include:biz-d.mail.qq.com -all"

dig +short txt qq-{a..c}.mail.qq.com biz-{a..d}.mail.qq.com
"v=spf1 ip4:101.226.139.0/25 ip4:101.91.43.0/25 ip4:101.91.44.128/25 ip4:112.64.237.128/25 ip4:116.128.173.0/25 ip4:121.51.40.128/25 ip4:121.51.6.0/25 ip4:162.62.52.214 ip4:162.62.55.67 ip4:162.62.57.0/24 ip4:162.62.58.211 ip4:162.62.58.216 -all"
"v=spf1 ip4:162.62.58.69 ip4:162.62.63.194 ip4:180.163.24.128/25 ip4:183.2.187.0/25 ip4:203.205.221.128/25 ip4:203.205.251.0/25 ip4:210.51.43.0/25 ip4:58.246.222.128/25 ip4:58.250.143.128/25 ip4:61.241.55.128/25 -all"
"v=spf1 ip4:113.108.92.0/25 ip4:121.14.77.0/25 ip4:81.69.217.16/28 ip4:54.164.151.162 -all"
"v=spf1 ip4:114.132.122.39 ip4:114.132.123.192 ip4:114.132.124.171 ip4:114.132.125.233 ip4:114.132.197.227 ip4:114.132.224.180 ip4:114.132.233.22 ip4:114.132.58.0/24 ip4:43.155.65.254 ip4:114.132.62.0/24 ip4:106.55.200.77 -all"
"v=spf1 ip4:114.132.63.24 ip4:114.132.64.0/26 ip4:114.132.65.219 ip4:43.155.67.158 ip4:114.132.67.179 ip4:114.132.73.137 ip4:114.132.74.132 ip4:43.154.209.5 ip4:43.154.197.177 ip4:43.154.155.102 ip4:43.155.80.173 ip4:43.154.221.58 -all"
"v=spf1 ip4:54.204.34.129 ip4:54.204.34.130 ip4:54.243.244.52 ip4:52.205.10.60 ip4:35.173.142.173 ip4:54.207.22.56 ip4:54.207.19.206 ip4:54.254.200.92 ip4:54.254.200.128 ip4:54.92.39.34 ip4:54.206.16.166 ip4:54.206.34.216 ip4:114.132.75.215 -all"
"v=spf1 ip4:52.59.177.22 ip4:18.194.254.142 ip4:18.132.163.193 ip4:18.169.211.239 ip4:13.245.186.79 ip4:13.245.218.24 ip4:15.184.224.54 ip4:15.184.82.18 ip4:114.132.76.87 ip4:114.132.77.159 ip4:114.132.78.196 ip4:114.132.79.153 ip4:43.154.54.12 -all"

语法

  1. "v=spf1":指定 SPF 记录的版本,目前只有 SPFv1 版本。

  2. 机制(Mechanisms):SPF 使用机制来指定哪些服务器被授权发送邮件。常用的机制有:

    • "all":定义了所有情况下的默认结果。可以是 "+all"(通过验证)或 "-all"(不通过验证)。例如,"-all" 表示除非匹配其他通过机制,否则所有情况都不通过验证,即严格拒绝伪造邮件。
    • "ip4" 和 "ip6":指定允许发送邮件的 IPv4 和 IPv6 地址。例如:"ip4:192.0.2.1" 表示允许来自 192.0.2.1 的服务器发送邮件。
    • "a" 和 "mx":允许域名的 A 记录和 MX 记录指定的主机发送邮件。
    • "include":允许引用其他域名的 SPF 记录。例如:"include:example.com" 表示允许按照 example.com 的 SPF 记录来验证。
  3. 修饰符(Modifiers):修饰符可以在机制之后指定,用于调整验证的结果。常用的修饰符有:

    • "+":显示通过验证。
    • "-":显示不通过验证,相当于 "-all"。
    • "~":软失败,邮件可能被接受,但会标记为不可信。
    • "?":中性结果,邮件可能被接受,但不影响验证的结果。

SPF 记录示例:

v=spf1 ip4:192.0.2.1 a mx ~all

以上示例表示允许 IP 地址为 192.0.2.1 的服务器、A 记录和 MX 记录指定的主机发送邮件,但验证结果为软失败,邮件可能会被接受,但标记为不可信。

DNS SPF 类型

IANA 设计了一种专门的 DNS 类型用来记录 SPF 信息,但是采用率非常低。
可以忽略,继续使用 TXT 类型来保存 SPF 信息。

参考资料与拓展阅读

  • Sender Policy Framework
  • RFC4408, Experimental / 2006, Sender Policy Framework (SPF) for Authorizing Use of Domains in E-Mail, Version 1
  • RFC7208, Standards Track / 2014, Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1
  • http://www.open-spf.org/Tools/
  • Golang 库:https://github.com/mileusna/spf

    package main
    
    import (
       "net"
    
       "github.com/mileusna/spf"
    )
    
    func main() {
       // optional, set DNS server which will be used by resolver.
       // Default is Google's 8.8.8.8:53
       spf.DNSServer = "1.1.1.1:53"
    
       ip := net.ParseIP("123.123.123.123")
       r := spf.CheckHost(ip, "domain.com", "name@domain.com", "");
       // returns spf check result
       // "PASS" / "FAIL" / "SOFTFAIL" / "NEUTRAL" / "NONE" / "TEMPERROR" / "PERMERROR"
    
       // if you only need to retrive SPF record as string from DNS
       spfRecord, _ := spf.LookupSPF("domain.com")
    }
    
  • Golang 库:https://github.com/asggo/spf

    package main
    
    import "github.com/asggo/spf"
    
    func main() {
    
          SMTPClientIP := "1.1.1.1"
          envelopeFrom := "info@example.com"
    
          result, err := spf.SPFTest(SMTPClientIP, envelopeFrom)
          if err != nil {
                   panic(err)
          }
    
          switch result {
          case spf.Pass:
                   // allow action
          case spf.Fail:
                   // deny action
          }
       //...
    }
    

#370 邮件安全:DKIM

2020-02-10

DomainKeys Identified Mail,域名密钥识别邮件
作用是使用非对称加密(公钥 + 私钥)对邮件内容进行签名,防止伪造和篡改。

历史

  • 2004 年,雅虎 DomainKeys 和思科 Identified Internet Mail 合并为 DKIM。
  • 2007 年 2 月,DKIM 被列入互联网工程工作小组(IETF)的标准提案(Proposed Standard),并于同年 5 月成为正式标准(Standards Track)。

示例

DKIM-Signature: v=1; a=rsa-sha256; d=example.net; s=brisbane;
     c=relaxed/simple; q=dns/txt; i=foo@eng.example.net;
     t=1117574938; x=1118006938; l=200;
     h=from:to:subject:date:keywords:keywords;
     z=From:foo@eng.example.net|To:joe@example.com|
       Subject:demo=20run|Date:July=205,=202005=203:44:08=20PM=20-0700;
     bh=MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=;
     b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZ
              VoG4ZHRNiYzR
Tag Required Meanning
v version
a signing algorithm
d Signing Domain Identifier (SDID)
s selector
c - canonicalization algorithm(s) for header and body
q - default query method
i - Agent or User Identifier (AUID)
t recommended signature timestamp
x recommended expire time
l - body length
h header fields - list of those that have been signed
z - header fields - copy of selected header fields and values
bh body hash
b signature of headers and body

示例 2

邮件中的 DKIM-Signature 头:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.instagram.com;
    s=s1024-2013-q3; t=1674163477;
    bh=6RXa/HYJQNKpB5PIGtLn7v1NE/4T5FaqxBLWNHVRZu8=;
    h=Date:To:Subject:From:MIME-Version:Content-Type;
    b=VAY3x16QtXeH1rQxu6eEbzhfgZl69m1sG9XzN3ym4FbWiMg+K+IfMGF4yszGYk8yO
     YXAAJZuQfG45pjthISDDSwhhBK0WGgufQ8ofnzhNUN9WT/okEATC+JfzksS9w2Ts4V
     ALa/4HHXnikQV5AFNJJNJIvWMN/fJ5c49nLkW024=

域名中的 DKIM 公钥:

dig TXT +short s1024-2013-q3._domainkey.mail.instagram.com
"k=rsa; t=s; h=sha256; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7twdVo+BW8Pv2poU5129KYmE6npHdxUU8fktUKTE9TNovCvLy5LVjYc3TQcUFjOH" "VaZ89ZCjmpAcrA2QnTEKZ/2QWV56gn6bWdFW4SFxnQdHjguBZQykfKe5KTxy2a/OxuA0x2dHfdnYfw7RVzr4uednpKcWJy4Rl3gM6XB1zDwIDAQAB"

Python 编程中的应用

准备工作

  1. selector 选择 s2020 (随便)
  2. 生成密钥对
# 方法一:使用 OpenDKIM
sudo apt install -y opendkim-tools
opendkim-genkey --help
opendkim-genkey -D . -d markjour.com -s s2020

# 方法二:使用 OpenSSL
openssl genrsa -out dkim-markjour-s2020.pem 2048
openssl rsa -in dkim-markjour-s2020.pem -pubout -outform der 2>/dev/null | openssl base64 -A > dkim-markjour-s2020.pub
  1. 配置 DNS

s2020._domainkey.markjour.com

"v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjnfmXLuyBt0Tus/Bdr87GpLqcRCZX7UC61OiPZ1Y3MG42qBcdEAMJfu7qop7KOLL8cywTRxiX39ehmf0ZovAXH1KkijiX/16tkI3cO9T6KS4vyr0Ip3fsGgNgjn5rH3M5AZAmbym6DIzYrtpTiAKgLYmFLALd9SLi/OhFIltWK+QJhaJgcuWUXCzlry01Fdsv1qj28WdZ6PQbQrSffc1qzkvOEOlmZXwWjQfg5X4E3DR4WKenC6f5WdcJeXk4pUeBOdQDoEM+4uCk4S6cN3OuYEQbvVmfQ5RAlCODccx7lJemWZZnlIf+03FppUEEMENZ8tu3iixD24m2q9wDLDL5QIDAQAB
  • v 版本,只有一种选择:DKIM1,必须放最前面,可以忽略
  • h 哈希算法,sha1 或者 sha256
  • k 密钥类型,只有一种选择:rsa,可以忽略
  • n 注释,可以忽略
  • p 公钥(ASN.1 DER-encoded + Base64)
  • s 服务类型,默认 *,表示所有服务,可以忽略
  • t 逗号隔开的标记
    • y 测试
    • s DKIM 签名中的 i= 域名必须是 d= 域名完全相同(子域也不行)。

参考:

签名

headers = {'aaa': '123', 'bbb': '456', 'ccc': '789'}

校验签名

mail = """
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qq.com; s=s0907;
    t=1331524836; bh=Pqr4lbxMcef/3IqsXx/edT0iwPe18N7n8qKmQSnLio8=;
    h=X-QQ-SSF:X-QQ-Spam:X-QQ-BUSINESS-ORIGIN:X-Originating-IP:
     X-QQ-STYLE:X-QQ-mid:From:To:Sender:Subject:Mime-Version:
     Content-Type:Content-Transfer-Encoding:Date:X-Priority:Message-ID:
     X-QQ-MIME:X-Mailer:X-QQ-Mailer:X-QQ-ReplyHash;
    b=hF3hXt429Mp9WUJx9wQQYYk32EABCQST/OmV+dI+vJ/XIidVkc6fsh8l/vBz/optb
     MDp0XIupHHkUozz6jwMryhHd/ZNjLNtBBAIOgl1wH7R016x8uTtDQink5uIPH+5
X-QQ-SSF: 0000000000010060
X-QQ-Spam: true
X-QQ-BUSINESS-ORIGIN: 2
X-Originating-IP: 61.151.148.196
X-QQ-STYLE:
X-QQ-mid: bizmail6t1331524835t2734595
From: "=?gb18030?B?wffE6g==?=" <thinkphp@qq.com>
To: "=?gb18030?B?uvqwug==?=" <ninedoors@126.com>
Sender: liuchen@topthink.net
Subject: =?gb18030?B?UmU60rvOu1RQZXK1xNLJu/M=?=
Mime-Version: 1.0
Content-Type: multipart/alternative;
    boundary="----=_NextPart_4F5D74E3_DF6406C0_249C5176"
Content-Transfer-Encoding: 8Bit
Date: Mon, 12 Mar 2012 12:00:35 +0800
X-Priority: 3
Message-ID: <tencent_519100472CAA258E750FA58D@qq.com>
X-QQ-MIME: TCMime 1.0 by Tencent
X-Mailer: QQMail 2.x
X-QQ-Mailer: QQMail 2.x
X-QQ-ReplyHash: 3949397671
""".strip('\n')

#368 PyCryptodome

2020-02-05

和 PyCrypto 的关系

PyCrypto 是 Python 界最知名的加密模块,它提供了一系列的加密算法,包括对称加密、非对称加密、哈希算法、签名算法等。
不过有一个很大的问题:上一个版本 2.6.1 发布于 2013-10-18,已经很多年没有维护了。

PyCryptodome 是 PyCrypto 的分叉,该项目在统一套代码的基础上提供了两种包:pycryptodomepycryptodomex

  1. 前者保持对 PyCrypto 的兼容,所有的代码都在 Crypto 名称下,
  2. 后者丢掉了历史包袱,放弃对 PyCrypto 的兼容,所有代码都在 Cryptodome 名称下。

  3. https://pypi.org/project/pycryptodome/

  4. https://pypi.org/project/pycryptodomex/
  5. https://www.pycryptodome.org/en/latest/
  6. https://github.com/Legrandin/pycryptodome/

其他加密模块

示例