feat: add crypto

This commit is contained in:
banshan
2024-12-31 13:28:45 +08:00
parent ddcdf831ae
commit 5a4b88a5a8
17 changed files with 968 additions and 1 deletions

View File

@@ -65,3 +65,15 @@ snap:
onpub: onpub:
transform: transform:
.* : $0 .* : $0
crypto:
enable: false
isstatic: false
algo: aes_ctr # 加密算法 支持 aes_ctr xor_c
encryptlen: 1024
secret:
key: your key
iv: your iv
onpub:
transform:
.* : $0

View File

@@ -7,7 +7,7 @@ import (
"m7s.live/v5" "m7s.live/v5"
_ "m7s.live/v5/plugin/cascade" _ "m7s.live/v5/plugin/cascade"
// _ "m7s.live/v5/plugin/crypto" _ "m7s.live/v5/plugin/crypto"
_ "m7s.live/v5/plugin/debug" _ "m7s.live/v5/plugin/debug"
_ "m7s.live/v5/plugin/flv" _ "m7s.live/v5/plugin/flv"
_ "m7s.live/v5/plugin/gb28181" _ "m7s.live/v5/plugin/gb28181"

43
plugin/crypto/api.go Normal file
View File

@@ -0,0 +1,43 @@
package plugin_crypto
import (
"encoding/base64"
"fmt"
"net/http"
cryptopkg "m7s.live/v5/plugin/crypto/pkg"
)
func (p *CryptoPlugin) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 设置 CORS 头
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.Header().Set("Content-Type", "application/json")
// 获取 stream 参数
stream := r.URL.Query().Get("stream")
if stream == "" {
http.Error(w, "stream parameter is required", http.StatusBadRequest)
return
}
//判断 stream 是否存在
if !p.Server.Streams.Has(stream) {
http.Error(w, "stream not found", http.StatusNotFound)
return
}
keyConf, err := cryptopkg.ValidateAndCreateKey(p.IsStatic, p.Algo, p.Secret.Key, p.Secret.Iv, stream)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// cryptor, err := method.GetCryptor(p.Algo, keyConf)
// if err != nil {
// http.Error(w, err.Error(), http.StatusBadRequest)
// return
// }
// w.Write([]byte(cryptor.GetKey()))
w.Write([]byte(fmt.Sprintf("%s.%s", base64.RawStdEncoding.EncodeToString([]byte(keyConf.Key)), base64.RawStdEncoding.EncodeToString([]byte(keyConf.Iv)))))
}

44
plugin/crypto/index.go Normal file
View File

@@ -0,0 +1,44 @@
package plugin_crypto
import (
m7s "m7s.live/v5"
crypto "m7s.live/v5/plugin/crypto/pkg"
)
var _ = m7s.InstallPlugin[CryptoPlugin](crypto.NewTransform)
type CryptoPlugin struct {
m7s.Plugin
IsStatic bool `desc:"是否静态密钥" default:"false"`
Algo string `desc:"加密算法" default:"aes_ctr"` //加密算法
EncryptLen int `desc:"加密字节长度" default:"1024"` //加密字节长度
Secret struct {
Key string `desc:"加密密钥" default:"your key"` //加密密钥
Iv string `desc:"加密向量" default:"your iv"` //加密向量
} `desc:"密钥配置"`
}
// OnInit 初始化插件时的回调函数
func (p *CryptoPlugin) OnInit() (err error) {
// 初始化全局配置
crypto.GlobalConfig = crypto.Config{
IsStatic: p.IsStatic,
Algo: p.Algo,
EncryptLen: p.EncryptLen,
Secret: struct {
Key string `desc:"加密密钥" default:"your key"`
Iv string `desc:"加密向量" default:"your iv"`
}{
Key: p.Secret.Key,
Iv: p.Secret.Iv,
},
}
p.Info("crypto config initialized",
"algo", p.Algo,
"isStatic", p.IsStatic,
"encryptLen", p.EncryptLen,
)
return nil
}

View File

