mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-10-06 02:46:50 +08:00
feat: add crypto
This commit is contained in:
@@ -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
|
@@ -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
43
plugin/crypto/api.go
Normal 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
44
plugin/crypto/index.go
Normal 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
|
||||||
|
}
|
31
plugin/crypto/pkg/getkey_test.go
Executable file
31
plugin/crypto/pkg/getkey_test.go
Executable 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)
|
||||||
|
}
|
99
plugin/crypto/pkg/method/aes_cbc.go
Executable file
99
plugin/crypto/pkg/method/aes_cbc.go
Executable 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
|
||||||
|
}
|
61
plugin/crypto/pkg/method/aes_ctr.go
Executable file
61
plugin/crypto/pkg/method/aes_ctr.go
Executable 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))
|
||||||
|
}
|
28
plugin/crypto/pkg/method/aes_ctr.java
Executable file
28
plugin/crypto/pkg/method/aes_ctr.java
Executable 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"));
|
||||||
|
}
|
||||||
|
}
|
12
plugin/crypto/pkg/method/aes_ctr.js
Executable file
12
plugin/crypto/pkg/method/aes_ctr.js
Executable 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());
|
14
plugin/crypto/pkg/method/aes_ctr_browser.js
Executable file
14
plugin/crypto/pkg/method/aes_ctr_browser.js
Executable 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))
|
13
plugin/crypto/pkg/method/aes_ctr_node.js
Executable file
13
plugin/crypto/pkg/method/aes_ctr_node.js
Executable 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());
|
||||||
|
|
92
plugin/crypto/pkg/method/cryptor_test.go
Executable file
92
plugin/crypto/pkg/method/cryptor_test.go
Executable 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")
|
||||||
|
}
|
||||||
|
}
|
51
plugin/crypto/pkg/method/icrypto.go
Executable file
51
plugin/crypto/pkg/method/icrypto.go
Executable 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[:])
|
||||||
|
}
|
6
plugin/crypto/pkg/method/package.json
Executable file
6
plugin/crypto/pkg/method/package.json
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"aes-js": "^3.1.2",
|
||||||
|
"crypto-js": "^4.1.1"
|
||||||
|
}
|
||||||
|
}
|
184
plugin/crypto/pkg/method/stream.go
Executable file
184
plugin/crypto/pkg/method/stream.go
Executable 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
100
plugin/crypto/pkg/method/xor.go
Executable 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)
|
||||||
|
}
|
177
plugin/crypto/pkg/transform.go
Normal file
177
plugin/crypto/pkg/transform.go
Normal 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,
|
||||||
|
)
|
||||||
|
}
|
Reference in New Issue
Block a user