Search code examples
javascriptnode.jsgithubencryptionlibsodium

GitHub API secret encryption with libsodium in Node.js: UnhandledPromiseRejectionWarning: Error: bad public key size


I want to set a repository secret via the GitHub REST API. I use the example from the docs:

const sodium = require('tweetsodium');

const key = "base64-encoded-public-key";
const value = "plain-text-secret";

// Convert the message and key to Uint8Array's (Buffer implements that interface)
const messageBytes = Buffer.from(value);
const keyBytes = Buffer.from(key, 'base64');

// Encrypt using LibSodium.
const encryptedBytes = sodium.seal(messageBytes, keyBytes);

// Base64 the encrypted secret
const encrypted = Buffer.from(encryptedBytes).toString('base64');

console.log(encrypted);

I receive this error:

(node:6008) UnhandledPromiseRejectionWarning: Error: bad public key size
    at checkBoxLengths (C:\Users\User\probot\node_modules\tweetnacl\nacl-fast.js:2158:54)
    at Function.nacl.box.before (C:\Users\User\probot\node_modules\tweetnacl\nacl-fast.js:2231:3)
    at Object.nacl.box (C:\Users\User\probot\node_modules\tweetnacl\nacl-fast.js:2225:20)
    at Object.tweetSodium.seal (C:\Users\User\probot\node_modules\tweetsodium\dist\index.umd.js:53:33)
    at createSecret (C:\Users\User\probot\src\service\secret.js:55:33)
    at Object.<anonymous> (C:\Users\User\probot\src\service\secret.js:73:1)
    at Module._compile (internal/modules/cjs/loader.js:1138:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
    at Module.load (internal/modules/cjs/loader.js:986:32)
    at Function.Module._load (internal/modules/cjs/loader.js:879:14)
(node:6008) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:6008) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Problem: How do I correctly encrypt and decrypt the secret so i can use it in my API?

Solution: @Topaco mentioned that you need to use a base64 encoded key such as 2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvvcCU=. This solves the error descibed above.

EDIT

I will use different secrets in different repositories. Should I generate a new base64 encoded key for each repo?


Solution

  • The example code was not quite clear what the key actually is and where you get it. You need the "repository public key" that you get from the /repos/{owner}/{repo}/codespaces/secrets/public-key endpoint.

    Use the repository public key together with the value from your new secret:

    const key = "base64-encoded-public-key"; // this is the repository public key you need to fetch from GitHub
    const value = "plain-text-secret"; // the secret value
    

    Then you can create or update your key:

      const res = await octokit.actions.createOrUpdateRepoSecret({
        owner: "GITHUBUSER",  
        repo: "GITHUB_REPO", 
        secret_name: "KEY_NAME", 
        encrypted_value: encrypted,
      });