@@ -0,0 +1,31 @@
package crypto
import (
"encoding/base64"
"io"
"net/http"
"strings"
"testing"
)
func TestGetKey(t *testing.T) {
stream := "/hdl/live/test0.flv"
host := "http://localhost:8080/crypto/?stream="
r, err := http.DefaultClient.Get(host + stream)
if err != nil {
t.Error("get", err)
return
}
b, err := io.ReadAll(r.Body)
if err != nil {
t.Error("read", err)
return
}
b64 := strings.Split(string(b), ".")
key, err := base64.RawStdEncoding.DecodeString(b64[0])
t.Log("key", key, err)
iv, err := base64.RawStdEncoding.DecodeString(b64[1])
t.Log("iv", iv, err)
}

View File

@@ -0,0 +1,99 @@
package method
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"errors"
)
//加密过程:
// 1、处理数据对数据进行填充采用PKCS7当密钥长度不够时缺几位补几个几的方式。
// 2、对数据进行加密采用AES加密方法中CBC加密模式
// 3、对得到的加密数据进行base64加密得到字符串
// 解密过程相反
// AesEncrypt 加密 cbc 模式
type AesCryptor struct {
key []byte
}
func newAesCbc(cfg Key) (ICryptor, error) {
var cryptor *AesCryptor
if cfg.Key == "" {
return nil, errors.New("aes cryptor config no key")
} else {
cryptor = &AesCryptor{key: []byte(cfg.Key)}
}
return cryptor, nil
}
func init() {
RegisterCryptor("aes_cbc", newAesCbc)
}
func (c *AesCryptor) Encrypt(origin []byte) ([]byte, error) {
//创建加密实例
block, err := aes.NewCipher(c.key)
if err != nil {
return nil, err
}
//判断加密快的大小
blockSize := block.BlockSize()
//填充
encryptBytes := pkcs7Padding(origin, blockSize)
//初始化加密数据接收切片
crypted := make([]byte, len(encryptBytes))
//使用cbc加密模式
blockMode := cipher.NewCBCEncrypter(block, c.key[:blockSize])
//执行加密
blockMode.CryptBlocks(crypted, encryptBytes)
return crypted, nil
}
func (c *AesCryptor) Decrypt(encrypted []byte) ([]byte, error) {
//创建实例
block, err := aes.NewCipher(c.key)
if err != nil {
return nil, err
}
//获取块的大小
blockSize := block.BlockSize()
//使用cbc
blockMode := cipher.NewCBCDecrypter(block, c.key[:blockSize])
//初始化解密数据接收切片
crypted := make([]byte, len(encrypted))
//执行解密
blockMode.CryptBlocks(crypted, encrypted)
//去除填充
crypted, err = pkcs7UnPadding(crypted)
if err != nil {
return nil, err
}
return crypted, nil
}
func (c *AesCryptor) GetKey() string {
return base64.RawStdEncoding.EncodeToString(c.key)
}
// pkcs7Padding 填充
func pkcs7Padding(data []byte, blockSize int) []byte {
//判断缺少几位长度。最少1最多 blockSize
padding := blockSize - len(data)%blockSize
//补足位数。把切片[]byte{byte(padding)}复制padding个
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
// pkcs7UnPadding 填充的反向操作
func pkcs7UnPadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, errors.New("加密字符串错误!")
}
//获取填充的个数
unPadding := int(data[length-1])
return data[:(length - unPadding)], nil
}

View File

@@ -0,0 +1,61 @@
package method
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"errors"
"fmt"
)
type AesCtrCryptor struct {
key []byte
iv []byte
}
func newAesCtr(cfg Key) (ICryptor, error) {
var cryptor *AesCtrCryptor
if cfg.Key == "" || cfg.Iv == "" {
return nil, errors.New("aes ctr cryptor config no key")
}
cryptor = &AesCtrCryptor{key: []byte(cfg.Key), iv: []byte(cfg.Iv)}
return cryptor, nil
}
func init() {
RegisterCryptor("aes_ctr", newAesCtr)
}
func (c *AesCtrCryptor) Encrypt(origin []byte) ([]byte, error) {
block, err := aes.NewCipher(c.key)
if err != nil {
panic(err)
}
aesCtr := cipher.NewCTR(block, c.iv)
// EncryptRaw the plaintext
ciphertext := make([]byte, len(origin))
aesCtr.XORKeyStream(ciphertext, origin)
return ciphertext, nil
}
func (c *AesCtrCryptor) Decrypt(encrypted []byte) ([]byte, error) {
block, err := aes.NewCipher(c.key)
if err != nil {
panic(err)
}
aesCtr := cipher.NewCTR(block, c.iv)
// Decrypt the ciphertext
plaintext := make([]byte, len(encrypted))
aesCtr.XORKeyStream(plaintext, encrypted)
return plaintext, nil
}
func (c *AesCtrCryptor) GetKey() string {
return fmt.Sprintf("%s.%s", base64.RawStdEncoding.EncodeToString(c.key), base64.RawStdEncoding.EncodeToString(c.iv))
}

