
Security News
The Code You Didn't Write Is Still Yours to Defend
AI agents are pulling packages into environments no scanner is watching, creating exposure before security teams can see it.
mima-kit is a cryptographic suite implemented in TypeScript. The goal is to provide an easy-to-use cryptographic library. mima-kit 是一个使用 TypeScript 实现的密码学套件。目标是提供一个简单易用的密码学库。
mima-kit 是一个使用 TypeScript 实现的密码学套件。目标是提供一个简单易用的密码学库。mima-kit 尚处于早期开发阶段,API 可能会发生变化。
在线使用: https://rsoram.github.io/mima-live/
npm install mima-kit
▶ 字符编码
UTF8 UTF-8 编码HEX 十六进制编码B64 Base64 编码B64URL Base64URL 编码密码学中的数据通常是二进制数据,在 JS 中通常以 Uint8Array 表示,string 和 Uint8Array 的转换需要 字符编码。
如果您使用
Node.js这类支持Buffer的环境,那么您可以直接使用Buffer进行编解码。如果您使用的是浏览器环境,就可以使用mima-kit提供的解码器。
mima-kit 提供的编解码器会自动判断输入数据的类型。
Uint8Array 类型的数据,会将其转换为 stringstring 类型的数据,会将其转换为 Uint8Array// convert utf-8 string to Uint8Array
const e = UTF8('mima-kit')
// convert Uint8Array to utf-8 string
const d = UTF8(e)
console.log(d) // 'mima-kit'
interface Codec {
/** Parse encoded string to Uint8Array */
(input: string): U8
/** Stringify Uint8Array to encoded string */
(input: Uint8Array): string
FORMAT: string
}
在上述代码中,您可能留意到了 U8 类型。mima-kit 中绝大多数函数都会返回 U8 类型,她是 Uint8Array 的子类,旨在提供一些额外的方法。绝大多数情况下,您可以放心地将 U8 类型传递给其他使用 Uint8Array 的函数。
// Parse encoded string to U8
U8.fromSting('6D696D612D6B6974', HEX)
// Stringify U8 to encoded string
U8.fromSting('6D696D612D6B6974', HEX)
.to(UTF8) // 'mima-kit'
// Convert BigInt to U8
U8.fromBI(0x12345678n) // [0x12, 0x34, 0x56, 0x78]
// Convert U8 to BigInt
U8.fromBI(0x12345678n)
.toBI() // 305419896n (0x12345678n)
散列算法 是一种将任意长度的数据映射为固定长度数据的算法。该定义非常宽泛,但在密码学中,通常讨论的是 加密散列算法。带密钥的加密散列算法 会额外使用一个密钥产生更安全的散列值。
Specification: GM/T 0004-2012
const m = UTF8('mima-kit')
sm3(m).to(HEX)
Specification: RFC 1321
const m = UTF8('mima-kit')
md5(m).to(HEX)
Specification: FIPS PUB 180-4
const m = UTF8('mima-kit')
sha1(m).to(HEX)
Specification: FIPS PUB 180-4
const m = UTF8('mima-kit')
sha224(m).to(HEX)
sha256(m).to(HEX)
sha384(m).to(HEX)
sha512(m).to(HEX)
const sha512_224 = sha512t(224)
sha512_224(m).to(HEX)
Specification: FIPS PUB 202
const m = UTF8('mima-kit')
sha3_224(m).to(HEX)
sha3_256(m).to(HEX)
sha3_384(m).to(HEX)
sha3_512(m).to(HEX)
shake128(256)(m).to(HEX)
shake256(512)(m).to(HEX)
Specification: NIST SP 800-185
// optional function name
const n = UTF8('name')
// optional customization string
const s = UTF8('custom')
const m = UTF8('mima-kit')
cshake128(256, n, s)(m).to(HEX)
cshake256(512, n, s)(m).to(HEX)
Specification: NIST SP 800-185
// optional customization string
const s = UTF8('custom')
const m = ['mima', '-', 'kit'].map(v => UTF8(v))
tuplehash128(256, s)(m).to(HEX)
tuplehash256(512, s)(m).to(HEX)
tuplehash128XOF(256, s)(m).to(HEX)
tuplehash256XOF(512, s)(m).to(HEX)
Specification: NIST SP 800-185
注意:
mima-kit提供的ParallelHash算法并不能真正并行计算,只是将输入分块后分别计算,最后将结果拼接。
// optional customization string
const s = UTF8('custom')
const m = UTF8('mima-kit')
const blockSize = 1024
parallelhash128(blockSize, 256, s)(m).to(HEX)
parallelhash256(blockSize, 512, s)(m).to(HEX)
parallelhash128XOF(blockSize, 256, s)(m).to(HEX)
parallelhash256XOF(blockSize, 512, s)(m).to(HEX)
Specification: TurboSHAKE
// optional Domain Separator
// range: 0x01 ~ 0x7F, default: 0x1F
const D = 0x0B
const m = UTF8('mima-kit')
turboshake128(256, D)(m).to(HEX)
turboshake256(512, D)(m).to(HEX)
Specification: KangarooTwelve
// optional customization string
const s = UTF8('custom')
const m = UTF8('mima-kit')
kt128(256, s)(m).to(HEX)
kt256(512, s)(m).to(HEX)
Specification: RFC 2104
密钥长度的参数
k_size默认使用散列算法的DIGEST_SIZE。该参数不会影响函数的结果,但会被其他函数使用,例如ECIES。
const key = UTF8('password')
const m = UTF8('mima-kit')
// HMAC-SM3
hmac(sm3)(key, m).to(HEX)
// HMAC-SHA1-80 with 80-bit digest and 160-bit key
hmac(sha1, 80)(key, m).to(HEX)
// HMAC-SHA1-160 with 160-bit digest and 80-bit key
hmac(sha1, 160, 80)(key, m).to(HEX)
Specification: NIST SP 800-185
// customization string
const s = UTF8('custom')
const key = UTF8('password')
const m = UTF8('mima-kit')
kmac128(256, s)(key, m).to(HEX)
kmac256(512, s)(key, m).to(HEX)
kmac128XOF(256, s)(key, m).to(HEX)
kmac256XOF(512, s)(key, m).to(HEX)
如果您已经实现了一个伟大而又神秘的散列算法,您可以使用 createHash 函数将其包装成一个可被调用的 Hash 对象。然后您就可以像使用其他 加密散列算法 一样,将您的算法和 mima-kit 中其他高级算法一起使用。
如果您熟悉
JS,您会发现createHash的本质不过是Object.assign的包装。您完全可以用Object.assign替代createHash,但createHash会为您提供一些类型提示,避免发生恼人的拼写错误。
const _greatHash: Digest = (M: Uint8Array) => new U8(M)
const greatHashDescription: HashDescription = {
ALGORITHM: 'GreatHash',
BLOCK_SIZE: 64,
DIGEST_SIZE: 64,
}
const greatHash = createHash(_greatHash, greatHashDescription)
// HMAC-GreatHash
const hmac_gh = hmac(greatHash)
interface Digest {
(M: Uint8Array): U8
}
interface HashDescription {
/** Algorithm name */
ALGORITHM: string
/** Block size (byte) */
BLOCK_SIZE: number
/** Digest size (byte) */
DIGEST_SIZE: number
OID?: string
}
对称密钥算法 是一种使用相同密钥进行 加密 和 解密 的加密算法。它可以分为 分组密码算法 和 流密码算法。分组密码算法 通常需要组合 填充模式 和 工作模式 一起使用。分组密码算法 可以通过特定的 工作模式 和 NO_PAD 转换为 流密码算法。
你可以在
/test/cipher.test.ts中找到更多使用示例。
const k = HEX('')
const iv = HEX('')
const p = UTF8('mima-kit')
// using SM4-CBC
const cbc_sm4 = cbc(sm4)(k, iv)
const c = cbc_sm4.encrypt(p)
const m = cbc_sm4.decrypt(c)
m.to(UTF8) // 'mima-kit'
// using SM4-CTR in stream mode
const ctr_sm4 = ctr(sm4, NO_PAD)(k, iv)
const c = ctr_sm4.encrypt(p)
const m = ctr_sm4.decrypt(c)
m.to(UTF8) // 'mima-kit'
单独使用 分组密码算法 没有太大的意义,因为它只能对单个数据块进行加解密。
Specification: GM/T 0002-2012
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
sm4(k).encrypt(m) // c
sm4(k).decrypt(c) // m
Specification: FIPS PUB 197
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
aes(128)(k).encrypt(m) // c
aes(128)(k).decrypt(x) // m
aes(192)(k).encrypt(m) // c
aes(192)(k).decrypt(c) // m
aes(256)(k).encrypt(m) // c
aes(256)(k).decrypt(c) // m
Specification: RFC 5794
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
aria(128)(k).encrypt(m) // c
aria(128)(k).decrypt(c) // m
aria(192)(k).encrypt(m) // c
aria(192)(k).decrypt(c) // m
aria(256)(k).encrypt(m) // c
aria(256)(k).decrypt(c) // m
Specification: RFC 3713
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
camellia(128)(k).encrypt(m) // c
camellia(128)(k).decrypt(c) // m
camellia(192)(k).encrypt(m) // c
camellia(192)(k).decrypt(c) // m
camellia(256)(k).encrypt(m) // c
camellia(256)(k).decrypt(c) // m
Specification: FIPS PUB 46-3
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
des(k).encrypt(m) // c
des(k).decrypt(c) // m
Specification: FIPS PUB 46-3
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
t_des(128)(k).encrypt(m) // c
t_des(128)(k).decrypt(c) // m
t_des(192)(k).encrypt(m) // c
t_des(192)(k).decrypt(c) // m
Specification: ARC5
ARC5 算法是一个参数化的算法,可以接受长度为 0 < k.byteLength < 256 的密钥。参数化后算法标记为 ARC5-w/r,其中 w 是工作字的比特长度,r 是轮数。
// 推荐的参数化配置
// +-----+----+
// | w | r |
// +-----+----+
// | 8 | 8 |
// | 16 | 12 |
// | 32 | 16 | (default)
// | 64 | 20 |
// | 128 | 24 |
// +-----+----+
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
const spec8 = arc5(8, 8) // ARC5-8/8
const spec16 = arc5(16, 12) // ARC5-16/12
const spec32 = arc5(32, 16) // ARC5-32/16 (default)
const spec64 = arc5(64, 20) // ARC5-64/20
const spec128 = arc5(128, 24) // ARC5-128/24
spec32(k).encrypt(m) // c
spec32(k).decrypt(c) // m
Specification: Blowfish
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
blowfish(k).encrypt(m) // c
blowfish(k).decrypt(c) // m
Specification: Twofish
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
twofish(128)(k).encrypt(m) // c
twofish(128)(k).decrypt(x) // m
twofish(192)(k).encrypt(m) // c
twofish(192)(k).decrypt(c) // m
twofish(256)(k).encrypt(m) // c
twofish(256)(k).decrypt(c) // m
Specification: TEA
向 TEA 算法传递一个代表 轮数 的参数。TEA 算法的 轮数 可以是任意正整数,默认使用 32。
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
tea(32)(k).encrypt(m) // c
tea(32)(k).decrypt(c) // m
Specification: XTEA
向 XTEA 算法传递一个代表 轮数 的参数。XTEA 算法的 轮数 可以是任意正整数,默认使用 32。
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
xtea(32)(k).encrypt(m) // c
xtea(32)(k).decrypt(c) // m
PKCS7_PAD PKCS#7 填充模式X923_PAD ANSI X9.23 填充模式ISO7816_PAD ISO/IEC 7816-4 填充模式ZERO_PAD 零填充模式NO_PAD 无填充模式单独使用 填充模式 没有太大的意义,因为它只是对数据进行填充或者去填充。
let block_size: number
let m = new Uint8Array()
let p = new Uint8Array()
// add padding
p = PKCS7_PAD(m, block_size)
// remove padding
m = PKCS7_PAD(p)
interface Padding {
/**
* add padding
* @param {Uint8Array} M - Message
* @param {number} BLOCK_SIZE - Block size
*/
(M: Uint8Array, BLOCK_SIZE: number): U8
/**
* remove padding
* @param {Uint8Array} P - Padded message
*/
(P: Uint8Array): U8
ALGORITHM: string
}
const cbc_sm4 = cbc(sm4, PKCS7_PAD)
const cbc_sm4 = cbc(sm4, X923_PAD)
const cbc_sm4 = cbc(sm4, ISO7816_PAD)
const cbc_sm4 = cbc(sm4, ZERO_PAD)
NO_PAD模式不会对数据进行填充,这一模式仅用于将分组密码算法转换为流密码算法。
// run SM4-OFB in stream mode
const ofb_sm4 = ofb(sm4, NO_PAD)
ecb Electronic Codebookcbc Cipher Block Chainingpcbc Progressive Chaining Block Ciphercfb Cipher Feedbackofb Output Feedbackctr Counter Modegcm Galois/Counter Modemima-kit 将 工作模式 与 分组密码算法 完全解偶,这意味着您可以将任意 分组密码算法 与任意 工作模式 结合使用。
Electronic Codebook (ECB) 是最简单的工作模式。ECB 模式将明文分成固定长度的数据块,然后对每个数据块进行加密。
ECB 模式不需要 iv。ECB 模式传递的 iv 参数会被忽略。const k = HEX('')
const m = HEX('')
const c = HEX('')
const CIPHER = ecb(sm4)(k)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
Cipher Block Chaining (CBC) 是最常用的工作模式。CBC 模式每个明文块都会与前一个密文块进行异或操作,然后再进行加密。
CBC 模式需要 iv。iv 的长度与加密算法的 BLOCK_SIZE 相同。const k = HEX('')
const iv = HEX('')
const m = HEX('')
const c = HEX('')
const CIPHER = cbc(sm4)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
Progressive Chaining Block Cipher (PCBC) 是 CBC 的变种。PCBC 模式每个明文块都会与前一个明文和前一个密文块进行异或操作,然后再进行加密。PCBC 模式旨在将密文中的微小变化在加解密时无限传播。
PCBC 模式需要 iv。iv 的长度与加密算法的 BLOCK_SIZE 相同。const k = HEX('')
const iv = HEX('')
const m = HEX('')
const c = HEX('')
const CIPHER = pcbc(sm4)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
Cipher Feedback (CFB) 将分组密码转换为流密码。CFB 模式通过加密前一个密文块获得加密数据流,然后与明文块进行异或操作,获得密文块。
CFB 模式需要 iv。iv 的长度与加密算法的 BLOCK_SIZE 相同。CFB 可以通过 NO_PAD 转换为 流密码算法。const k = HEX('')
const iv = HEX('')
const m = HEX('')
const c = HEX('')
const CIPHER = cfb(sm4)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
Output Feedback (OFB) 将分组密码转换为流密码。OFB 模式通过加密 iv 获得加密数据流,然后与明文块进行异或操作,获得密文块。
OFB 模式需要 iv。iv 的长度与加密算法的 BLOCK_SIZE 相同。OFB 可以通过 NO_PAD 转换为 流密码算法。const k = HEX('')
const iv = HEX('')
const m = HEX('')
const c = HEX('')
const CIPHER = ofb(sm4)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
Counter Mode (CTR) 将分组密码转换为流密码。CTR 模式将 iv 与计数器组合以生成唯一的 计数器块,通过加密 计数器块 获得加密数据流,然后与明文块进行异或操作,获得密文块。
CTR 模式需要 iv。iv 的长度与加密算法的 BLOCK_SIZE 相同。CTR 可以通过 NO_PAD 转换为 流密码算法。const k = HEX('')
const iv = HEX('')
const m = HEX('')
const c = HEX('')
const CIPHER = ctr(sm4)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
Galois/Counter Mode (GCM) 将分组密码转换为流密码。GCM 模式可以看作是 CTR 模式的变种,它在 CTR 模式的基础上增加了 认证 功能。
GCM 模式需要 iv。iv 的长度没有限制,但推荐使用 96 位长度的 iv。GCM 可以通过 NO_PAD 转换为 流密码算法。AUTH_TAG 长度由 AUTH_TAG_SIZE 参数决定。AUTH_TAG 最大长度为 128 位,设置任意长度都不会影响程序的运行,但一般推荐使用 128、120、112、104、96 位长度,对于某些应用也可以使用 64、32 位长度。mima-kit 实现的 GCM 模式并没有进行查表优化,因此性能可能会比较慢。
const k = HEX('')
const iv = HEX('')
const m = HEX('')
const a = HEX('')
const c = HEX('')
const t = HEX('')
const CIPHER = gcm(aes(128))(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
CIPHER.sign(c, a) // auth tag
CIPHER.verify(t, c, a) // true or false
通常 流密码算法 不需要复杂的配置,一般只需要 key 和 iv。
const k = HEX('')
const iv = HEX('')
const cipher = salsa20(k, iv)
const p = UTF8('mima-kit')
const c = cipher.encrypt(p)
const m = cipher.decrypt(c)
// p === m
ZUC 是 3GPP 规范中的流密码算法,它包含机密性算法 128-EEA3 和完整性算法 128-EIA3。由于 ZUC 算法主要用于移动通信,所以函数接口和其他流密码算法有所不同。
参考 /test/cipher.test.ts 以获取更多使用示例。
const k = new Uint8Array(16)
const m = new Uint8Array(4)
const c = new Uint8Array([0x27, 0xBE, 0xDE, 0x74])
const mac = new Uint8Array([0xC8, 0xA9, 0x59, 0x5E])
const params: ZUCParams = {
KEY: k,
M: m,
COUNTER: 0,
BEARER: 0,
DIRECTION: 0,
LENGTH: 1,
}
// 128-EEA3 加密消息
eea3(params) // c
// 128-EIA3 计算消息认证码
eia3(params) // mac
// 128-EEA3 解密消息
params.M = c
eea3(params) // m
Specification: ARC4
ARC4 算法可以接受长度为 0 < k.byteLength < 256 的密钥,同时 ARC4 算法不需要 iv。
const k = HEX('')
const cipher = arc4(k)
const c = cipher.encrypt(UTF8('mima-kit'))
const m = cipher.decrypt(c)
Specification: Salsa20
Salsa20 算法可以接受长度为 16 或 32 字节的密钥和 8 字节的 iv。
const k = HEX('')
const iv = HEX('')
const cipher = salsa20(k, iv)
const c = cipher.encrypt(UTF8('mima-kit'))
const m = cipher.decrypt(c)
Specification: Rabbit
Rabbit 算法可以接受长度为 16 字节的密钥。对于 iv,Rabbit 算法可以接受长度为 0 或 8 字节的 iv。当 iv 长度为 0 字节时,Rabbit 算法会跳过 iv Setup 步骤。
const p = UTF8('mima-kit')
const k = HEX('')
const iv = new Uint8Array(8)
const cipher = rabbit(k, iv)
const c = cipher.encrypt(p)
const m = cipher.decrypt(c)
// skip iv setup
const cipher = rabbit(k, new Uint8Array(0))
const c = cipher.encrypt(p)
const m = cipher.decrypt(c)
与 包装您的加密散列算法 一样,您可以使用 createCipher 函数将您的 对称密钥算法 包装成一个可被调用的 Cipher 对象。然后您就可以像使用其他 对称密钥算法 一样,将您的算法和 mima-kit 中其他高级算法一起使用。
如果您熟悉
JS,您会发现createCipher的本质不过是Object.assign的包装。您完全可以用Object.assign替代createCipher,但createCipher会为您提供一些类型提示,避免发生恼人的拼写错误。
const _greatCipher: Cipher = (k: Uint8Array) => {
const cipher = {
encrypt: (M: Uint8Array) => new U8(k.map((v, i) => v ^ M[i])),
decrypt: (C: Uint8Array) => new U8(k.map((v, i) => v ^ C[i])),
}
return cipher
}
const greatCipherDescription: BlockCipherInfo = {
ALGORITHM: 'GreatCipher',
BLOCK_SIZE: 16,
KEY_SIZE: 16,
MIN_KEY_SIZE: 16,
MAX_KEY_SIZE: 16,
}
const greatCipher = createCipher(_greatCipher, greatCipherDescription)
// GCM-GreatCipher
const gcm_gc = gcm(greatCipher)
interface Cipher {
(k: Uint8Array): Cipherable
}
interface Cipherable {
encrypt: (M: Uint8Array) => U8
decrypt: (C: Uint8Array) => U8
}
interface BlockCipherInfo {
ALGORITHM: string
/** Block size (byte) */
BLOCK_SIZE: number
/** Recommended key size (byte) */
KEY_SIZE: number
/** Minimum key size (byte) */
MIN_KEY_SIZE: number
/** Maximum key size (byte) */
MAX_KEY_SIZE: number
}
非对称密钥算法是一种使用不同密钥进行加密和解密的加密算法。非对称密钥算法通常包含 公钥 和 私钥,公钥 用于加密,私钥 用于解密。
mima-kit不支持也不打算支持ASN.1编码。如果您真的需要将密钥对导出为ASN.1编码,您可以使用asn1js这个库。在
Node.js环境中,mima-kit使用本机crypto模块产生素数。而在浏览器环境中,mima-kit使用Miller-Rabin算法产生素数。
Specification: RFC 8017
RSA 算法是一种基于大素数分解的非对称加密算法。mima-kit 提供的 RSA 算法支持大于 256 位的密钥。因为 mima-kit 内部实现的大数运算相关的函数在处理太小的数字时可能会产生错误的结果。且我并没有测试过小于 256 位的密钥,所以我无法保证小于 256 位的密钥是否能正常工作。
我想这个世界上应该没有人会使用这么小的密钥吧...
在 PKCS#1 中规定了 RSA 算法的 密码学原语,这些原语是实现规范中其他高级方案的基础。当传入 number 时,rsa 会生成一个带有 原语 能力的 RSA 密钥对。当传入 RSAPrivateKey 或 RSAPublicKey 时,会使用传入的对象作为密钥提供 原语 能力。
需要注意的是,
原语的encrypt,decrypt,sign,verify方法返回的是bigint类型,而不是U8类型。
// Generate RSA key pair
const key = rsa(2048)
const p = UTF8('mima-kit')
const c = U8.fromBI(key.encrypt(p))
const m = U8.fromBI(key.decrypt(c))
// p === m
const s = U8.fromBI(key.sign(p))
const v = U8.fromBI(key.verify(s))
// v === m
// Using existing key pair
const k: RSAPrivateKey = {
n: 82829320812173273978971929158153744899206558830123557057765054811547521644103n,
e: 65537n,
d: 2824085895826802885484730392051734790667622575612305367583022267256127084981n,
p: 259507137283474348662341935422619692757n,
q: 319179355447530963616684534587734455979n,
dP: 244693883692716798906542597942783565521n,
dQ: 232625874131426773839982556335858160883n,
qInv: 143180457747603899913822528225463864868n,
}
const key = rsa(k)
MGF1 是 PKCS#1 标准中的一个函数组件,它用于生成 OAEP 和 PSS 等密码学方案中的 Mask。MGF1 需要组合 Hash 函数,通常 MGF1 不会直接使用,而是作为 OAEP 和 PSS 的一部分。
const mgf = mgf1(sha1)
const seed = new U8()
const length = 32
const mask = mgf(seed, length)
interface MGF {
(mdfSeed: Uint8Array, maskLen: number): Uint8Array
}
RSAES-PKCS1-v1_5 是 PKCS#1 标准中的一个加密方案。
const p = UTF8('mima-kit')
const key = rsa(2048)
const cipher = pkcs1_es_1_5(key)
const c = cipher.encrypt(p)
const m = cipher.decrypt(c)
// p === m
RSAES-OAEP 是 PKCS#1 标准中的一个加密方案。它需要组合 Hash 函数、MGF 函数和 Label 数据。
const p = UTF8('mima-kit')
const key = rsa(2048)
// using SHA-256, MGF1-SHA-256, and empty label by default
const cipher = pkcs1_es_oaep(key)
// using SHA-1, MGF1-SHA-1, and empty label
const cipher = pkcs1_es_oaep(key, sha1)
// using SHA-1, MGF1-SHA-256, and label 'mima-kit'
const cipher = pkcs1_es_oaep(key, sha1, mgf1(sha256), UTF8('mima-kit'))
const c = cipher.encrypt(p)
const m = cipher.decrypt(c)
// p === m
RSASSA-PKCS1-v1_5 是 PKCS#1 标准中的一个签名方案。它需要组合 Hash 函数。
RSASSA-PKCS1-v1_5会用到Hash的OID,mima-kit中只有部份Hash函数记录了OID,请务必在使用RSASSA-PKCS1-v1_5时检查Hash函数的OID是否正确。
const p = UTF8('mima-kit')
const key = rsa(2048)
// check OID before using
sha256.OID = '2.16.840.1.101.3.4.2.1'
// using SHA-256 by default
const cipher = pkcs1_ssa_1_5(key)
// using SHA-1
const cipher = pkcs1_ssa_1_5(key, sha1)
const s = cipher.sign(p)
const v = cipher.verify(p, s)
// v === true
RSASSA-PSS 是 PKCS#1 标准中的一个签名方案。它需要组合 Hash 函数、MGF 函数和 Salt Length。
const p = UTF8('mima-kit')
const key = rsa(2048)
// using SHA-256, MGF1-SHA-256, and sha256.DIGEST_SIZE
const cipher = pkcs1_ssa_pss(key)
// using SHA-1, MGF1-SHA-1, and sha1.DIGEST_SIZE
const cipher = pkcs1_ssa_pss(key, sha1)
// using SHA-1, MGF1-SHA-256, and sha1.DIGEST_SIZE
const cipher = pkcs1_ssa_pss(key, sha1, mgf1(sha256))
// using SHA-1, MGF1-SHA-256, and 32
const cipher = pkcs1_ssa_pss(key, sha1, mgf1(sha256), 32)
const s = cipher.sign(p)
const v = cipher.verify(p, s)
// v === true
Specification: SEC 1
Elliptic-Curve Cryptography 是一种基于椭圆曲线的非对称加密算法。mima-kit 目前仅支持基于素域 Weierstrass 和 Montgomery 椭圆曲线的 ECC 算法。
使用 ECC 算法前需要选择一个 椭圆曲线。参考 椭圆曲线列表。
在
mima-kit的仓库中有许多未导出到包外的椭圆曲线,您可以在/src/core/ecParams.ts中找到这些椭圆曲线。这些椭圆曲线大多是过于老旧且不常用的曲线,我也没有测试过是否能正常地工作。
const ec = FpECC(secp256r1)
// Generate ECC key pair: ECKeyPair<U8>
const key = ec.gen()
const key = ec.gen('key_pair')
// Generate ECC private key: ECPrivateKey<U8>
const s_key = ec.gen('private_key')
// Generate ECC public key: ECKeypair<U8>
const p_key = ec.gen('public_key', s_key)
interface FpECPoint<T = bigint | Uint8Array> {
isInfinity: boolean
x: T
y: T
}
interface ECPublicKey<T = bigint | Uint8Array> {
/** Prime Field Elliptic Curve Public Key */
readonly Q: Readonly<FpECPoint<T>>
}
interface ECPrivateKey<T = bigint | Uint8Array> {
/** Prime Field Elliptic Curve Private Key */
readonly d: T
}
/** Elliptic Curve Key Pair */
interface ECKeyPair<T = bigint | Uint8Array> extends ECPrivateKey<T>, ECPublicKey<T> {
}
Point Compress 是 ECC 算法的公钥压缩方法,用于转换 FpECPoint 和 U8。
const ec = FpECC(secp256r1)
const { PointToU8, U8ToPoint } = ec.utils
const P = ec.gen().Q
// will not compress by default
const U = pointToU8(P)
// compress
const U = pointToU8(P, true)
// decompress: FpECPoint<U8>
const P = U8ToPoint(U)
Elliptic Curve Diffie-Hellman 是 ECC 算法的一种密钥协商协议。在计算得到共享密钥后,通常会使用 KDF 从共享密钥中派生出一个或多个密钥。
ECDH的结果是一个FpECPoint<U8>,通常会使用x作为KDF的密钥材料。
const ec = FpECC(secp256r1)
const keyA = ec.gen()
const keyB = ec.gen()
const secretA = ec.dh(keyA, keyB).x
const secretB = ec.dh(keyB, keyA).x
// secretA === secretB
Elliptic Curve Co-factor Diffie-Hellman 是基于 ECDH 的一种密钥协商协议。对曲线参数中 co-factor 为 1 的曲线,ECDH 和 ECCDH 的结果是相同的。
ECCDH的结果是一个FpECPoint<U8>,通常会使用x作为KDF的密钥材料。
const ec = FpECC(w25519)
const keyA = ec.gen()
const keyB = ec.gen()
const secretAc = ec.cdh(keyA, keyB).x
const secretBc = ec.cdh(keyB, keyA).x
// secretAc === secretBc
Elliptic Curve Menezes-Qu-Vanstone 是基于 ECDH 的一种密钥协商协议。
ECMQV的结果是一个FpECPoint<U8>,通常会使用x作为KDF的密钥材料。
const ec = FpECC(secp256r1)
const u_k1 = ec.gen()
const u_k2 = ec.gen()
const v_k1 = ec.gen()
const v_k2 = ec.gen()
const secretA = ec.mqv(u_k1, u_k2, v_k1, v_k2).x
const secretB = ec.mqv(v_k1, v_k2, u_k1, u_k2).x
// secretA === secretB
Elliptic Curve Digital Signature Algorithm 是 ECC 算法的一种签名方案。
需要注意的是,
ECDSA的签名方法返回的是ECDSASignature类型,而不是U8类型。因为ECDSA签名的结果包含了r和s两个值。而在不同的标准下,对r和s的转换和拼接方式有可能不同。所以返回ECDSASignature可以提供更多的灵活性。
const ec = FpECC(secp256r1)
const key = ec.gen()
const p = UTF8('mima-kit')
// using SHA-256 by default
const signer = ec.dsa()
// using SHA-1
const signer = ec.dsa(sha1)
// sign: ECDSASignature<U8>
const s = cipher.sign(key, p)
const v = cipher.verify(key, p, s)
// v === true
interface ECDSASignature<T = bigint | Uint8Array> {
/** Temporary Public Key's x */
r: T
/** Signature Value */
s: T
}
ECIES 是 ECC 算法的一种集成加密方案。ECIES 的配置内容比较多,请参考 ECIESConfig 接口。
ECIES的结果是一个ECIESCiphertext类型,它包含了临时公钥、密文和校验值。
const ec = FpECC(secp256r1)
const key = ec.gen()
const cipher = ec.ies()
const p = UTF8('mima-kit')
const c = cipher.encrypt(key, p)
const m = cipher.decrypt(key, c)
// p === m
interface ECIESConfig {
/** Block Cipher Algorithm (default: AES-256-GCM) */
cipher?: IVBlockCipher
/** Key Hash Function (default: HMAC-SHA-256) */
mac?: KeyHash
/** Key Derivation Function (default: ANSI-X9.63-KDF with SHA-256) */
kdf?: KDF
/** Additional Data 1 (default: empty) */
S1?: Uint8Array
/** Additional Data 2 (default: empty) */
S2?: Uint8Array
/** Initialization Vector (default: Uint8Array(cipher.BLOCK_SIZE)) */
iv?: Uint8Array
}
interface ECIESCiphertext {
/** Temporary Public Key */
R: ECPublicKey
/** Ciphertext */
C: Uint8Array
/** Check Value */
D: Uint8Array
}
Specification: GB/T 35276-2017
SM2 算法是中国国家密码管理局发布的一种基于 椭圆曲线 的 非对称加密算法。理论上,SM2 算法可以使用任意的 椭圆曲线,但是在实际应用中,SM2 算法通常使用 sm2p256v1 曲线,所以 mima-kit 使用 sm2p256v1 曲线作为 SM2 算法的默认曲线。
const sm2ec = sm2()
// Generate SM2 key pair
const key = sm2ec.gen()
const key = sm2ec.gen('key_pair')
// Generate SM2 private key
const s_key = sm2ec.gen('private_key')
// Generate SM2 public key
const p_key = sm2ec.gen('public_key', s_key)
SM2 在 ECC 的基础上增加了 可辨别标识 (Distinguishing Identifier) 的概念。可辨别标识 利用用户标识、公钥和曲线的部分参数,实现无歧义地标识实体的身份信息。
const sm2ec = sm2()
const ID = UTF8('alice@rabbit.panic')
const KA = sm2ec.gen()
const ZA = sm2ec.di(ID, KA)
interface SM2DI {
/**
* @param {Uint8Array} id - User Identity
* @param {ECPublicKey} key - Public Key
* @param {Hash} hash - Hash Algorithm (default: SM3)
*/
(id: Uint8Array, key: ECPublicKey, hash?: Hash): U8
}
SM2 算法的密钥协商协议。与标准不同,mima-kit 的 SM2-DH 直接返回 共享密钥。你需要另外使用 KDF 从 共享密钥 中派生密钥。SM2 标准使用的 KDF 是 ANSI-X9.63-KDF with SM3。ANSI-X9.63-KDF 和 SM3 都是 mima-kit 支持的算法,你可以直接使用她们。
const sm2ec = sm2()
const kdf = x963kdf(sm3)
// Initiator: Alice
// Responder: Bob
// Step 1: Alice
const KA = sm2ec.gen()
const KX = sm2ec.gen()
const ka = { Q: KA.Q } // public key of Alice
const kx = { Q: KX.Q } // temporary public key of Alice
const ID_A = UTF8('alice@rabbit.panic')
const ZA = sm2ec.di(ID_A, KA) // Alice's distinguishable identifier
// send ZA, ka, kx to Bob
// Step 2: Bob
const KB = sm2ec.gen()
const KY = sm2ec.gen()
const kb = { Q: KB.Q } // public key of Bob
const ky = { Q: KY.Q } // temporary public key of Bo
const ID_B = UTF8('bob@rolling.stone')
const ZB = sm2ec.di(ID_B, KB) // Bob's distinguishable identifier
const SB = sm2ec.dh(KB, KY, ka, kx, ZA, ZB) // shared secret key
const DKB = kdf(256, S) // derive key
// send ZB, kb, ky to Alice
// Step 3: Alice
const SA = sm2ec.dh(KA, KX, kb, ky, ZA, ZB) // shared secret key
const DKA = kdf(256, S) // derive key
SA === SB
DKA === DKB
interface SM2DH {
/**
* @param {ECKeyPair} KA - Self Key Pair
* @param {ECPublicKey} KX - Self Temporary Key Pair
* @param {ECPublicKey} KB - Opposite Public Key
* @param {ECPublicKey} KY - Opposite Temporary Public Key
* @param [Uint8Array] ZA - Initiator Identity Derived Value
* @param [Uint8Array] ZB - Receiver Identity Derived Value
* @returns {U8} - Keying Material
*/
(KA: ECKeyPair, KX: ECKeyPair, KB: ECPublicKey, KY: ECPublicKey, ZA?: Uint8Array, ZB?: Uint8Array): U8
}
SM2 Digital Signature Algorithm 是 SM2 算法的签名方案。她接受一个 Hash 函数作为参数,SM2-DSA 使用 SM3 作为默认的 Hash 函数。
SM2-DSA的签名方法返回的是SM2DSASignature类型,而不是U8类型。SM2-DSA签名的结果包含了r和s两个值。
const sm2ec = sm2()
const ID = UTF8('alice@rabbit.panic')
const KA = sm2ec.gen()
const ZA = sm2ec.di(ID, KA)
const M = UTF8('mima-kit')
const signer = sm2ec.dsa() // using SM3 by default
const signature = signer.sign(ZA, KA, M)
signer.verify(ZA, KA, M, signature) // true
interface SM2DSASignature<T = bigint | Uint8Array> {
r: T
s: T
}
interface SM2DSA {
/**
* @param {Hash} hash - Hash Algorithm (default: SM3)
*/
(hash?: Hash): {
/**
* @param {Uint8Array} Z - Identity Derived Value
* @param {ECPrivateKey} key - Signer Private Key
* @param {Uint8Array} M - Message
*/
sign: (Z: Uint8Array, key: ECPrivateKey, M: Uint8Array) => SM2DSASignature<U8>
/**
* @param {Uint8Array} Z - Identity Derived Value
* @param {ECPublicKey} key - Signer Public Key
* @param {Uint8Array} M - Message
* @param {SM2DSASignature} S - Signature
*/
verify: (Z: Uint8Array, key: ECPublicKey, M: Uint8Array, S: SM2DSASignature) => boolean
}
}
SM2-ES 是 SM2 算法的加密方案。
const sm2ec = sm2(curve)
const M = UTF8('The king\'s ears are donkey ears')
const key = sm2ec.gen()
const cipher = sm2ec.es()
const C = cipher.encrypt(key, M)
cipher.decrypt(key, C) // M
interface SM2Encrypt {
/**
* @param {ECPublicKey} p_key - Receiver Public Key
* @param {Uint8Array} M - Plaintext
*/
(p_key: ECPublicKey, M: Uint8Array): U8
}
interface SM2Decrypt {
/**
* @param {ECPrivateKey} s_key - Decryptor Private Key
* @param {Uint8Array} C - Ciphertext
*/
(s_key: ECPrivateKey, C: Uint8Array): U8
}
interface SM2EncryptionScheme {
/**
* @param {Hash} hash - Hash Algorithm (default: SM3)
* @param {KDF} kdf - Key Derivation Function (default: X9.63 KDF with SM3)
* @param {'c1c2c3' | 'c1c3c2'} order - Ciphertext Segment Order (default: 'c1c3c2')
*/
(hash?: Hash, kdf?: KDF, order?: 'c1c2c3' | 'c1c3c2'): {
encrypt: SM2Encrypt
decrypt: SM2Decrypt
}
}
Specification: RFC 7748
x25519 和 x448 是基于 Montgomery 曲线的 ECC 算法。他们不是 FpECC 的实例,而是单独的算法。
需要注意
mima-kit提供的x25519和x448可能无法与其他实现完全兼容。因为RFC 7748规定以小端序作为编码方式,而mima-kit使用大端序作为编码方式。通过转换端序应该可以与其他实现兼容。
虽然
FpECC也可以进行Montgomery曲线的计算,但是x25519和x448算法只需要x坐标,且他们的算法实施都会对私钥进行clamp处理,所以他们的底层是更高效的独立算法。
// Generate key pair: X25519KeyPair<U8>
const key = x25519.gen()
const key = x25519.gen('key_pair')
// Generate private key: X25519PrivateKey<U8>
const s_key = x25519.gen('private_key')
// Generate public key: X25519KeyPair<U8>
const p_key = x25519.gen('public_key', s_key)
interface X25519PrivateKey<T = bigint | Uint8Array> {
/** Private Key */
d: T
}
interface X25519PublicKey<T = bigint | Uint8Array> {
/** Public Key */
Q: T
}
interface X25519KeyPair<T = bigint | Uint8Array> extends X25519PrivateKey<T>, X25519PublicKey<T> {
}
x25519 和 x448 算法的密钥协商协议。与标准不同,他们直接返回 共享密钥。你需要另外使用 KDF 从 共享密钥 中派生密钥。
const keyA = x25519.gen()
const keyB = x25519.gen()
const secretA = x25519.dh(keyA, keyB)
const secretB = x25519.dh(keyB, keyA)
// secretA === secretB
密钥派生函数 (KDF) 是一种从一个密钥派生出另一个或多个密钥的算法。KDF 很少直接使用,而是作为其他算法方案的一部分。
interface KDF {
/**
* @param {number} k_bit - 期望的密钥长度 / output keying material length
* @param {Uint8Array} ikm - 输入密钥材料 / input keying material
* @param {Uint8Array} info - 附加信息 / optional context and application specific information
*/
(k_bit: number, ikm: Uint8Array, info?: Uint8Array): U8
}
X9.63KDF 是 ANSI-X9.63 标准中的一个密钥派生函数。X9.63KDF 需要组合 Hash 函数。
const kdf = x963kdf(sha256)
HKDF 是 RFC 5869 标准中的一个密钥派生函数。HKDF 需要组合 KeyHash 函数和一个可选的 salt
const mac = hmac(sha256)
const kdf = hkdf(mac)
PBKDF2 是 PKCS#5 标准中的一个密钥派生函数。PBKDF2 需要组合 KeyHash 函数,指定 iteration 次数和一个可选的 salt。
const mac = hmac(sha256)
const kdf = pbkdf2(mac, 1000)
mima-kit 并没有导出所有的 椭圆曲线,但是您可以在 /src/core/ecParams.ts 中找到所有的 椭圆曲线。
Weierstrass 曲线在表格之外,
sm2p256v1也是导出的Weierstrass曲线。它适用于所有ECC算法,但是它常用于SM2算法,所以不写入表格之中。
| SEC | NIST | X9.63 | RFC 5639 |
|---|---|---|---|
| - | w25519 | - | - |
| - | w448 | - | - |
secp192k1 | - | - | - |
secp192r1 | p192 | prime192v1 | - |
secp224k1 | - | - | - |
secp224r1 | p224 | - | - |
secp256r1 | p256 | prime256v1 | - |
secp256k1 | - | - | - |
secp384r1 | p384 | - | - |
secp521r1 | p521 | - | - |
| - | - | - | bp192r1 |
| - | - | - | bp224r1 |
| - | - | - | bp256r1 |
| - | - | - | bp320r1 |
| - | - | - | bp384r1 |
| - | - | - | bp512r1 |
FAQs
mima-kit is a cryptographic suite implemented in TypeScript. The goal is to provide an easy-to-use cryptographic library. mima-kit 是一个使用 TypeScript 实现的密码学套件。目标是提供一个简单易用的密码学库。
The npm package mima-kit receives a total of 310 weekly downloads. As such, mima-kit popularity was classified as not popular.
We found that mima-kit demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
AI agents are pulling packages into environments no scanner is watching, creating exposure before security teams can see it.

Security News
GitHub Actions checkout now blocks risky pull_request_target checkouts by default to help prevent pwn request supply chain attacks.

Product
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.