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 | 24 ++-- 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, 393 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 058373a1..4ba689b6 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 6855d9ee..eb88d396 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 82ca5d21..ff2512d0 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 03b6bda4..5a97a834 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 6713fb95..3d4e8a15 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 397ac85d..d1392f43 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 4e298af1..b9760d89 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) - - // 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{}) + encryptedValue, err := encryption.AesEncrypt(data, k) + if err == nil { + + 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 c310b1e3..1e3e4fa1 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 3a68670e..045d0d0b 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 1ca9b87c..4b154b4b 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 5b6b1a09..34488218 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 8c64faa5..eb492d62 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 392b83a8..8070c6c7 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 edd484f9..8452f5d3 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 122ec398..6e04d1c7 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 b1c2f371..8307d55f 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 534c0bc3..3f89b2e9 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 8db876cb..5690c145 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 0b4bd2ca..7b4e0040 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 54a15bb2..8856be75 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 d300ad97..8c9ded9e 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 3c4561a0..af723ecd 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')}

+
+