View File

@@ -0,0 +1,28 @@
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Aes256Ctr {
public static byte[] decrypt(byte[] ciphertext, byte[] key, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
return cipher.doFinal(ciphertext);
}
public static void main(String[] args) throws Exception {
int[] intArray = {253, 72, 209, 96, 36};
byte[] ciphertext = new byte[intArray.length];
for (int i = 0; i < intArray.length; i++) {
ciphertext[i] = (byte) intArray[i];
}
// byte[] ciphertext = //byte[]{253,72,209,96,36]; // ciphertext to be decrypted
byte[] key = "01234567012345670123456701234567".getBytes();// 256-bit key
byte[] iv = "0123456701234567".getBytes();// initialization vector
byte[] plaintext = decrypt(ciphertext, key, iv);
System.out.println(new String(plaintext, "UTF-8"));
}
}

View File

@@ -0,0 +1,12 @@
const crypto = require('crypto');
const key = Buffer.from('01234567012345670123456701234567', 'utf8');
console.log(key)
const nonce = Buffer.from('0123456701234567', 'utf8');
console.log(nonce)
const ciphertext = Buffer.from([253,72,209,96,36]);
const decipher = crypto.createDecipheriv('aes-256-ctr', key, nonce);
const plaintext = decipher.update(ciphertext);
const finalPlaintext = decipher.final();
console.log(Buffer.concat([plaintext, finalPlaintext]).toString());

View File

@@ -0,0 +1,14 @@
var aesjs = require('aes-js');
let ciphertext = Uint8Array.from([253, 72, 209, 96, 36]);
let key = aesjs.utils.utf8.toBytes('01234567012345670123456701234567');
console.log(key)
let iv = aesjs.utils.utf8.toBytes('0123456701234567');
console.log(iv)
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(iv));
var decryptedBytes = aesCtr.decrypt(ciphertext);
console.log(decryptedBytes)
console.log(aesjs.utils.utf8.fromBytes(decryptedBytes))

View File

@@ -0,0 +1,13 @@
const crypto = require('crypto');
let key = Buffer.from('01234567012345670123456701234567', 'utf8');
console.log(key)
let iv = Buffer.from('0123456701234567', 'utf8');
console.log(iv)
let ciphertext = Buffer.from([253,72,209,96,36]);
const decipher = crypto.createDecipheriv('aes-256-ctr', key, iv);
const plaintext = decipher.update(ciphertext);
const finalPlaintext = decipher.final();
console.log(Buffer.concat([plaintext, finalPlaintext]).toString());

View File

