{"id":15286907,"url":"https://github.com/perkss/tinklj","last_synced_at":"2025-04-12T12:23:56.791Z","repository":{"id":45555805,"uuid":"166395922","full_name":"perkss/tinklj","owner":"perkss","description":"A Cryptographic Clojure Api for the Google Tink library","archived":false,"fork":false,"pushed_at":"2024-09-21T18:54:51.000Z","size":1104,"stargazers_count":25,"open_issues_count":0,"forks_count":2,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-03-26T07:06:26.772Z","etag":null,"topics":["clojure","crypto","cryptography","encryption","encryption-algorithms","encryption-decryption","security"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/perkss.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":"2019-01-18T11:39:25.000Z","updated_at":"2024-09-21T18:54:51.000Z","dependencies_parsed_at":"2023-02-04T14:01:31.561Z","dependency_job_id":"43e77592-c7e0-41b8-80be-d6cf0eba08c2","html_url":"https://github.com/perkss/tinklj","commit_stats":{"total_commits":64,"total_committers":4,"mean_commits":16.0,"dds":0.328125,"last_synced_commit":"8d3659bbf9df10513608a780354ea5c20c5428c6"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perkss%2Ftinklj","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perkss%2Ftinklj/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perkss%2Ftinklj/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perkss%2Ftinklj/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/perkss","download_url":"https://codeload.github.com/perkss/tinklj/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248565497,"owners_count":21125503,"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":["clojure","crypto","cryptography","encryption","encryption-algorithms","encryption-decryption","security"],"created_at":"2024-09-30T15:18:55.789Z","updated_at":"2025-04-12T12:23:56.752Z","avatar_url":"https://github.com/perkss.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"docs/logo/Tinklj-1st-font-blue-with padding-01.jpg\" alt=\"Tinklj\" width=\"1000\"/\u003e\n\nTinklj a Clojure api for [Google Tink](https://github.com/google/tink) cryptographic library, offering simplistic and secure cryptography for Clojure.\n\n# Installation\n\n[![Clojars Project](https://img.shields.io/clojars/v/tinklj.svg)](https://clojars.org/tinklj)\n\n[![Clojure CI](https://github.com/perkss/tinklj/actions/workflows/clojure.yml/badge.svg)](https://github.com/perkss/tinklj/actions/workflows/clojure.yml)\n\n#### Leiningen/Boot\n```\n[tinklj \"0.1.15-SNAPSHOT\"]\n```\n#### Clojure CLI/deps.edn\n```\ntinklj {:mvn/version \"0.1.15-SNAPSHOT\"}\n```\n#### Gradle\n```\ncompile 'tinklj:tinklj:0.1.15-SNAPSHOT'\n```\n#### Maven\n``` xml\n\u003cdependency\u003e\n  \u003cgroupId\u003etinklj\u003c/groupId\u003e\n  \u003cartifactId\u003etinklj\u003c/artifactId\u003e\n  \u003cversion\u003e0.1.15-SNAPSHOT\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n# Getting Started\n\nThe best way to find example use cases for the encryption techniques is to check the [Acceptance Tests](https://github.com/perkss/tinklj/blob/master/test/tinklj/acceptance/symmetric_key_encryption_test.clj).\n\n## Initialize Tinklj\n\nFirst we need to call register to register the encryption techniques.\nIf you want to use all implementations of all primitives in tinklj then you\ncall as follows:\n\n```clojure\n(:require [tinklj.config :refer [register])\n\n(register)\n```\n\nIf you wish to use a specific implementation such as AEAD you use:\n\n```clojure\n(:require [tinklj.config :refer [register])\n\n(register :aead)\n```\nAvailable options are:\n  - `:aead`\n  - `:daead`\n  - `:mac`\n  - `:signature`\n  - `:streaming`\n\nTo do custom initialization then registration proceeds directly with a Registry class.\n\n```clojure\n(:require [tinklj.config :refer [register-key-manager])\n\n(register-key-manager (MyAeadKeyManager.)\n```\n\n\n## Generating New Key(set)s\n\nBelow shows how you can generate a keyset containing a randomly generated AES128-GCM\nspecified.\n```clojure\n(:require [tinklj.keys.keyset-handle :as keyset-handles])\n\n(keyset-handles/generate-new :aes128-gcm)\n```\n\n## Storing Keysets\n\nOnce you have generated the keyset you can store it down for future use.\nCleartext and encrypted keysets can be stored. Obviously encrypted is recommended.\n\n```clojure\n(:require [tinklj.keys.keyset-handle :as keyset-handles]\n          [tinklj.keysets.keyset-storage :as keyset-storage])\n\n(def filename \"my_keyset.json\")\n(def keyset-handle (keyset-handles/generate-new :aes128-gcm)\n\n(keyset-storage/write-clear-text-keyset-handle keyset-handle filename)\n```\n\nTinklj supports encrypting keysets with master keys stored in a remote key management systems. Lets see how to write these remote KMS keys.\n\n```clojure\n(:require [tinklj.keys.keyset-handle :as keyset-handles]\n          [tinklj.keysets.keyset-storage :as keyset-storage]\n          [tinklj.keysets.integration.gcp-kms-client :refer [gcp-kms-client])\n\n\n;; Generate the key material...\n(def keyset-handle (keyset-handles/generate-new :aes128-gcm)\n\n;; and write it to a file...\n(def filename \"my_keyset.json\")\n;; encrypted with the this key in GCP KMS\n(def master-key-uri = \"gcp-kms://projects/tinklj-examples/locations/global/keyRings/foo/cryptoKeys/bar\")\n(def kms-client (gcp-kms-client))\n\n(keyset-storage/write-remote-kms-keyset-handle\n                    keyset-handle\n                    kms-client\n                    filename\n                    master-key-uri)\n```\n\n## Loading Existing Keysets\n\nOnce the keyset is stored it can be reloaded as follows:\n\n```clojure\n(:require [tinklj.keysets.keyset-storage :as keyset-storage])\n\n(keyset-storage/load-clear-text-keyset-handle filename)\n```\n\nLoading remote encrypted keys:\n\n```clojure\n(:require [tinklj.keys.keyset-handle :as keyset-handles]\n          [tinklj.keysets.keyset-storage :as keyset-storage]\n          [tinklj.keysets.integration.gcp-kms-client :refer [gcp-kms-client])\n\n\n(def filename \"my_keyset.json\")\n;; encrypted with the this key in GCP KMS\n(def master-key-uri = \"gcp-kms://projects/tinklj-examples/locations/global/keyRings/foo/cryptoKeys/bar\")\n(def kms-client (gcp-kms-client))\n\n(keyset-storage/load-remote-kms-keyset-handle\n                    kms-client\n                    filename\n                    master-key-uri)\n```\n\n\n## Obtaining and Using Primitives\n\nWe then get the primitive of the keyset-handle and can use this to encrypt and decypt.\n```clojure\n(keyset-handles/get-primitive keyset-handle)\n```\n\n\n## Symmetric Key Encryption\n#### Authenticated Encryption with Associated Data\n```clojure\n(:require [tinklj.encryption.aead :refer [encrypt decrypt]\n          [tinklj.keys.keyset-handle :as keyset-handle]\n          [tinklj.primitives :as primitives])\n\n;; 1. Generate the key material.\n(def keyset-handle (keyset-handle/generate-new :aes128-gcm))\n\n;; 2. Get the primitive.\n(def aead (primitives/aead keyset-handle))\n\n;; 3. Use the primitive to encrypt a plaintext,\n(def ciphertext (encrypt aead (.getBytes data-to-encrypt) aad))\n\n;; ... or to decrypt a ciphertext.\n(def decrypted (decrypt aead ciphertext aad))\n```\n\n## Deterministic Symmetric Key Encryption\n#### Deterministic Authenticated Encryption with Associated Data\n```clojure\n(:require [tinklj.encryption.daead :refer [encrypt decrypt]\n          [tinklj.keys.keyset-handle :as keyset-handle]\n          [tinklj.primitives :as primitives])\n\n\n;; 1. Generate the key material.\n(def keyset-handle (keyset-handle/generate-new :aes256-siv))\n\n;; 2. Get the primitive.\n(def daead (primitives/deterministic keyset-handle))\n\n;; 3. Use the primitive to deterministically encrypt a plaintext,\n(def ciphertext (encrypt daead (.getBytes data-to-encrypt) aad))\n\n;; ... or to deterministically decrypt a ciphertext.\n(def decrypted (decrypt daead ciphertext aad))\n```\n\n## Symmetric Key Encryption of Streaming Data\n```clojure\n;; 1. Generate the key material.\n(def keyset-handle (keyset-handles/generate-new :aes128-ctr-hmac-sha256-4kb))\n\n;; 2. Get the primitive.\n(def streaming-primitive (primitives/streaming keyset-handle))\n\n;; 3. Get the Encrypting Channel\n(def encrypting-channel (streaming/encrypting-channel\n                                      streaming-primitive\n                                      ciphertext-destination\n                                      associated-data))\n\n;; 4. Write Encrypting Data\n(streaming/encrypting-channel-write encrypting-channel byte-buffer)\n\n;; 5. Get the Decrypting Channel\n(def decrypting-channel (streaming/decrypting-channel\n                                           streaming-primitive\n                                           (.getChannel file-input-stream)\n                                           associated-data))\n\n;; 6. Read the Decrypted Data\n(streaming/decrypting-channel-read decrypting-channel buf)\n```\n\n## Message Authentication Code\n\nHow to compute or verify a MAC (Message Authentication Code)\n\n```clojure\n(:require [tinklj.primitives :as primitives]\n          [tinklj.keys.keyset-handle :as keyset-handles]\n          [tinklj.mac.message-authentication-code :as mac])\n\n;; 1. Generate the key material.\n(def keyset-handle (keyset-handles/generate-new :hmac-sha256-128bittag))\n\n;; 2. Get the primitive.\n(def mac-primitive (primitives/mac keyset-handle))\n\n;; 3. Use the primitive to compute a tag,\n(mac/compute mac-primitive data)\n\n;; ... or to verify a tag.\n(mac/verify mac-primitive tag data)\n```\n\n## Digital Signatures\n\nHere is an example of how to sign or verify a digital signature:\n\n```clojure\n(:require [tinklj.primitives :as primitives]\n          [tinklj.keys.keyset-handle :as keyset-handles]\n          [tinklj.signature.digital-signature :refer [sign verify])\n\n;; SIGNING\n\n;; 1. Generate the private key material.\n(def private-keyset-handle (keyset-handles/generate-new :ecdsa-p256))\n\n;; 2. Sign the data.\n(def signature (sign private-keyset-handle data)\n\n;; VERIFYING\n\n;; 1. Obtain a handle for the public key material.\n(def public-key-set-handle (get-public-keyset-handle private-keyset-handle))\n\n;; 2. Use the primitive to verify.\n(verify public-key-set-handle\n        signature\n        data)\n```\n\n## Hybrid Encryption\n\nTo encrypt or decrypt using a [combination of public key encryption and symmetric key encryption](https://github.com/google/tink/blob/master/docs/PRIMITIVES.md#hybrid-encryption) one can use the following:\n\n```clojure\n(:require [tinklj.config :refer [register]]\n  [tinklj.keys.keyset-handle :as keyset]\n  [tinklj.primitives :as primitives]\n  [tinklj.encryption.aead :refer [encrypt decrypt]])\n\n(register)\n\n;; 1. Generate the private key material.\n(def private-keyset-handle (keyset/generate-new :ecies-p256-hkdf-hmac-sha256-aes128-gcm))\n\n;;  Obtain the public key material.\n(def public-keyset-handle (keyset/get-public-keyset-handle private-keyset-handle))\n\n;; ENCRYPTING\n\n;; 2. Get the primitive.\n(def hybrid-encrypt (primitives/hybrid-encryption public-keyset-handle))\n\n;; 3. Use the primitive.\n(def encrypted-data (encrypt hybrid-encrypt\n                             (.getBytes plain-text)\n                             aad))\n;; DECRYPTING\n\n;;  2. Get the primitive.\n(def hybrid-decrypt (primitives/hybrid-decryption private-keyset-handle))\n\n;; 3. Use the primitive.\n(def decrypted-data (decrypt hybrid-decrypt\n                             encrypted-data\n                             aad))\n```\n\n## Envelope Encryption\n```clojure\n(:require  [tinklj.keys.keyset-handle :as keyset]\n           [tinklj.primitives :as primitives]\n           [tinklj.encryption.aead :refer [encrypt]]\n           [tinklj.keysets.integration.kms-client :as client]\n           [tinklj.keysets.integration.gcp-kms-client :refer [gcp-kms-client])\n\n;; 1. Generate the key material.\n    (def kmsKeyUri\n        \"gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar\")\n\n    (def keysetHandle (keyset/generate-new\n        (keyset/create-kms-envelope-aead-key-template kmsKeyUri :aes128-gcm)))\n\n;; 2. Register the KMS client.\n    (def gcp-client (gcp-kms-client))\n    (client/with-credentials gcp-client \"credentials.json\")\n    (client/kms-client-add gcp-client)\n\n;; 3. Get the primitive.\n    (def aead (primitives/aead keyset-handle))\n\n;; 4. Use the primitive.\n    (def ciphertext (encrypt aead (.getBytes data-to-encrypt) aad))\n\n```\n\n\n## Key Rotation\nTo complete key rotation you need a keyset-handle that contains the keyset that should be rotated, and a specification of the new key via the KeyTemplate map for example :aes128-gcm.\n\n```clojure\n(:require [tinklj.keys.keyset-handle :as keyset-handles]\n          [tinklj.keysets.keyset-storage :as keyset-storage])\n\n(def keyset-handle (keyset-handles/generate-new :aes128-gcm)) ;; existing keyset\n(def keyset-template (:hmac-sha256-128bittag keyset-handles/key-templates)) ;; template for the new key\n\n(keyset-storage/rotate-keyset-handle keyset-handle keyset-template)\n```\n\n## FAQ\n\n1. ### *Exception: java.security.InvalidKeyException: Illegal key size or default parameters* when getting a primitive\n  - If you are running a version of java 8 below `1.8.0_162` you will need to download the (Java Cryptography Extension)[http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html]\n  - If you are using a version of the JVM before `1.8.0_161` and `1.8.0_151` you will need to set `(Security/setProperty \"crypto.policy\" \"unlimited\")`\n  - Above Java 8 Update 161 the default encryption strength is unlimited by default\n\n  See (this stack overflow question for more)[https://stackoverflow.com/questions/24907530/java-security-invalidkeyexception-illegal-key-size-or-default-parameters-in-and]\n\n\n# Feature List\nBased on the available feature list defined [here](https://github.com/google/tink/blob/master/docs/JAVA-HOWTO.md)\n- [x] Generation of keysets\n- [x] Symmetric key encryption\n- [x] Storing keysets\n- [x] Loading existing keysets\n- [x] Storing and loading encrypted keysets\n- [x] Deterministic symmetric key encryption\n- [x] Streaming symmetric key encryption\n- [x] MAC codes\n- [x] Digital signatures\n- [x] Hybrid encryption\n- [x] Envelope encryption\n- [x] Key rotation\n\n# Contributions\n\nPlease feel free to pick up and issue or create issues and contribute.\n\n# Contributors\n\nThanks to all the developers involved!\n\nMatt @lamp\n\nChris @minimal\n\nStuart @perkss\n\n\n# Logo\n\nThanks and credit for the great Tinklj logo!\n\nJ.G Designer @newfinal100\n\n# Disclaimer\nThis is not an official Google product or library.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperkss%2Ftinklj","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fperkss%2Ftinklj","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperkss%2Ftinklj/lists"}