{"id":18446473,"url":"https://github.com/ovh/symmecrypt","last_synced_at":"2025-04-09T10:08:31.966Z","repository":{"id":56080988,"uuid":"130082854","full_name":"ovh/symmecrypt","owner":"ovh","description":"Golang symmetric encryption library","archived":false,"fork":false,"pushed_at":"2024-08-26T14:39:06.000Z","size":50,"stargazers_count":119,"open_issues_count":5,"forks_count":11,"subscribers_count":44,"default_branch":"master","last_synced_at":"2025-04-02T07:11:15.614Z","etag":null,"topics":["aead","aes","aes-gcm","chacha20-poly1305","cryptography","golang","golang-library","symmetric-cryptography"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ovh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-04-18T15:23:31.000Z","updated_at":"2025-02-10T23:47:30.000Z","dependencies_parsed_at":"2025-01-15T15:12:55.681Z","dependency_job_id":"797d7aca-7985-469f-8494-a032ccee5c26","html_url":"https://github.com/ovh/symmecrypt","commit_stats":{"total_commits":27,"total_committers":10,"mean_commits":2.7,"dds":0.4444444444444444,"last_synced_commit":"060996e7477a6e32ada3d98c0210a868cdf7aba8"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovh%2Fsymmecrypt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovh%2Fsymmecrypt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovh%2Fsymmecrypt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovh%2Fsymmecrypt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ovh","download_url":"https://codeload.github.com/ovh/symmecrypt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248018060,"owners_count":21034048,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aead","aes","aes-gcm","chacha20-poly1305","cryptography","golang","golang-library","symmetric-cryptography"],"created_at":"2024-11-06T07:09:28.258Z","updated_at":"2025-04-09T10:08:31.939Z","avatar_url":"https://github.com/ovh.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# symmecrypt\n\n*symmecrypt* is a symmetric encryption toolsuite.\nIt provides recommended implementations of crypto algorithms and facilities around configuration management\nand encryption key lifecycle.\n\n[![GoDoc](https://godoc.org/github.com/ovh/symmecrypt?status.svg)](https://godoc.org/github.com/ovh/symmecrypt) [![Go Report Card](https://goreportcard.com/badge/github.com/ovh/symmecrypt)](https://goreportcard.com/report/github.com/ovh/symmecrypt)\n\n## Overview\n\n* [*symmecrypt*](https://github.com/ovh/symmecrypt): Symmetrical encryption with MAC. Built-in cipher implementations provided, but extensible. Also provides a keyring mechanism for easy key rollover.\n* [*symmecrypt/seal*](https://github.com/ovh/symmecrypt/tree/master/seal): Encryption through a symmetric key split in shards (shamir)\n* [*symmecrypt/keyloader*](https://github.com/ovh/symmecrypt/tree/master/keyloader): Configuration manager that loads symmecrypt compatible keys from configuration, and supports key *seal* and *hot-reloading*.\n\n\n## Dependencies\n\n* [*configstore*](https://github.com/ovh/configstore): *symmecrypt/seal* and *symmecrypt/keyloader* provide configuration management facilities, that rely on the *configstore* library. It provides file system, in memory sources, as well as data source abstraction (*providers*), so that any piece of code can easily bridge it with its own configuration management.\n\n\n## Example\n\n```go\n    k, err := keyloader.LoadKey(\"storage\")\n    if err != nil {\n        panic(err)\n    }\n\n    encrypted, err := k.Encrypt([]byte(\"foobar\"), []byte(\"additional\"), []byte(\"mac\"), []byte(\"data\"))\n    if err != nil {\n        panic(err)\n    }\n\n    // decryption will fail if you do not provide the same additional data\n    // of course, you can also encrypt/decrypt without additional data\n    decrypted, err := k.Decrypt(encrypted, []byte(\"additional\"), []byte(\"mac\"), []byte(\"data\"))\n    if err != nil {\n        panic(err)\n    }\n\n    // output: foobar\n    fmt.Println(string(decrypted))\n```\n\n## Configuration format\n\nPackage `configstore` is used for sourcing and managing key configuration values.\n\n```go\n    // before loading a key by its identifier, package `configstore` needs to\n    // be configured so it knows about possible configuration sources\n    //\n    // this would load keys from a file named \"key.txt\" that contains a key with\n    // the identifier \"storage\":\n    configstore.File(\"key.txt\")\n    // more options can be found here: https://github.com/ovh/configstore\n    k, err := keyloader.LoadKey(\"storage\")\n```\n\n`symmecrypt` looks for items in the config store that are of key `encryption-key`, its value is expected to be a JSON string containing the key itself.\n\nIf loading from a text file this would look like:\n\n```\n- key: encryption-key\n  value: '{\"identifier\":\"storage\",\"cipher\":\"aes-gcm\",\"timestamp\":1559309532,\"key\":\"b6a942c0c0c75cc87f37d9e880c440ac124e040f263611d9d236b8ed92e35521\"}'\n```\n\nor when done in code:\n\n```go\n  item := configstore.NewItem(\"encryption-key\", `{\"identifier\":\"storage\",\"cipher\":\"aes-gcm\",\"timestamp\":1559309532,\"key\":\"b6a942c0c0c75cc87f37d9e880c440ac124e040f263611d9d236b8ed92e35521\"}`, 0)\n```\n\n## Key rollover\n\nIt is important to be able to easily rollover keys when doing symmetric encryption.\n\nFor that, one needs to be able to keep decrypting old ciphertexts using the old key,\nwhile encrypting new entries with a new, different key.\n\nThen, the old ciphertexts should all be re-encrypted using the new key.\n\n*symmecrypt* + *symmecrypt/keyloader* make that easy, by providing a keyring / composite key implementation that encrypts with the latest key, while decrypting with *any* key of the keyring.\n\nEncryption keys are fetched from the configuration, and are expected to have the following format:\n```\n    encryption-key: {\"cipher\":\"aes-gcm\",\"key\":\"442fca912da8309613542e7bb29788a44c162cde6ee4f0f5b1322132f65a2ddc\",\"identifier\":\"storage\",\"timestamp\":1522138216}\n    encryption-key: {\"cipher\":\"aes-gcm\",\"key\":\"49a9bc2774e7976c44f4bb6e1e3e6fc70e629be5923a511c8187b72bdc8f848c\",\"identifier\":\"storage\",\"timestamp\":1522138240}\n    \n```\nWith this configuration, the previous example code would automatically instantiate a composite key through *keyloader.LoadKey()*, and be able to decrypt using either key, while all new encryptions would use the timestamp == 1522138240 key.\n\n\n## Seal\n\nIf you do not want to rely on the confidentiality of your configuration to protect your encryption keys, you can *seal* them.\n\n*symmecrypt/seal* provides encryption through a symmetric key which is split in several shards (shamir algorithm). The number of existing shards and the minimum threshold needed to unlock the seal can be configured when first generating it.\n\n*symmecrypt/keyloader* uses *symmecrypt/seal* to generate and load encryption keys which are themselves encrypted. This is controlled via the *sealed* boolean property in a key configuration.\n\nWhen generating a key via *symmecrypt/keyloader.GenerateKey*, use *sealed* = true. This will use the singleton global instance of the *symmecrypt/seal* package to directly seal the key.\n\nWhen loading a key via *symmecrypt/keyloader.LoadKey*, the returned key will automatically decrypt itself and become usable as soon as the singleton global instance of *symmecrypt/seal* becomes unsealed (human operation).\n\nA sealed encryption key is unusable on its own, which makes your configuration less at risk. Additionally, the metadata of the key (identifier, timestamp, cipher...) are passed as additional MAC data when encrypting/decrypting the key, preventing any alteration.\n\n\n```\n    seal:           {\"min\": 2, \"total\": 3, \"nonce\": \"9cce8734c707881b1b00d24c3d9cee13\"} // Seal definition\n    encryption-key: {\"cipher\":\"aes-gcm\",\"key\":\"3414e0524c6a52018849b562b74e611748caf842dd653abc53469c986993f79d4406c662a1a7a9bef141ea88e0464e5bd79857f496418df81bb19ec391174af1d956603c7b8c2825a528972610b25483601c3083ef14c62c31e04f69\",\"identifier\":\"storage\",\"sealed\":true,\"timestamp\":1522138887}\n    encryption-key: {\"cipher\":\"aes-gcm\",\"key\":\"52ef448282bfbdaedcbda970a54b8626ef97a58ffc5489897554c8cba85cf4001d93b23751aaffb5ef2175192bb83ee7c0568634e8d0c7e4ae39f5102402d984220c64d4c6450b034b841844be818a6c5b0ef9016d92b9de1de5408c\",\"identifier\":\"storage\",\"sealed\":true,\"timestamp\":1522138924}\n    \n```\n\nThese keys can be generated via *symmecrypt/keyloader.GenerateKey()*, and are recognized and correctly instantiated by *symmecrypt/keyloader.LoadKey()*.\n\n\n## Supporting your old crypto code\n\nIf you want to start using *symmecrypt* but currently depend on another different implementation, no worries.\n*symmecrypt* supports custom types/ciphers. You can register a named factory via *symmecrypt.RegisterCipher()*, which has to return an object respecting the *symmecrypt.Key* interface, and will be invoked by *symmecrypt/keyloader* when this cipher is specified in a key configuration.\nThat way, you can bridge your old code painlessly, and can get rid of the compatibility bridge once you rollover your encrypted data.\n\nOr you can also decide to keep your own Key implementation, and use it through the keyloader that way.\n\nNote: no matter its cipher (built-in or extended), a key can optionally be sealed without additional logic, this is all handled by *symmecrypt/keyloader* itself.\n\n\n```\n    seal:           {\"min\": 2, \"total\": 3, \"nonce\": \"9cce8734c707881b1b00d24c3d9cee13\"} // Seal definition\n    encryption-key: {\"cipher\":\"old-aes-algo\",\"key\":\"3414e0524c6a52018849b562b74e611748caf842dd653abc53469c986993f79d4406c662a1a7a9bef141ea88e0464e5bd79857f496418df81bb19ec391174af1d956603c7b8c2825a528972610b25483601c3083ef14c62c31e04f69\",\"identifier\":\"storage\",\"sealed\":true,\"timestamp\":1522138887}\n    encryption-key: {\"cipher\":\"aes-gcm\",\"key\":\"52ef448282bfbdaedcbda970a54b8626ef97a58ffc5489897554c8cba85cf4001d93b23751aaffb5ef2175192bb83ee7c0568634e8d0c7e4ae39f5102402d984220c64d4c6450b034b841844be818a6c5b0ef9016d92b9de1de5408c\",\"identifier\":\"storage\",\"sealed\":true,\"timestamp\":1522138924}\n\n```\n\n```go\n    symmecrypt.RegisterCipher(\"old-aes-algo\", OldAESFactory)\n\n    k, err := keyloader.LoadKey(\"storage\")\n    if err != nil {\n        panic(err)\n    }\n\n    encrypted, err := k.Encrypt([]byte(\"foobar\"), []byte(\"additional\"), []byte(\"mac\"), []byte(\"data\"))\n    if err != nil {\n        panic(err)\n    }\n\n    decrypted, err := k.Decrypt(encrypted, []byte(\"additional\"), []byte(\"mac\"), []byte(\"data\"))\n    if err != nil {\n        panic(err)\n    }\n\n    // output: foobar\n    fmt.Println(string(decrypted))\n```\n\nWith such a configuration, any of your previous ciphertexts can be read using your old implementation, but any new data will be encrypted using *symmecrypt*'s aes-gcm implementation.\n\n\n## Available ciphers\n\n*symmecrypt* provides built-in implementations of symmetric authenticated ciphers:\n\n### aes-gcm\n\nRobust | Fast | Proven\n--- | --- | ---\n:star::star: | :star::star::star: | :star::star::star:\n\n[AES Galois/Counter mode](https://csrc.nist.gov/publications/detail/sp/800-38d/final) (256bits), with built-in authentication.\n\n:exclamation: **Nonces are randomly generated and should not be repeated with *aes-gcm*, remember to rollover your key on a regular basis. Nonce size is 96 bits, which is not ideal for random generation due to the risk of collision, prefer *xchacha20-poly1305*.**\n\n### chacha20-poly1305\n\nRobust | Fast | Proven\n--- | --- | ---\n:star::star: | :star::star::star: | :star::star:\n\n[ChaCha20-Poly1305](https://tools.ietf.org/html/rfc7539), with built-in authentication.\n\n:exclamation: **Nonces are randomly generated and should not be repeated with *chacha20-poly1305*, remember to rollover your key on a regular basis. Nonce size is 96 bits, which is not ideal for random generation due to the risk of collision, prefer *xchacha20-poly1305*.**\n\n\n### xchacha20-poly1305\n\nRobust | Fast | Proven\n--- | --- | ---\n:star::star::star: | :star::star::star: | :star::star:\n\nVariant of [ChaCha20-Poly1305](https://tools.ietf.org/html/rfc7539) with extended nonce, with built-in authentication.\n\n:exclamation: **Nonces are randomly generated and should not be repeated with *xchacha20-poly1305*, remember to rollover your key on a regular basis. Nonce size is 192 bits, which is acceptable for random generation.**\n\n### aes-pmac-siv\n\nRobust | Fast | Proven\n--- | --- | ---\n:star::star::star: | :star::star: | :star:\n\nParallelized implementation of [AES-SIV](https://tools.ietf.org/html/rfc5297) (256 bits), with built-in authentication.\n\n:exclamation: **This cipher is still young, use with caution.**\n\n:exclamation: **This is one of the rare ciphers which is not weak to nonce reuse.**\n\nMore information:\n* [AES-PMAC-SIV](https://github.com/miscreant/miscreant/wiki/AES-PMAC-SIV)\n* [miscreant and the nonce reuse issue](https://tonyarcieri.com/introducing-miscreant-a-multi-language-misuse-resistant-encryption-library)\n\n### hmac\n\nRobust | Fast | Proven\n--- | --- | ---\n:star::star::star: | :star::star::star: | :star::star::star:\n\n:exclamation: **DOES NOT GUARANTEE CONFIDENTIALITY.**\n\nHMAC-sha512 for authentication only. Note: if the input consists only of printable characters, so will the output.\n\n## Command-line tool\n\nA command-line tool is available as a companion to the library ([source](https://github.com/ovh/symmecrypt/tree/master/cmd/symmecrypt)).\n\nIt can be used to generate new random encryption keys for any of the built-in symmecrypt ciphers, and to encrypt/decrypt arbitrary data.\n\n### Example (new key)\n```bash\n    $ symmecrypt new aes-gcm --key=storage_key\n    {\"identifier\":\"storage_key\",\"cipher\":\"aes-gcm\",\"timestamp\":1538383069,\"key\":\"46ca74bf7a980ffbfdeea5a66593f7a8f12039f872694015e66c44b652165ee4\"}\n```\n\n### Example (file)\n```bash\n    $ export ENCRYPTION_KEY_BASE64=$(symmecrypt new aes-gcm --base64)\n    $ symmecrypt encrypt \u003c\u003cEOF \u003etest.encrypted\n    foo\n    bar\n    baz\n    EOF\n    $ cat -e test.encrypted\n    ^^JDM-1^EM-$M-^K1nX;^WM-^HC6^Xw^?^BM-.M-p^[M-%=^M-^ZM-uM-%M-2^H6M-sM-NM-FM-^H^RM-]g^_\u0026$\n    $ symmecrypt decrypt \u003ctest.encrypted\n    foo\n    bar\n    baz\n```\n\n### Example (script)\n\n```bash\n    export ENCRYPTION_KEY_BASE64=$(symmecrypt new aes-gcm --base64)\n    ENCRYPTED=$(echo foo bar baz | symmecrypt encrypt --base64)\n    PLAIN=$(echo $ENCRYPTED | symmecrypt decrypt --base64)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovh%2Fsymmecrypt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fovh%2Fsymmecrypt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovh%2Fsymmecrypt/lists"}