@@ -0,0 +1,92 @@
package method
import (
"encoding/base64"
"testing"
)
func TestStream(t *testing.T) {
encKey, _ := CreateKey(32)
macKey, _ := CreateKey(32)
plaintext := "0123456789012345"
pt := []byte(plaintext)
var cfg Key
cfg.EncKey = string(encKey)
cfg.MacKey = string(macKey)
c, _ := GetCryptor("stream", cfg)
t.Log("key", c.GetKey())
encryptData, err := c.Encrypt(pt)
t.Log("stream encrypt base64", base64.RawStdEncoding.EncodeToString(encryptData), err)
decryptData, err := c.Decrypt(encryptData)
t.Log("stream decrypt", string(decryptData), err)
if string(decryptData) != plaintext {
t.Error("decrypt error")
}
}
func TestAesCbc(t *testing.T) {
encKey, _ := CreateKey(16)
plaintext := "0123456789012345"
pt := []byte(plaintext)
var cfg Key
cfg.Key = string(encKey)
c, _ := GetCryptor("aes_cbc", cfg)
t.Log(c.GetKey())
encryptData, err := c.Encrypt(pt)
t.Log("aes_cbc encrypt base64", base64.RawStdEncoding.EncodeToString(encryptData), err)
decryptData, err := c.Decrypt(encryptData)
t.Log("aes_cbc decrypt", string(decryptData), err)
if string(decryptData) != plaintext {
t.Error("decrypt error")
}
}
func TestAesCtr(t *testing.T) {
encKey, _ := CreateKey(32)
iv, _ := CreateKey(16)
plaintext := "0123456789012345"
pt := []byte(plaintext)
var cfg Key
cfg.Key = string(encKey)
cfg.Iv = string(iv)
c, _ := GetCryptor("aes_ctr", cfg)
t.Log(c.GetKey())
encryptData, err := c.Encrypt(pt)
t.Log("aes_ctr encrypt ", string(encryptData), err)
decryptData, err := c.Decrypt(encryptData)
t.Log("aes_ctr decrypt", string(decryptData), err)
if string(decryptData) != plaintext {
t.Error("decrypt error")
}
}
func TestXor(t *testing.T) {
encKey, _ := CreateKey(32)
iv, _ := CreateKey(16)
plaintext := "0123456789012345"
pt := []byte(plaintext)
var cfg Key
cfg.Key = string(encKey)
cfg.Iv = string(iv)
c, _ := GetCryptor("xor", cfg)
t.Log(c.GetKey())
encryptData, err := c.Encrypt(pt)
t.Log("xor encrypt ", string(encryptData), "len", len(string(encryptData)), err)
decryptData, err := c.Decrypt(encryptData)
t.Log("xor decrypt", string(decryptData), err)
if string(decryptData) != plaintext {
t.Error("decrypt error")
}
}

View File

@@ -0,0 +1,51 @@
package method
import (
"crypto/md5"
"crypto/rand"
"encoding/hex"
"fmt"
)
type ICryptor interface {
Encrypt(origin []byte) ([]byte, error)
Decrypt(encrypted []byte) ([]byte, error)
GetKey() string // 获取密钥 格式base64(key).base64(iv)
}
const (
CryptoEncrypt = iota + 1
CryptoDecrypt
)
type CryptoBuilder func(cfg Key) (ICryptor, error)
var (
builders = make(map[string]CryptoBuilder)
)
func RegisterCryptor(name string, builder CryptoBuilder) {
builders[name] = builder
}
func GetCryptor(cryptor string, cfg Key) (ICryptor, error) {
builder, exists := builders[cryptor]
if !exists {
return nil, fmt.Errorf("Unknown ICryptor %q", cryptor)
}
return builder(cfg)
}
func CreateKey(keySize int) ([]byte, error) {
key := make([]byte, keySize)
_, err := rand.Read(key)
if err != nil {
return nil, err
}
return key, nil
}
func Md5Sum(s string) string {
ret := md5.Sum([]byte(s))
return hex.EncodeToString(ret[:])
}

View File

@@ -0,0 +1,6 @@
{
"dependencies": {
"aes-js": "^3.1.2",
"crypto-js": "^4.1.1"
}
}

View File

