mima-kit
v0.0.5
Published
mima-kit is a cryptographic suite implemented in TypeScript. The goal is to provide an easy-to-use cryptographic library. mima-kit 是一个使用 TypeScript 实现的密码学套件。目标是提供一个简单易用的密码学库。
Downloads
46
Maintainers
Readme
mima-kit
mima-kit
是一个使用 TypeScript
实现的密码学套件。目标是提供一个简单易用的密码学库。mima-kit
尚处于早期开发阶段,API 可能会发生变化。
安装
npm install mima-kit
散列算法
散列方案(createHash)
mima-kit
原生实现的散列算法都是基于 Uint8Array
。为了方便使用,mima-kit
使用 createHash
函数对原生实现进行包装。
import type { HashScheme } from 'mima-kit'
import { B64URL, createHash, sm3 } from 'mima-kit'
const scheme: HashScheme = {
digest: sm3.digest,
INPUT_CODEC: B64URL,
OUTPUT_CODEC: B64URL,
}
const _sm3 = createHash(
scheme,
{
ALGORITHM: sm3.ALGORITHM,
BLOCK_SIZE: sm3.BLOCK_SIZE,
DIGEST_SIZE: sm3.DIGEST_SIZE,
}
)
console.log(_sm3('bWltYS1raXQ')) // Base64url string
type Digest = (M: Uint8Array) => Uint8Array
interface HashScheme {
digest: Digest
/**
* @default UTF8
*/
INPUT_CODEC?: Codec
/**
* @default HEX
*/
OUTPUT_CODEC?: Codec
}
interface HashDescription {
/**
* 算法名称
*/
ALGORITHM: string
/**
* 分块大小 (byte)
*/
BLOCK_SIZE: number
/**
* 摘要大小 (byte)
*/
DIGEST_SIZE: number
}
散列算法的默认行为
通过 createHash
包装的散列算法有一些默认行为:
digest
函数是算法的原生实现,其输入输出均为Uint8Array
类型。- 除了调用
digest
函数外,还可以直接调用签名函数。签名函数接受string
或Uint8Array
类型的输入,并会自动对string
类型的输入进行UTF8
编码。 - 签名函数默认输出为
HEX
编码字符串,可以通过传递第二个参数来更改输出编码。只要编码器实现了Codec
接口,理论上可以使用任何编码。 - 算法不仅提供了丰富的调用方式和可自由组合的编码器,还记录了许多有用的信息,如算法名称、分块大小、输出长度以及输入输出的编解码器等。你可以通过
console.log
查看这些信息。
import { B64, sm3 } from 'mima-kit'
let M: string | Uint8Array
// When M is Uint8Array
M = new Uint8Array()
console.log(sm3(M)) // Hex string
console.log(sm3(M, B64)) // Base64 string
console.log(sm3.digest(M)) // Uint8Array
// When M is string
M = 'utf-8 string'
console.log(sm3(M)) // Hex string
console.log(sm3(M, B64)) // Base64 string
console.log(sm3.digest(M)) // Error
// Algorithm Information
console.log(sm3)
加密散列算法
MD5
md5('mima-kit')
SM3
sm3('mima-kit')
SHA-1
sha1('mima-kit')
SHA-2
sha224('mima-kit')
sha256('mima-kit')
sha384('mima-kit')
sha512('mima-kit')
const sha512_224 = sha512t(224)
sha512_224('mima-kit')
SHA-3
sha3_224('mima-kit')
sha3_256('mima-kit')
sha3_384('mima-kit')
sha3_512('mima-kit')
shake128(256)('mima-kit')
shake256(512)('mima-kit')
cSHAKE
import type { cSHAKEConfig } from 'mima-kit'
const config: cSHAKEConfig = {
N: 'name', // function name
S: 'custom', // customization string
}
cShake128(256, config)('mima-kit')
cShake256(512, config)('mima-kit')
TupleHash
import type { TupleHashConfig } from 'mima-kit'
const config: TupleHashConfig = {
S: 'custom', // customization string
}
tupleHash128(256, config)(['mima', '-', 'kit'])
tupleHash256(512, config)(['mima', '-', 'kit'])
tupleHash128XOF(256, config)(['mima', '-', 'kit'])
tupleHash256XOF(512, config)(['mima', '-', 'kit'])
ParallelHash
注意:
mima-kit
提供的ParallelHash
算法并不能真正并行计算,只是将输入分块后分别计算,最后将结果拼接。
import type { ParallelHashConfig } from 'mima-kit'
const config: ParallelHashConfig = {
S: 'custom', // customization string
}
const blockSize = 1024
parallelHash128(blockSize, 256, config)('mima-kit')
parallelHash256(blockSize, 512, config)('mima-kit')
parallelHash128XOF(blockSize, 256, config)('mima-kit')
parallelHash256XOF(blockSize, 512, config)('mima-kit')
带密钥的加密散列算法
HMAC
import type { HMACScheme } from 'mima-kit'
const scheme: HMACScheme = {
hash: sm3,
key: 'password',
}
hmac(scheme)('mima-kit')
KMAC
import type { KMACConfig } from 'mima-kit'
const config: KMACConfig = {
K: 'password', // key
S: 'custom', // customization string
}
kmac128(256, config)('mima-kit')
kmac256(512, config)('mima-kit')
kmac128XOF(256, config)('mima-kit')
kmac256XOF(512, config)('mima-kit')
对称密钥算法
对称密钥算法是一种使用相同密钥进行加密和解密的加密算法。大致可以分为:分组密码算法
和 流密码算法
。
分组密码算法
通常,我们会将 分组密码算法
、 填充模式
和 分组模式
组合在一起,形成一个完整的 分组密码方案
。因为单独的 分组密码算法
只能对单个数据块进行加解密,所以它们在单独使用时并没有太大的意义。
mima-kit
将 组合
这一行为放在了 分组模式
中,以达到灵活复用的目的。查看 分组模式 将 分组密码算法
、分组模式
和 填充模式
组合在一起。
const k = ''
const iv = ''
const config: CBCConfig = { }
const cbc_sm4 = cbc(sm4, config)(k, iv)
const c = cbc_sm4.encrypt('mima-kit') // c: Hex string
const m = cbc_sm4.decrypt(c) // m: UTF8 string
SM4
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
sm4(k).encrypt(m) // c
sm4(k).decrypt(c) // m
AES
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
ARIA
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
Camellia
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
DES
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
des(k).encrypt(m) // c
des(k).decrypt(c) // m
3DES
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
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
Blowfish
let k: Uint8Array
let m: Uint8Array
let c: Uint8Array
blowfish(k).encrypt(m) // c
blowfish(k).decrypt(c) // m
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
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
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
填充模式
单独使用 填充模式
并没有太大的意义。查看 分组模式 将 分组密码算法
、分组模式
和 填充模式
组合在一起。
PKCS#7
let block_size: number
let m: Uint8Array
let p: Uint8Array
PKCS7(m, block_size) // p
PKCS7(p) // m
ANSI X9.23
let block_size: number
let m: Uint8Array
let p: Uint8Array
ANSI_X923(m, block_size) // p
ANSI_X923(p) // m
ISO/IEC 7816-4
let block_size: number
let m: Uint8Array
let p: Uint8Array
ISO7816_4(m, block_size) // p
ISO7816_4(p) // m
Zero Padding
let block_size: number
let m: Uint8Array
let p: Uint8Array
ZERO_PAD(m, block_size) // p
ZERO_PAD(p) // m
分组模式
查看 /test/cipher.test.ts
以获取更多使用示例。
ECB
Electronic Codebook (ECB) 是最简单的分组模式。ECB
模式将明文分成固定长度的数据块,然后对每个数据块进行加密。
ECB
模式不需要iv
。
const k = '' // hex string
const m = '' // utf8 string
const c = '' // hex string
const config: ECBConfig = { }
const CIPHER = ecb(sm4, config)(k)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
interface ECBConfig {
/**
* @default PKCS7
*/
PADDING?: Padding
/**
* @default HEX
*/
KEY_CODEC?: Codec
/**
* @default UTF8
*/
ENCRYPT_INPUT_CODEC?: Codec
/**
* @default HEX
*/
ENCRYPT_OUTPUT_CODEC?: Codec
/**
* @default HEX
*/
DECRYPT_INPUT_CODEC?: Codec
/**
* @default UTF8
*/
DECRYPT_OUTPUT_CODEC?: Codec
}
CBC
Cipher Block Chaining (CBC) 是最常用的分组模式。CBC
模式每个明文块都会与前一个密文块进行异或操作,然后再进行加密。
CBC
模式需要iv
。iv
的长度与加密算法的BLOCK_SIZE
相同。
const k = '' // hex string
const iv = '' // hex string
const m = '' // utf8 string
const c = '' // hex string
const config: CBCConfig = { }
const CIPHER = cbc(sm4, config)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
interface CBCConfig {
/**
* @default PKCS7
*/
PADDING?: Padding
/**
* @default HEX
*/
KEY_CODEC?: Codec
/**
* @default HEX
*/
IV_CODEC?: Codec
/**
* @default UTF8
*/
ENCRYPT_INPUT_CODEC?: Codec
/**
* @default HEX
*/
ENCRYPT_OUTPUT_CODEC?: Codec
/**
* @default HEX
*/
DECRYPT_INPUT_CODEC?: Codec
/**
* @default UTF8
*/
DECRYPT_OUTPUT_CODEC?: Codec
}
PCBC
Progressive Chaining Block Cipher (PCBC) 是 CBC
的变种。PCBC
模式每个明文块都会与前一个明文和前一个密文块进行异或操作,然后再进行加密。PCBC
模式旨在将密文中的微小变化在加解密时无限传播。
PCBC
模式需要iv
。iv
的长度与加密算法的BLOCK_SIZE
相同。
const k = '' // hex string
const iv = '' // hex string
const m = '' // utf8 string
const c = '' // hex string
const config: PCBCConfig = { }
const CIPHER = pcbc(sm4, config)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
interface PCBCConfig {
/**
* @default PKCS7
*/
PADDING?: Padding
/**
* @default HEX
*/
KEY_CODEC?: Codec
/**
* @default HEX
*/
IV_CODEC?: Codec
/**
* @default UTF8
*/
ENCRYPT_INPUT_CODEC?: Codec
/**
* @default HEX
*/
ENCRYPT_OUTPUT_CODEC?: Codec
/**
* @default HEX
*/
DECRYPT_INPUT_CODEC?: Codec
/**
* @default UTF8
*/
DECRYPT_OUTPUT_CODEC?: Codec
}
CFB
Cipher Feedback (CFB) 将分组密码转换为流密码。CFB
模式通过加密前一个密文块获得加密数据流,然后与明文块进行异或操作,获得密文块。
CFB
模式需要iv
。iv
的长度与加密算法的BLOCK_SIZE
相同。
const k = '' // hex string
const iv = '' // hex string
const m = '' // utf8 string
const c = '' // hex string
const config: CFBConfig = { }
const CIPHER = cfb(sm4, config)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
interface CFBConfig {
/**
* @default PKCS7
*/
PADDING?: Padding
/**
* @default HEX
*/
KEY_CODEC?: Codec
/**
* @default HEX
*/
IV_CODEC?: Codec
/**
* @default UTF8
*/
ENCRYPT_INPUT_CODEC?: Codec
/**
* @default HEX
*/
ENCRYPT_OUTPUT_CODEC?: Codec
/**
* @default HEX
*/
DECRYPT_INPUT_CODEC?: Codec
/**
* @default UTF8
*/
DECRYPT_OUTPUT_CODEC?: Codec
}
OFB
Output Feedback (OFB) 将分组密码转换为流密码。OFB
模式通过加密 iv
获得加密数据流,然后与明文块进行异或操作,获得密文块。
OFB
模式需要iv
。iv
的长度与加密算法的BLOCK_SIZE
相同。
const k = '' // hex string
const iv = '' // hex string
const m = '' // utf8 string
const c = '' // hex string
const config: OFBConfig = { }
const CIPHER = ofb(sm4, config)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
interface OFBConfig {
/**
* @default PKCS7
*/
PADDING?: Padding
/**
* @default HEX
*/
KEY_CODEC?: Codec
/**
* @default HEX
*/
IV_CODEC?: Codec
/**
* @default UTF8
*/
ENCRYPT_INPUT_CODEC?: Codec
/**
* @default HEX
*/
ENCRYPT_OUTPUT_CODEC?: Codec
/**
* @default HEX
*/
DECRYPT_INPUT_CODEC?: Codec
/**
* @default UTF8
*/
DECRYPT_OUTPUT_CODEC?: Codec
}
CTR
CTR
模式需要iv
。iv
的长度与加密算法的BLOCK_SIZE
相同。
Counter Mode (CTR) 将分组密码转换为流密码。CTR
模式将 iv
与计数器组合以生成唯一的 计数器块
,通过加密 计数器块
获得加密数据流,然后与明文块进行异或操作,获得密文块。
const k = '' // hex string
const iv = '' // hex string
const m = '' // utf8 string
const c = '' // hex string
const config: CTRConfig = { }
const CIPHER = ctr(sm4, config)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
interface CTRConfig {
/**
* @default PKCS7
*/
PADDING?: Padding
/**
* @default HEX
*/
KEY_CODEC?: Codec
/**
* @default HEX
*/
IV_CODEC?: Codec
/**
* @default UTF8
*/
ENCRYPT_INPUT_CODEC?: Codec
/**
* @default HEX
*/
ENCRYPT_OUTPUT_CODEC?: Codec
/**
* @default HEX
*/
DECRYPT_INPUT_CODEC?: Codec
/**
* @default UTF8
*/
DECRYPT_OUTPUT_CODEC?: Codec
}
GCM
Galois/Counter Mode (GCM) 将分组密码转换为流密码。GCM
模式可以看作是 CTR
模式的变种,它在 CTR
模式的基础上增加了 认证
功能。
GCM
模式需要iv
。iv
的长度没有限制,但推荐使用96
位长度的iv
。- 签名生成的
AUTH_TAG
长度由AUTH_TAG_SIZE
参数决定。AUTH_TAG
最大长度为128
位,设置任意长度都不会影响程序的运行,但一般推荐使用128
、120
、112
、104
、96
位长度,对于某些应用也可以使用64
、32
位长度。
mima-kit
实现的 GCM
模式并没有进行查表优化,因此性能可能会比较慢。
const k = '' // hex string
const iv = '' // hex string
const m = '' // utf8 string
const a = '' // utf8 string
const c = '' // hex string
const t = '' // hex string
const config: GCMConfig = { }
const CIPHER = gcm(aes(128), config)(k, iv)
CIPHER.encrypt(m) // c
CIPHER.decrypt(c) // m
CIPHER.sign(c, a) // auth tag
CIPHER.verify(t, c, a) // true or false
interface GCMConfig {
/**
* @default PKCS7
*/
PADDING?: Padding
/**
* @default HEX
*/
KEY_CODEC?: Codec
/**
* @default HEX
*/
IV_CODEC?: Codec
/**
* @default UTF8
*/
ENCRYPT_INPUT_CODEC?: Codec
/**
* @default HEX
*/
ENCRYPT_OUTPUT_CODEC?: Codec
/**
* @default HEX
*/
DECRYPT_INPUT_CODEC?: Codec
/**
* @default UTF8
*/
DECRYPT_OUTPUT_CODEC?: Codec
/**
* @default UTF8
*/
ADDITIONAL_DATA_CODEC?: Codec
/**
* 认证标签长度 (byte)
* @default 16
*/
AUTH_TAG_SIZE?: number
/**
* @default HEX
*/
AUTH_TAG_CODEC?: Codec
}
流密码算法
通常 流密码算法
不需要复杂的配置,一般只需要 key
和 iv
。
const k = ''
const iv = ''
const config: IVStreamCipherConfig = { }
const cipher = salsa20(k, iv, config)
const c = cipher.encrypt('mima-kit') // c: Hex string
const m = cipher.decrypt(c) // m: UTF8 string
interface IVStreamCipherConfig {
/**
* @default HEX
*/
KEY_CODEC?: Codec
/**
* @default HEX
*/
IV_CODEC?: Codec
/**
* @default UTF8
*/
ENCRYPT_INPUT_CODEC?: Codec
/**
* @default HEX
*/
ENCRYPT_OUTPUT_CODEC?: Codec
/**
* @default HEX
*/
DECRYPT_INPUT_CODEC?: Codec
/**
* @default UTF8
*/
DECRYPT_OUTPUT_CODEC?: Codec
}
ZUC
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,
}
const config: ZUCConfig = { }
// 128-EEA3 加密消息
eea3(params, config) // c
// 128-EIA3 计算消息认证码
eia3(params, config) // mac
// 128-EEA3 解密消息
params.M = c
eea3(params, config) // m
ARC4
ARC4
算法可以接受长度为 0 < k.byteLength < 256
的密钥,同时 ARC4
算法不需要 iv
。
const k = ''
const config: StreamCipherConfig = { }
const cipher = arc4(k, config)
const c = cipher.encrypt('mima-kit') // c: Hex string
const m = cipher.decrypt(c) // m: UTF8 string
interface StreamCipherConfig {
/**
* @default HEX
*/
KEY_CODEC?: Codec
/**
* @default UTF8
*/
ENCRYPT_INPUT_CODEC?: Codec
/**
* @default HEX
*/
ENCRYPT_OUTPUT_CODEC?: Codec
/**
* @default HEX
*/
DECRYPT_INPUT_CODEC?: Codec
/**
* @default UTF8
*/
DECRYPT_OUTPUT_CODEC?: Codec
}
Salsa20
Salsa20
算法可以接受长度为 16
或 32
字节的密钥和 8
字节的 iv
。
const k = ''
const iv = ''
const cipher = salsa20(k, iv)
const c = cipher.encrypt('mima-kit') // c: Hex string
const m = cipher.decrypt(c) // m: UTF8 string
Rabbit
Rabbit
算法可以接受长度为 16
字节的密钥。对于 iv
,Rabbit
算法可以接受长度为 0
或 8
字节的 iv
。当 iv
长度为 0
字节时,Rabbit
算法会跳过 iv Setup
步骤。
const k = ''
const iv = new Uint8Array(8)
const cipher = rabbit(k, iv)
const c = cipher.encrypt('mima-kit') // c: Hex string
const m = cipher.decrypt(c) // m: UTF8 string
// skip iv setup
const cipher = rabbit(k, new Uint8Array(0))
const c = cipher.encrypt('mima-kit') // c: Hex string
const m = cipher.decrypt(c) // m: UTF8 string