Skip to content

gk-encrypt and gk-decrypt

These packages are used to encrypt and decrypt GitKraken's secFiles, which contain sensitive data such as access tokens.

They are primarily intended for use by the gk-login package, but can also be used independently.

Although their execution is considered safe (since they only read the secFiles and output results to stdout), they are provided as-is, with no warranty.

Usage

gk-encrypt

txt
usage: gk-encrypt [-h] -o FILE [-v]

Encrypt JSON data as GitKraken secret.

options:
  -h, --help         show this help message and exit
  -o, --output FILE  write encrypted output to FILE (e.g.,
                     $HOME/.gitkraken/secFile)
  -v, --version      show program's version number and exit

Example: cat data.json | gk-encrypt -o ~/.gitkraken/secFile

gk-decrypt

txt
usage: gk-decrypt [-h] [-o FILE] [-p] [-v] SECRET_FILE

Decrypt GitKraken secret files.

positional arguments:
  SECRET_FILE        path to the secret file to decrypt (e.g.,
                     $HOME/.gitkraken/secFile)

options:
  -h, --help         show this help message and exit
  -o, --output FILE  write output to FILE instead of stdout
  -p, --pretty       pretty print JSON output with indentation
  -v, --version      show program's version number and exit

Example: gk-decrypt ~/.gitkraken/secFile -o decrypted.json --pretty

How to Run

sh
# Using the raw Bash script
$ ./pkgs/encrypt/script.sh
$ ./pkgs/decrypt/script.sh
sh
# ...or using new Nix commands
$ nix run '.#encrypt'
$ nix run '.#decrypt'
sh
# ...or using classic Nix commands
$ nix-build ./pkgs -A encrypt && ./result/bin/gk-encrypt
$ nix-build ./pkgs -A decrypt && ./result/bin/gk-decrypt
sh
# ...or from the Nix development shell (nix develop / nix-shell)
$ gk-encrypt
$ gk-decrypt

For further details, refer to the actual scripts code:

Encryption / Decryption Methods

The encryption and decryption methods are adapted from GitKraken's original code, reimplemented using Python modules.

The reference implementation below is stripped from irrelevant code, prettified from main.bundle.js and enhanced with comments.

TL;DR

The secrets are JSON data encrypted with the appId as the passphrase.

js
class Ae {
  data = {};

  /**
   * Represents secure data.
   * @param {string} re - Path to secret file
   * @param {string} ne - Secret file password (appId)
   * @param {string} se - Cryptography algorithm
   */
  constructor(re, ne, se) {
    (this.secPath = re),
      (this.password = ne),
      (this.algorithm = se),
      (this.cryptoHack = le.default), // This is Node's crypto module
      this.loadData();
  }

  /**
   * Loads data from a secret file (decryption).
   */
  async loadData() {
    try {
      // Create a decipher with the password key
      const re = this.cryptoHack.createDecipher(
          this.algorithm,
          this.password,
        ),
        // Read the entire secret file as a Buffer
        ne = await pe.promises.readFile(this.secPath),
        // Decrypt data, verify authentication tag and convert decrypted Buffer to string
        se = Buffer.concat([re.update(ne), re.final()]).toString();
      // Parse decrypted data as JSON
      this.data = JSON.parse(se);
    } catch {
      this.data = {};
    }
  }

  /**
   * Saves data into a secret file (encryption).
   */
  async saveData() {
    try {
      // Create a cipher with the password key
      const re = this.cryptoHack.createCipher(
          this.algorithm,
          this.password,
        ),
        // Encrypt JSON data as string with authentication tag into a Buffer
        ne = Buffer.concat([
          re.update(Buffer.from(JSON.stringify(this.data))),
          re.final(),
        ]);
      // Replace entire content of secret file with encrypted data
      await pe.promises.writeFile(this.secPath, ne);
    } catch {}
  }
}

Released under the MIT License