@@ -0,0 +1,184 @@
package method
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"hash"
"io"
)
type Key struct {
Key string
Iv string
EncKey string
MacKey string
}
func init() {
RegisterCryptor("stream", newStream)
}
type StreamCryptor struct {
enckey []byte
macKey []byte
encrypter *StreamEncrypter `yaml:"-"`
decrypter *StreamDecrypter `json:"-"`
}
func NewStreamEncrypter(encKey, macKey []byte) (*StreamEncrypter, error) {
block, err := aes.NewCipher(encKey)
if err != nil {
return nil, err
}
iv := make([]byte, block.BlockSize())
_, err = rand.Read(iv)
if err != nil {
return nil, err
}
stream := cipher.NewCTR(block, iv)
mac := hmac.New(sha256.New, macKey)
return &StreamEncrypter{
Block: block,
Stream: stream,
Mac: mac,
IV: iv,
}, nil
}
func NewStreamDecrypter(encKey, macKey []byte, meta StreamMeta) (*StreamDecrypter, error) {
block, err := aes.NewCipher(encKey)
if err != nil {
return nil, err
}
stream := cipher.NewCTR(block, meta.IV)
mac := hmac.New(sha256.New, macKey)
return &StreamDecrypter{
Block: block,
Stream: stream,
Mac: mac,
Meta: meta,
}, nil
}
type StreamMeta struct {
// IV is the initial value for the crypto function
IV []byte
// Hash is the sha256 hmac of the stream
Hash []byte
}
type StreamEncrypter struct {
Source io.Reader
Block cipher.Block
Stream cipher.Stream
Mac hash.Hash
IV []byte
}
// StreamDecrypter is a decrypter for a stream of data with authentication
type StreamDecrypter struct {
Source io.Reader
Block cipher.Block
Stream cipher.Stream
Mac hash.Hash
Meta StreamMeta
}
// Read encrypts the bytes of the inner reader and places them into p
func (s *StreamEncrypter) Read(p []byte) (int, error) {
n, readErr := s.Source.Read(p)
if n > 0 {
s.Stream.XORKeyStream(p[:n], p[:n])
err := writeHash(s.Mac, p[:n])
if err != nil {
return n, err
}
return n, readErr
}
return 0, io.EOF
}
// Meta returns the encrypted stream metadata for use in decrypting. This should only be called after the stream is finished
func (s *StreamEncrypter) Meta() StreamMeta {
return StreamMeta{IV: s.IV, Hash: s.Mac.Sum(nil)}
}
// Read reads bytes from the underlying reader and then decrypts them
func (s *StreamDecrypter) Read(p []byte) (int, error) {
n, readErr := s.Source.Read(p)
if n > 0 {
err := writeHash(s.Mac, p[:n])
if err != nil {
return n, err
}
s.Stream.XORKeyStream(p[:n], p[:n])
return n, readErr
}
return 0, io.EOF
}
func newStream(cfg Key) (ICryptor, error) {
var cryptor *StreamCryptor
if (cfg.EncKey == "") || (cfg.MacKey == "") {
return nil, errors.New("stream cryptor config not enckey or mackey")
} else {
encKey := []byte(cfg.EncKey)
macKey := []byte(cfg.MacKey)
encrypter, err := NewStreamEncrypter(encKey, macKey)
if err != nil {
return nil, err
}
decrypter, err := NewStreamDecrypter(encKey, macKey, encrypter.Meta())
if err != nil {
return nil, err
}
cryptor = &StreamCryptor{
enckey: encKey,
macKey: macKey,
encrypter: encrypter,
decrypter: decrypter,
}
}
return cryptor, nil
}
func (c *StreamCryptor) Encrypt(origin []byte) ([]byte, error) {
c.encrypter.Source = bytes.NewReader(origin)
return io.ReadAll(c.encrypter)
}
func (c *StreamCryptor) Decrypt(encrypted []byte) ([]byte, error) {
c.decrypter.Source = bytes.NewReader(encrypted)
return io.ReadAll(c.decrypter)
}
func (c *StreamCryptor) GetKey() string {
b64 := base64.RawStdEncoding
return fmt.Sprintf("%s.%s.%s.%s",
b64.EncodeToString(c.enckey),
b64.EncodeToString(c.macKey),
b64.EncodeToString(c.encrypter.IV),
b64.EncodeToString(c.encrypter.Mac.Sum(nil)),
)
}
func writeHash(mac hash.Hash, p []byte) error {
m, err := mac.Write(p)
if err != nil {
return err
}
if m != len(p) {
return errors.New("could not write all bytes to hmac")
}
return nil
}

100
plugin/crypto/pkg/method/xor.go Executable file
View File

