From e474a62dbc8ee3c91e71d7c5519d0d94dd04d4a6 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Mon, 23 Oct 2023 10:56:36 +0200 Subject: [PATCH] Add hindi #119 + allow recordings encryption + decryption tooling. --- README.md | 17 ++- machinery/main.go | 15 +++ machinery/src/capture/main.go | 22 ++++ machinery/src/config/main.go | 16 ++- machinery/src/encryption/main.go | 42 +++---- machinery/src/models/Config.go | 5 +- machinery/src/models/MQTT.go | 22 ++-- machinery/src/routers/mqtt/main.go | 23 +++- machinery/src/utils/main.go | 65 +++++++++++ ui/public/locales/de/translation.json | 11 +- ui/public/locales/en/translation.json | 11 +- ui/public/locales/es/translation.json | 11 +- ui/public/locales/fr/translation.json | 11 +- ui/public/locales/hi/translation.json | 11 +- ui/public/locales/it/translation.json | 11 +- ui/public/locales/ja/translation.json | 11 +- ui/public/locales/nl/translation.json | 11 +- ui/public/locales/pl/translation.json | 11 +- ui/public/locales/pt/translation.json | 11 +- ui/public/locales/zh/translation.json | 11 +- .../LanguageSelect/LanguageSelect.jsx | 1 + ui/src/pages/Settings/Settings.jsx | 104 ++++++++++++++++-- 22 files changed, 392 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 058373a..4ba689b 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,20 @@ The default username and password for the Kerberos Agent is: **_Please note that you change the username and password for a final installation, see [Configure with environment variables](#configure-with-environment-variables) below._** +## Encryption + +You can encrypt your recordings and outgoing MQTT messages with your own AES and RSA keys by enabling the encryption settings. Once enabled all your recordings will be encrypted using AES-256-CBC and your symmetric key. You can either use the default `openssl` toolchain to decrypt the recordings with your AES key, as following: + + openssl aes-256-cbc -d -md md5 -in encrypted.mp4 -out decrypted.mp4 -k your-key-96ab185xxxxxxxcxxxxxxxx6a59c62e8 + +, and additionally you can decrypt a folder of recordings, using the Kerberos Agent binary as following: + + go run main.go -action decrypt ./data/recordings your-key-96ab185xxxxxxxcxxxxxxxx6a59c62e8 + +or for a single file: + + go run main.go -action decrypt ./data/recordings/video.mp4 your-key-96ab185xxxxxxxcxxxxxxxx6a59c62e8 + ## Configure and persist with volume mounts An example of how to mount a host directory is shown below using `docker`, but is applicable for [all the deployment models and tools described above](#running-and-automating-a-kerberos-agent). @@ -227,7 +241,8 @@ Next to attaching the configuration file, it is also possible to override the co | `AGENT_KERBEROSVAULT_DIRECTORY` | The directory, in the provider, where the recordings will be stored in. | "" | | `AGENT_DROPBOX_ACCESS_TOKEN` | The Access Token from your Dropbox app, that is used to leverage the Dropbox SDK. | "" | | `AGENT_DROPBOX_DIRECTORY` | The directory, in the provider, where the recordings will be stored in. | "" | -| `AGENT_ENCRYPTION` | Enable 'true' or disable 'false' end-to-end encryption through MQTT (recordings will follow). | "false" | +| `AGENT_ENCRYPTION` | Enable 'true' or disable 'false' end-to-end encryption for MQTT messages. | "false" | +| `AGENT_ENCRYPTION_RECORDINGS` | Enable 'true' or disable 'false' end-to-end encryption for recordings. | "false" | | `AGENT_ENCRYPTION_FINGERPRINT` | The fingerprint of the keypair (public/private keys), so you know which one to use. | "" | | `AGENT_ENCRYPTION_PRIVATE_KEY` | The private key (assymetric/RSA) to decryptand sign requests send over MQTT. | "" | | `AGENT_ENCRYPTION_SYMMETRIC_KEY` | The symmetric key (AES) to encrypt and decrypt request send over MQTT. | "" | diff --git a/machinery/main.go b/machinery/main.go index 6855d9e..eb88d39 100644 --- a/machinery/main.go +++ b/machinery/main.go @@ -78,6 +78,21 @@ func main() { case "discover": log.Log.Info(timeout) + case "decrypt": + log.Log.Info("Decrypting: " + flag.Arg(0) + " with key: " + flag.Arg(1)) + symmetricKey := []byte(flag.Arg(1)) + + if symmetricKey == nil || len(symmetricKey) == 0 { + log.Log.Fatal("Main: symmetric key should not be empty") + return + } + if len(symmetricKey) != 32 { + log.Log.Fatal("Main: symmetric key should be 32 bytes") + return + } + + utils.Decrypt(flag.Arg(0), symmetricKey) + case "run": { // Print Kerberos.io ASCII art diff --git a/machinery/src/capture/main.go b/machinery/src/capture/main.go index 82ca5d2..ff2512d 100644 --- a/machinery/src/capture/main.go +++ b/machinery/src/capture/main.go @@ -8,6 +8,7 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/kerberos-io/agent/machinery/src/encryption" "github.com/kerberos-io/agent/machinery/src/log" "github.com/kerberos-io/agent/machinery/src/models" "github.com/kerberos-io/agent/machinery/src/utils" @@ -405,6 +406,27 @@ func HandleRecordStream(queue *pubsub.Queue, configDirectory string, configurati utils.CreateFragmentedMP4(fullName, config.Capture.FragmentedDuration) } + // Check if we need to encrypt the recording. + if config.Encryption != nil && config.Encryption.Enabled == "true" && config.Encryption.Recordings == "true" && config.Encryption.SymmetricKey != "" { + // reopen file into memory 'fullName' + contents, err := os.ReadFile(fullName) + if err == nil { + // encrypt + encryptedContents, err := encryption.AesEncrypt(contents, config.Encryption.SymmetricKey) + if err == nil { + // write back to file + err := os.WriteFile(fullName, []byte(encryptedContents), 0644) + if err != nil { + log.Log.Error("HandleRecordStream: error writing file: " + err.Error()) + } + } else { + log.Log.Error("HandleRecordStream: error encrypting file: " + err.Error()) + } + } else { + log.Log.Error("HandleRecordStream: error reading file: " + err.Error()) + } + } + // Create a symbol linc. fc, _ := os.Create(configDirectory + "/data/cloud/" + name) fc.Close() diff --git a/machinery/src/config/main.go b/machinery/src/config/main.go index 03b6bda..5a97a83 100644 --- a/machinery/src/config/main.go +++ b/machinery/src/config/main.go @@ -464,11 +464,10 @@ func OverrideWithEnvironmentVariables(configuration *models.Configuration) { /* When encryption is enabled */ case "AGENT_ENCRYPTION": - if value == "true" { - configuration.Config.Encryption.Enabled = true - } else { - configuration.Config.Encryption.Enabled = false - } + configuration.Config.Encryption.Enabled = value + break + case "AGENT_ENCRYPTION_RECORDINGS": + configuration.Config.Encryption.Recordings = value break case "AGENT_ENCRYPTION_FINGERPRINT": configuration.Config.Encryption.Fingerprint = value @@ -510,6 +509,13 @@ func SaveConfig(configDirectory string, config models.Config, configuration *mod } func StoreConfig(configDirectory string, config models.Config) error { + + // Encryption key can be set wrong. + encryptionPrivateKey := config.Encryption.PrivateKey + // Replace \\n by \n + encryptionPrivateKey = strings.ReplaceAll(encryptionPrivateKey, "\\n", "\n") + config.Encryption.PrivateKey = encryptionPrivateKey + // Save into database if os.Getenv("DEPLOYMENT") == "factory" || os.Getenv("MACHINERY_ENVIRONMENT") == "kubernetes" { // Write to mongodb diff --git a/machinery/src/encryption/main.go b/machinery/src/encryption/main.go index 6713fb9..3d4e8a1 100644 --- a/machinery/src/encryption/main.go +++ b/machinery/src/encryption/main.go @@ -38,58 +38,58 @@ func SignWithPrivateKey(data []byte, privateKey *rsa.PrivateKey) ([]byte, error) return signature, err } -func AesEncrypt(content string, password string) (string, error) { +func AesEncrypt(content []byte, password string) ([]byte, error) { salt := make([]byte, 8) _, err := rand.Read(salt) if err != nil { - return "", err + return nil, err } key, iv, err := DefaultEvpKDF([]byte(password), salt) block, err := aes.NewCipher(key) if err != nil { - return "", err + return nil, err } mode := cipher.NewCBCEncrypter(block, iv) - cipherBytes := PKCS5Padding([]byte(content), aes.BlockSize) + cipherBytes := PKCS5Padding(content, aes.BlockSize) mode.CryptBlocks(cipherBytes, cipherBytes) - data := make([]byte, 16+len(cipherBytes)) - copy(data[:8], []byte("Salted__")) - copy(data[8:16], salt) - copy(data[16:], cipherBytes) + cipherText := make([]byte, 16+len(cipherBytes)) + copy(cipherText[:8], []byte("Salted__")) + copy(cipherText[8:16], salt) + copy(cipherText[16:], cipherBytes) - cipherText := base64.StdEncoding.EncodeToString(data) + //cipherText := base64.StdEncoding.EncodeToString(data) return cipherText, nil } -func AesDecrypt(cipherText string, password string) (string, error) { - data, err := base64.StdEncoding.DecodeString(cipherText) - if err != nil { - return "", err - } - if string(data[:8]) != "Salted__" { - return "", errors.New("invalid crypto js aes encryption") +func AesDecrypt(cipherText []byte, password string) ([]byte, error) { + //data, err := base64.StdEncoding.DecodeString(cipherText) + //if err != nil { + // return nil, err + //} + if string(cipherText[:8]) != "Salted__" { + return nil, errors.New("invalid crypto js aes encryption") } - salt := data[8:16] - cipherBytes := data[16:] + salt := cipherText[8:16] + cipherBytes := cipherText[16:] key, iv, err := DefaultEvpKDF([]byte(password), salt) if err != nil { - return "", err + return nil, err } block, err := aes.NewCipher(key) if err != nil { - return "", err + return nil, err } mode := cipher.NewCBCDecrypter(block, iv) mode.CryptBlocks(cipherBytes, cipherBytes) result := PKCS5UnPadding(cipherBytes) - return string(result), nil + return result, nil } // https://stackoverflow.com/questions/27677236/encryption-in-javascript-and-decryption-with-php/27678978#27678978 diff --git a/machinery/src/models/Config.go b/machinery/src/models/Config.go index 397ac85..d1392f4 100644 --- a/machinery/src/models/Config.go +++ b/machinery/src/models/Config.go @@ -42,7 +42,7 @@ type Config struct { HubPrivateKey string `json:"hub_private_key" bson:"hub_private_key"` HubSite string `json:"hub_site" bson:"hub_site"` ConditionURI string `json:"condition_uri" bson:"condition_uri"` - Encryption *Encryption `json:"encryption" bson:"encryption"` + Encryption *Encryption `json:"encryption,omitempty" bson:"encryption",omitempty` } // Capture defines which camera type (Id) you are using (IP, USB or Raspberry Pi camera), @@ -161,7 +161,8 @@ type Dropbox struct { // Encryption type Encryption struct { - Enabled bool `json:"enabled" bson:"enabled"` + Enabled string `json:"enabled" bson:"enabled"` + Recordings string `json:"recordings" bson:"recordings"` Fingerprint string `json:"fingerprint" bson:"fingerprint"` PrivateKey string `json:"private_key" bson:"private_key"` SymmetricKey string `json:"symmetric_key" bson:"symmetric_key"` diff --git a/machinery/src/models/MQTT.go b/machinery/src/models/MQTT.go index 4e298af..b9760d8 100644 --- a/machinery/src/models/MQTT.go +++ b/machinery/src/models/MQTT.go @@ -30,7 +30,7 @@ func PackageMQTTMessage(configuration *Configuration, msg Message) ([]byte, erro // At the moment we don't do the encryption part, but we'll implement it // once the legacy methods (subscriptions are moved). msg.Encrypted = false - if configuration.Config.Encryption != nil && configuration.Config.Encryption.Enabled { + if configuration.Config.Encryption != nil && configuration.Config.Encryption.Enabled == "true" { msg.Encrypted = true } msg.PublicKey = "" @@ -65,15 +65,19 @@ func PackageMQTTMessage(configuration *Configuration, msg Message) ([]byte, erro // Create a 16bit key random k := configuration.Config.Encryption.SymmetricKey - encryptedValue, err := encryption.AesEncrypt(string(data), k) + encryptedValue, err := encryption.AesEncrypt(data, k) + if err == nil { - // Sign the encrypted value - signature, err := encryption.SignWithPrivateKey([]byte(encryptedValue), rsaKey) - base64Signature := base64.StdEncoding.EncodeToString(signature) - - msg.Payload.EncryptedValue = encryptedValue - msg.Payload.Signature = base64Signature - msg.Payload.Value = make(map[string]interface{}) + data := base64.StdEncoding.EncodeToString(encryptedValue) + // Sign the encrypted value + signature, err := encryption.SignWithPrivateKey([]byte(data), rsaKey) + if err == nil { + base64Signature := base64.StdEncoding.EncodeToString(signature) + msg.Payload.EncryptedValue = data + msg.Payload.Signature = base64Signature + msg.Payload.Value = make(map[string]interface{}) + } + } } } diff --git a/machinery/src/routers/mqtt/main.go b/machinery/src/routers/mqtt/main.go index c310b1e..1e3e4fa 100644 --- a/machinery/src/routers/mqtt/main.go +++ b/machinery/src/routers/mqtt/main.go @@ -3,6 +3,7 @@ package mqtt import ( "crypto/rsa" "crypto/x509" + "encoding/base64" "encoding/json" "encoding/pem" "fmt" @@ -168,7 +169,7 @@ func MQTTListenerHandler(mqttClient mqtt.Client, hubKey string, configDirectory // Messages might be encrypted, if so we'll // need to decrypt them. var payload models.Payload - if message.Encrypted && configuration.Config.Encryption != nil && configuration.Config.Encryption.Enabled { + if message.Encrypted && configuration.Config.Encryption != nil && configuration.Config.Encryption.Enabled == "true" { encryptedValue := message.Payload.EncryptedValue if len(encryptedValue) > 0 { symmetricKey := configuration.Config.Encryption.SymmetricKey @@ -198,12 +199,16 @@ func MQTTListenerHandler(mqttClient mqtt.Client, hubKey string, configDirectory if decryptedKey != nil { if string(decryptedKey) == symmetricKey { // Decrypt value with decryptedKey - decryptedValue, err := encryption.AesDecrypt(encryptedValue, string(decryptedKey)) + data, err := base64.StdEncoding.DecodeString(encryptedValue) + if err != nil { + return + } + decryptedValue, err := encryption.AesDecrypt(data, string(decryptedKey)) if err != nil { log.Log.Error("MQTTListenerHandler: error decrypting message: " + err.Error()) return } - json.Unmarshal([]byte(decryptedValue), &payload) + json.Unmarshal(decryptedValue, &payload) } else { log.Log.Error("MQTTListenerHandler: error decrypting message, assymetric keys do not match.") return @@ -333,10 +338,16 @@ func HandleRequestConfig(mqttClient mqtt.Client, hubKey string, payload models.P if key != "" && name != "" { + // Copy the config, as we don't want to share the encryption part. + deepCopy := configuration.Config + var configMap map[string]interface{} - inrec, _ := json.Marshal(configuration.Config) + inrec, _ := json.Marshal(deepCopy) json.Unmarshal(inrec, &configMap) + // Unset encryption part. + delete(configMap, "encryption") + message := models.Message{ Payload: models.Payload{ Action: "receive-config", @@ -370,6 +381,10 @@ func HandleUpdateConfig(mqttClient mqtt.Client, hubKey string, payload models.Pa if configPayload.Timestamp != 0 { config := configPayload.Config + + // Make sure to remove Encryption part, as we don't want to save it. + config.Encryption = configuration.Config.Encryption + err := configService.SaveConfig(configDirectory, config, configuration, communication) if err == nil { log.Log.Info("HandleUpdateConfig: Config updated") diff --git a/machinery/src/utils/main.go b/machinery/src/utils/main.go index 3a68670..045d0d0 100644 --- a/machinery/src/utils/main.go +++ b/machinery/src/utils/main.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "github.com/kerberos-io/agent/machinery/src/encryption" "github.com/kerberos-io/agent/machinery/src/log" "github.com/kerberos-io/agent/machinery/src/models" ) @@ -330,3 +331,67 @@ func PrintConfiguration(configuration *models.Configuration) { } log.Log.Info("Printing our configuration (config.json): " + configurationVariables) } + +func Decrypt(directoryOrFile string, symmetricKey []byte) { + // Check if file or directory + fileInfo, err := os.Stat(directoryOrFile) + if err != nil { + log.Log.Fatal(err.Error()) + return + } + + var files []string + if fileInfo.IsDir() { + // Create decrypted directory + err = os.MkdirAll(directoryOrFile+"/decrypted", 0755) + if err != nil { + log.Log.Fatal(err.Error()) + return + } + dir, err := os.ReadDir(directoryOrFile) + if err != nil { + log.Log.Fatal(err.Error()) + return + } + for _, file := range dir { + // Check if file is not a directory + if !file.IsDir() { + // Check if an mp4 file + if strings.HasSuffix(file.Name(), ".mp4") { + files = append(files, directoryOrFile+"/"+file.Name()) + } + } + } + } else { + files = append(files, directoryOrFile) + } + + // We'll loop over all files and decrypt them one by one. + for _, file := range files { + + // Read file + content, err := os.ReadFile(file) + if err != nil { + log.Log.Fatal(err.Error()) + return + } + // Decrypt using AES key + decrypted, err := encryption.AesDecrypt(content, string(symmetricKey)) + if err != nil { + log.Log.Fatal("Something went wrong while decrypting: " + err.Error()) + return + } + + // Write decrypted content to file with appended .decrypted + // Get filename split by / and get last element. + fileParts := strings.Split(file, "/") + fileName := fileParts[len(fileParts)-1] + pathToFile := strings.Join(fileParts[:len(fileParts)-1], "/") + + err = os.WriteFile(pathToFile+"/decrypted/"+fileName, []byte(decrypted), 0644) + if err != nil { + log.Log.Fatal(err.Error()) + return + } + } +} diff --git a/ui/public/locales/de/translation.json b/ui/public/locales/de/translation.json index 1ca9b87..4b154b4 100644 --- a/ui/public/locales/de/translation.json +++ b/ui/public/locales/de/translation.json @@ -85,7 +85,16 @@ "advanced_configuration": "Erweiterte Konfiguration", "description_advanced_configuration": "Erweiterte Einstellungen um Funktionen des Kerberos Agent zu aktivieren oder deaktivieren", "offline_mode": "Offline Modus", - "description_offline_mode": "Ausgehende Verbindungen deaktivieren" + "description_offline_mode": "Ausgehende Verbindungen deaktivieren", + "encryption": "Encryption", + "description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.", + "encryption_enabled": "Enable MQTT encryption", + "description_encryption_enabled": "Enable encryption for all MQTT messages.", + "encryption_recordings_enabled": "Enable recording encryption", + "description_encryption_recordings_enabled": "Enable encryption for all recordings.", + "encryption_fingerprint": "Fingerprint", + "encryption_privatekey": "Private key", + "encryption_symmetrickey": "Symmetric key" }, "camera": { "camera": "Kamera", diff --git a/ui/public/locales/en/translation.json b/ui/public/locales/en/translation.json index 5b6b1a0..3448821 100644 --- a/ui/public/locales/en/translation.json +++ b/ui/public/locales/en/translation.json @@ -85,7 +85,16 @@ "advanced_configuration": "Advanced configuration", "description_advanced_configuration": "Detailed configuration options to enable or disable specific parts of the Kerberos Agent", "offline_mode": "Offline mode", - "description_offline_mode": "Disable all outgoing traffic" + "description_offline_mode": "Disable all outgoing traffic", + "encryption": "Encryption", + "description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.", + "encryption_enabled": "Enable MQTT encryption", + "description_encryption_enabled": "Enable encryption for all MQTT messages.", + "encryption_recordings_enabled": "Enable recording encryption", + "description_encryption_recordings_enabled": "Enable encryption for all recordings.", + "encryption_fingerprint": "Fingerprint", + "encryption_privatekey": "Private key", + "encryption_symmetrickey": "Symmetric key" }, "camera": { "camera": "Camera", diff --git a/ui/public/locales/es/translation.json b/ui/public/locales/es/translation.json index 8c64faa..eb492d6 100644 --- a/ui/public/locales/es/translation.json +++ b/ui/public/locales/es/translation.json @@ -85,7 +85,16 @@ "advanced_configuration": "Advanced configuration", "description_advanced_configuration": "Detailed configuration options to enable or disable specific parts of the Kerberos Agent", "offline_mode": "Offline mode", - "description_offline_mode": "Disable all outgoing traffic" + "description_offline_mode": "Disable all outgoing traffic", + "encryption": "Encryption", + "description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.", + "encryption_enabled": "Enable MQTT encryption", + "description_encryption_enabled": "Enable encryption for all MQTT messages.", + "encryption_recordings_enabled": "Enable recording encryption", + "description_encryption_recordings_enabled": "Enable encryption for all recordings.", + "encryption_fingerprint": "Fingerprint", + "encryption_privatekey": "Private key", + "encryption_symmetrickey": "Symmetric key" }, "camera": { "camera": "Camera", diff --git a/ui/public/locales/fr/translation.json b/ui/public/locales/fr/translation.json index 392b83a..8070c6c 100644 --- a/ui/public/locales/fr/translation.json +++ b/ui/public/locales/fr/translation.json @@ -84,7 +84,16 @@ "advanced_configuration": "Configuration avancée", "description_advanced_configuration": "Les options de configuration détaillées pour activer ou désactiver des composants spécifiques de l'Agent Kerberos", "offline_mode": "Mode hors-ligne", - "description_offline_mode": "Désactiver tout le trafic sortant" + "description_offline_mode": "Désactiver tout le trafic sortant", + "encryption": "Encryption", + "description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.", + "encryption_enabled": "Enable MQTT encryption", + "description_encryption_enabled": "Enable encryption for all MQTT messages.", + "encryption_recordings_enabled": "Enable recording encryption", + "description_encryption_recordings_enabled": "Enable encryption for all recordings.", + "encryption_fingerprint": "Fingerprint", + "encryption_privatekey": "Private key", + "encryption_symmetrickey": "Symmetric key" }, "camera": { "camera": "Caméra", diff --git a/ui/public/locales/hi/translation.json b/ui/public/locales/hi/translation.json index edd484f..8452f5d 100644 --- a/ui/public/locales/hi/translation.json +++ b/ui/public/locales/hi/translation.json @@ -85,7 +85,16 @@ "advanced_configuration": "एडवांस कॉन्फ़िगरेशन", "description_advanced_configuration": "Kerberos एजेंट के विशिष्ट भागों को सक्षम या अक्षम करने के लिए विस्तृत कॉन्फ़िगरेशन विकल्प", "offline_mode": "ऑफ़लाइन मोड", - "description_offline_mode": "सभी आउटगोइंग ट्रैफ़िक अक्षम करें" + "description_offline_mode": "सभी आउटगोइंग ट्रैफ़िक अक्षम करें", + "encryption": "Encryption", + "description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.", + "encryption_enabled": "Enable MQTT encryption", + "description_encryption_enabled": "Enable encryption for all MQTT messages.", + "encryption_recordings_enabled": "Enable recording encryption", + "description_encryption_recordings_enabled": "Enable encryption for all recordings.", + "encryption_fingerprint": "Fingerprint", + "encryption_privatekey": "Private key", + "encryption_symmetrickey": "Symmetric key" }, "camera": { "camera": "कैमरा", diff --git a/ui/public/locales/it/translation.json b/ui/public/locales/it/translation.json index 122ec39..6e04d1c 100644 --- a/ui/public/locales/it/translation.json +++ b/ui/public/locales/it/translation.json @@ -85,7 +85,16 @@ "advanced_configuration": "Configurazione avanzata", "description_advanced_configuration": "Opzioni di configurazione dettagliate per abilitare o disabilitare parti specifiche del Kerberos Agent", "offline_mode": "Modalità offline", - "description_offline_mode": "Disabilita traffico in uscita" + "description_offline_mode": "Disabilita traffico in uscita", + "encryption": "Encryption", + "description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.", + "encryption_enabled": "Enable MQTT encryption", + "description_encryption_enabled": "Enable encryption for all MQTT messages.", + "encryption_recordings_enabled": "Enable recording encryption", + "description_encryption_recordings_enabled": "Enable encryption for all recordings.", + "encryption_fingerprint": "Fingerprint", + "encryption_privatekey": "Private key", + "encryption_symmetrickey": "Symmetric key" }, "camera": { "camera": "Videocamera", diff --git a/ui/public/locales/ja/translation.json b/ui/public/locales/ja/translation.json index b1c2f37..8307d55 100644 --- a/ui/public/locales/ja/translation.json +++ b/ui/public/locales/ja/translation.json @@ -85,7 +85,16 @@ "advanced_configuration": "詳細設定", "description_advanced_configuration": "Kerberos エージェントの特定の部分を有効または無効にするための詳細な構成オプション", "offline_mode": "オフラインモード", - "description_offline_mode": "すべての送信トラフィックを無効にする" + "description_offline_mode": "すべての送信トラフィックを無効にする", + "encryption": "Encryption", + "description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.", + "encryption_enabled": "Enable MQTT encryption", + "description_encryption_enabled": "Enable encryption for all MQTT messages.", + "encryption_recordings_enabled": "Enable recording encryption", + "description_encryption_recordings_enabled": "Enable encryption for all recordings.", + "encryption_fingerprint": "Fingerprint", + "encryption_privatekey": "Private key", + "encryption_symmetrickey": "Symmetric key" }, "camera": { "camera": "カメラ", diff --git a/ui/public/locales/nl/translation.json b/ui/public/locales/nl/translation.json index 534c0bc..3f89b2e 100644 --- a/ui/public/locales/nl/translation.json +++ b/ui/public/locales/nl/translation.json @@ -85,7 +85,16 @@ "advanced_configuration": "Geavanceerde instellingen", "description_advanced_configuration": "Detail instellingen om bepaalde functionaliteiten van je Kerberos Agent aan en uit te zetten", "offline_mode": "Offline modus", - "description_offline_mode": "Uitzetten van uitgaande connectiviteit" + "description_offline_mode": "Uitzetten van uitgaande connectiviteit", + "encryption": "Encrypteer", + "description_encryption": "Activeer encryptie voor alle uitgaande verkeer. MQTT berichten en/of opnames worden geencrypteerd met AES-256. Een private sleutel wordt gebruikt voor het ondertekenen.", + "encryption_enabled": "Activeer MQTT encryptie", + "description_encryption_enabled": "Activeer encryptie voor alle MQTT berichten.", + "encryption_recordings_enabled": "Activeer opname encryptie", + "description_encryption_recordings_enabled": "Activeer encryptie voor alle opnames.", + "encryption_fingerprint": "Vingerafdruk", + "encryption_privatekey": "Private sleutel", + "encryption_symmetrickey": "Symmetrische sleutel" }, "camera": { "camera": "Camera", diff --git a/ui/public/locales/pl/translation.json b/ui/public/locales/pl/translation.json index 8db876c..5690c14 100644 --- a/ui/public/locales/pl/translation.json +++ b/ui/public/locales/pl/translation.json @@ -85,7 +85,16 @@ "advanced_configuration": "Advanced configuration", "description_advanced_configuration": "Detailed configuration options to enable or disable specific parts of the Kerberos Agent", "offline_mode": "Offline mode", - "description_offline_mode": "Disable all outgoing traffic" + "description_offline_mode": "Disable all outgoing traffic", + "encryption": "Encryption", + "description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.", + "encryption_enabled": "Enable MQTT encryption", + "description_encryption_enabled": "Enable encryption for all MQTT messages.", + "encryption_recordings_enabled": "Enable recording encryption", + "description_encryption_recordings_enabled": "Enable encryption for all recordings.", + "encryption_fingerprint": "Fingerprint", + "encryption_privatekey": "Private key", + "encryption_symmetrickey": "Symmetric key" }, "camera": { "camera": "Camera", diff --git a/ui/public/locales/pt/translation.json b/ui/public/locales/pt/translation.json index 0b4bd2c..7b4e004 100644 --- a/ui/public/locales/pt/translation.json +++ b/ui/public/locales/pt/translation.json @@ -85,7 +85,16 @@ "advanced_configuration": "Configurações avançadas", "description_advanced_configuration": "Opções de configuração detalhadas para habilitar ou desabilitar partes específicas do Kerberos Agent", "offline_mode": "Modo Offline", - "description_offline_mode": "Desative todo o tráfego de saída" + "description_offline_mode": "Desative todo o tráfego de saída", + "encryption": "Encryption", + "description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.", + "encryption_enabled": "Enable MQTT encryption", + "description_encryption_enabled": "Enable encryption for all MQTT messages.", + "encryption_recordings_enabled": "Enable recording encryption", + "description_encryption_recordings_enabled": "Enable encryption for all recordings.", + "encryption_fingerprint": "Fingerprint", + "encryption_privatekey": "Private key", + "encryption_symmetrickey": "Symmetric key" }, "camera": { "camera": "Câmera", diff --git a/ui/public/locales/zh/translation.json b/ui/public/locales/zh/translation.json index 54a15bb..8856be7 100644 --- a/ui/public/locales/zh/translation.json +++ b/ui/public/locales/zh/translation.json @@ -85,7 +85,16 @@ "advanced_configuration": "高级配置", "description_advanced_configuration": "启用或禁用 Kerberos Agent 特定部分详细配置选项", "offline_mode": "离线模式", - "description_offline_mode": "禁用所有传出流量" + "description_offline_mode": "禁用所有传出流量", + "encryption": "Encryption", + "description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.", + "encryption_enabled": "Enable MQTT encryption", + "description_encryption_enabled": "Enable encryption for all MQTT messages.", + "encryption_recordings_enabled": "Enable recording encryption", + "description_encryption_recordings_enabled": "Enable encryption for all recordings.", + "encryption_fingerprint": "Fingerprint", + "encryption_privatekey": "Private key", + "encryption_symmetrickey": "Symmetric key" }, "camera": { "camera": "相机", diff --git a/ui/src/components/LanguageSelect/LanguageSelect.jsx b/ui/src/components/LanguageSelect/LanguageSelect.jsx index d300ad9..8c9ded9 100644 --- a/ui/src/components/LanguageSelect/LanguageSelect.jsx +++ b/ui/src/components/LanguageSelect/LanguageSelect.jsx @@ -24,6 +24,7 @@ const LanguageSelect = () => { pt: { label: 'Português', dir: 'ltr', active: false }, es: { label: 'Español', dir: 'ltr', active: false }, ja: { label: '日本', dir: 'rlt', active: false }, + hi: { label: 'हिंदी', dir: 'ltr', active: false }, }; if (!languageMap[selected]) { diff --git a/ui/src/pages/Settings/Settings.jsx b/ui/src/pages/Settings/Settings.jsx index 3c4561a..af723ec 100644 --- a/ui/src/pages/Settings/Settings.jsx +++ b/ui/src/pages/Settings/Settings.jsx @@ -810,6 +810,24 @@ class Settings extends React.Component { this.onUpdateDropdown('', 'timezone', value[0], config) } /> +
+
+

+ {t('settings.overview.description_advanced_configuration')} +

+
+ + this.onUpdateToggle('', 'offline', event, config) + } + /> +
+ {t('settings.overview.offline_mode')} +

{t('settings.overview.description_offline_mode')}

+
+