I'm trying to inject a github secret variable into a repository of an oauth 2.0 connected user (which means that i have its access token)
i'm using octokit to create a create a deploy key on the repository (of 4096 bytes) , then use sodium library (imposed by octokit docs) to endcrypt the secretValue with the deploy key after a lot of trials i recorded this problems: 1- if i put the length of the deploy key 32 bytes , i'm getting an error from github saying that the key is too short , 2- if i put a value of 4096 i'm getting an error that the key length is wrong (should be 32 bytes) 3- if i use another library different than sodium i get an error saying that i should use sodium and other libraries are not supported.
async function generatePublicKey():Promise<string> {
return new Promise(
(resolve, reject) => {
generateKeyPair('rsa', {
modulusLength: 4096,
publicKeyEncoding: {
type: 'pkcs1',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
}
}, (err, publicKey, privateKey) => {
if(err){
reject(err)
}
else{
const pemKey = sshpk.parseKey(publicKey, 'pem');
const sshRsa = pemKey.toString('ssh');
console.log("ssh key is : ",sshRsa);
resolve(sshRsa);
}
});
}
);
}
async injectSecretIntoRepository(repository: string, owner: string, accessToken: string, secretName: string, secretValue: string) {
const octokit = new Octokit({ auth: accessToken });
console.debug("finding deploy key")
const deployKeysResponse = await octokit.rest.repos.listDeployKeys({ owner, repo: repository });
// Find the deploy key with write access
let deployKey = deployKeysResponse.data.find((key) =>( key.read_only === false ));
console.warn(`deploy key: ${JSON.stringify( deployKeysResponse.data)}`)
if (!deployKey) {
console.warn(`Creating a new deploy key: `)
try{
const key = await generatePublicKey()
deployKey =( await octokit.rest.repos.createDeployKey({
owner,
repo: repository,
title: 'Componly deploy key',
key,
read_only: false,
})).data;
console.warn(`Deploy key created ${JSON.stringify(deployKey)}`)
}catch(err){
console.warn(`failed to create a new deploy key`+err)
throw new InternalServerErrorException(err)
}
}
try {
const sodium = await SodiumPlus.auto()
//
//@ts-ignore
//
const secretKey = CryptographyKey.from(deployKey.key)
const nonce = await sodium.randombytes_buf(sodium.CRYPTO_BOX_NONCEBYTES)
//@ts-ignore
const encryptedValue = await sodium.crypto_secretbox (secretValue,nonce,secretKey)
// Create or update the secret with the encrypted value
await octokit.rest.actions.createOrUpdateRepoSecret({
owner,
repo: repository,
secret_name: secretName,
encrypted_value:encryptedValue.toString("base64")
});
console.log("Secret injected successfully.");
} catch (err) {
console.error("Error injecting secret:", err);
throw new InternalServerErrorException("Failed to inject secret into the repository.");
}
}
There is a conflict because different parts of the process require different types of cryptographic keys with different lengths, and it appears that there might have been some confusion in using the same key for different purposes.
From what I can see, you need two different keys:
SSH Deploy Key:
Encryption Key for Secrets:
crypto_secretboxfunction from the sodium library is used for symmetric encryption, and it requires a 32-byte key.crypto_secretboxfunction, which expects a 32-byte symmetric key.So:
crypto_secretboxfunction from the sodium library.But: in your original code, it appears that the SSH deploy key was being used as the encryption key. This causes a conflict because the types and lengths of the keys are different for different purposes (SSH authentication vs. secret encryption).
To resolve this conflict, you need to generate two separate keys:
crypto_secretbox.This modified version of your function separates the concerns of the
deploykey (used for repository access) and the encryption key (used for encrypting the secret).By generating a separate 32-byte key for encryption, it should resolve the issues you were facing with key length.
GitHub does indeed need a way to decrypt the secret when it is used in a GitHub Action.
However, the
deploykey is not used for this purpose. The deploy key is primarily for granting access to the repository and is not used for encrypting or decrypting secrets.When you store a secret in a GitHub repository using the GitHub API, you are expected to encrypt the secret using a public key provided by GitHub. GitHub retains the corresponding private key. When a GitHub Action runs and references a secret, GitHub uses its private key to decrypt the secret.
Meaning:
octokit.rest.actions.getRepoPublicKey).crypto_box_sealfunction.In this modified version, the script first fetches the public key from GitHub, uses it to encrypt the secret, and then stores the encrypted secret.
When a GitHub Action references the secret by name, GitHub automatically decrypts it using the corresponding private key that it holds.