@@ -0,0 +1,100 @@
package method
import (
"crypto/subtle"
"encoding/base64"
"errors"
)
// SimpleXorCryptor 加密一次
type SimpleXorCryptor struct {
key []byte
}
func newSimpleXor(cfg Key) (ICryptor, error) {
var cryptor *SimpleXorCryptor
if cfg.Key == "" {
return nil, errors.New("xor cryptor config no key")
} else {
cryptor = &SimpleXorCryptor{key: []byte(cfg.Key)}
}
return cryptor, nil
}
// simpleXorEncryptDecrypt 对给定的字节数组进行 XOR 加密和解密
// key 是用于加密和解密的密钥
func simpleXorEncryptDecrypt(data []byte, key []byte) []byte {
dataLen := len(data)
result := make([]byte, dataLen)
keyLen := len(key)
for i := 0; i < dataLen; i += keyLen {
end := i + keyLen
if end > dataLen {
end = dataLen
}
subtle.XORBytes(result[i:end], data[i:end], key[:end-i])
}
return result
}
func (c *SimpleXorCryptor) Encrypt(origin []byte) ([]byte, error) {
return simpleXorEncryptDecrypt(origin, c.key), nil
}
func (c *SimpleXorCryptor) Decrypt(encrypted []byte) ([]byte, error) {
return simpleXorEncryptDecrypt(encrypted, c.key), nil
}
func (c *SimpleXorCryptor) GetKey() string {
return base64.RawStdEncoding.EncodeToString(c.key)
}
// 复杂的XOR加密器 加密两次
type ComplexXorCryptor struct {
key []byte
iv []byte
}
func newComplexXor(cfg Key) (ICryptor, error) {
var cryptor *ComplexXorCryptor
if cfg.Key == "" {
return nil, errors.New("xor cryptor config no key")
} else {
cryptor = &ComplexXorCryptor{key: []byte(cfg.Key), iv: []byte(cfg.Iv)}
}
return cryptor, nil
}
// complexXorEncryptDecrypt 对给定的字节数组进行 XOR 加密和解密
func complexXorEncryptDecrypt(arrayBuffer, key, iv []byte) []byte {
// Assuming the key and iv have been provided and are not nil
if key == nil || iv == nil {
panic("key and iv must not be nil")
}
result := make([]byte, len(arrayBuffer))
keyLen := len(key)
ivLen := len(iv)
for i := 0; i < len(result); i++ {
result[i] = arrayBuffer[i] ^ (key[i%keyLen] ^ iv[i%ivLen])
}
return result
}
func (c *ComplexXorCryptor) Encrypt(origin []byte) ([]byte, error) {
return complexXorEncryptDecrypt(origin, c.key, c.iv), nil
}
func (c *ComplexXorCryptor) Decrypt(encrypted []byte) ([]byte, error) {
return complexXorEncryptDecrypt(encrypted, c.key, c.iv), nil
}
func (c *ComplexXorCryptor) GetKey() string {
return base64.RawStdEncoding.EncodeToString(c.key) + "." + base64.RawStdEncoding.EncodeToString(c.iv)
}
func init() {
RegisterCryptor("xor_s", newSimpleXor)
RegisterCryptor("xor_c", newComplexXor)
}

View File

@@ -0,0 +1,177 @@
package crypto
import (
"github.com/deepch/vdk/codec/h265parser"
"m7s.live/v5/pkg"
"m7s.live/v5/pkg/codec"
"m7s.live/v5/pkg/task"
"fmt"
m7s "m7s.live/v5"
"m7s.live/v5/plugin/crypto/pkg/method"
)
// GlobalConfig 全局加密配置
var GlobalConfig Config
type Config struct {
IsStatic bool `desc:"是否静态密钥" default:"false"`
Algo string `desc:"加密算法" default:"aes_ctr"` //加密算法
EncryptLen int `desc:"加密字节长度" default:"1024"` //加密字节长度
Secret struct {
Key string `desc:"加密密钥" default:"your key"` //加密密钥
Iv string `desc:"加密向量" default:"your iv"` //加密向量
} `desc:"密钥配置"`
}
type Transform struct {
m7s.DefaultTransformer
cryptor method.ICryptor
}
func NewTransform() m7s.ITransformer {
ret := &Transform{}
ret.SetDescription(task.OwnerTypeKey, "Crypto")
return ret
}
// ValidateAndCreateKey 验证并创建加密密钥
func ValidateAndCreateKey(isStatic bool, algo string, secretKey, secretIv, streamPath string) (keyConf method.Key, err error) {
if isStatic {
switch algo {
case "aes_ctr":
keyConf.Key = secretKey
keyConf.Iv = secretIv
if len(keyConf.Iv) != 16 || len(keyConf.Key) != 32 {
return keyConf, fmt.Errorf("key or iv length is wrong")
}
case "xor_s":
keyConf.Key = secretKey
if len(keyConf.Key) != 32 {
return keyConf, fmt.Errorf("key length is wrong")
}
case "xor_c":
keyConf.Key = secretKey
keyConf.Iv = secretIv
if len(keyConf.Iv) != 16 || len(keyConf.Key) != 32 {
return keyConf, fmt.Errorf("key or iv length is wrong")
}
default:
return keyConf, fmt.Errorf("algo type is wrong")
}
} else {
/*
动态加密
key = md5(密钥+流名称)
iv = md5(流名称)前一半
*/
if secretKey != "" {
keyConf.Key = method.Md5Sum(secretKey + streamPath)
keyConf.Iv = method.Md5Sum(streamPath)[:16]
} else {
return keyConf, fmt.Errorf("secret key is empty")
}
}
return
}
func (t *Transform) Start() error {
// 在 Start 时获取并保存配置
t.Info("transform job started")
keyConf, err := ValidateAndCreateKey(GlobalConfig.IsStatic, GlobalConfig.Algo, GlobalConfig.Secret.Key, GlobalConfig.Secret.Iv, t.TransformJob.StreamPath)
if err != nil {
return err
}
t.cryptor, err = method.GetCryptor(GlobalConfig.Algo, keyConf)
if err != nil {
t.Error("failed to create cryptor", "error", err)
return err
}
// 使用 TransformJob 的 Subscribe 方法订阅流
if err := t.TransformJob.Subscribe(); err != nil {
t.Error("failed to subscribe stream", "error", err)
return err
}
t.Info("crypto transform started",
"stream", t.TransformJob.StreamPath,
"algo", GlobalConfig.Algo,
"isStatic", GlobalConfig.IsStatic,
)
return nil
}
func (t *Transform) Go() error {
// 创建发布者
if err := t.TransformJob.Publish(t.TransformJob.StreamPath + "/crypto"); err != nil {
t.Error("failed to create publisher", "error", err)
return err
}
// 处理音视频流
return m7s.PlayBlock(t.TransformJob.Subscriber,
func(audio *pkg.RawAudio) (err error) {
copyAudio := &pkg.RawAudio{
FourCC: audio.FourCC,
Timestamp: audio.Timestamp,
}
audio.Memory.Range(func(b []byte) {
copy(copyAudio.NextN(len(b)), b)
})
return t.TransformJob.Publisher.WriteAudio(copyAudio)
},
func(video *pkg.H26xFrame) error {
// 处理视频帧
if video.GetSize() == 0 {
return nil
}
copyVideo := &pkg.H26xFrame{
FourCC: video.FourCC,
CTS: video.CTS,
Timestamp: video.Timestamp,
}
for _, nalu := range video.Nalus {
mem := copyVideo.NextN(nalu.Size)
copy(mem, nalu.ToBytes())
needEncrypt := false
if video.FourCC == codec.FourCC_H264 {
switch codec.ParseH264NALUType(mem[0]) {
case codec.NALU_Non_IDR_Picture, codec.NALU_IDR_Picture:
needEncrypt = true
}
} else if video.FourCC == codec.FourCC_H265 {
switch codec.ParseH265NALUType(mem[0]) {
case h265parser.NAL_UNIT_CODED_SLICE_BLA_W_LP,
h265parser.NAL_UNIT_CODED_SLICE_BLA_W_RADL,
h265parser.NAL_UNIT_CODED_SLICE_BLA_N_LP,
h265parser.NAL_UNIT_CODED_SLICE_IDR_W_RADL,
h265parser.NAL_UNIT_CODED_SLICE_IDR_N_LP,
h265parser.NAL_UNIT_CODED_SLICE_CRA:
needEncrypt = true
}
}
if needEncrypt {
if encBytes, err := t.cryptor.Encrypt(mem); err == nil {
copyVideo.Nalus.Append(encBytes)
} else {
copyVideo.Nalus.Append(mem)
}
} else {
copyVideo.Nalus.Append(mem)
}
}
return t.TransformJob.Publisher.WriteVideo(copyVideo)
})
}
func (t *Transform) Dispose() {
t.Info("crypto transform disposed",
"stream", t.TransformJob.StreamPath,
)
}