Table of Contents
How I Create GitHub Token Manager
TL;DR
It’s just some simple CRUD using SQLite and AES-CBC 128-bit with Git integration.
GitHub token
Back in December 2020, GitHub announce that start in August 13, 2021 token is require for any Git operations 1.
Although it’s good and you can set different permission and expire date on each token, I just don’t want to copy token on text file every Git operations like my co-worker, so this is why I create simple token manager.
Initial goal
The initial goal is just simple CLI CRUD who encrypt GitHub token and decrypt the token when I need without NodeJS and Python installed, just download the binary ➜ add to PATH ➜ done, and being able to move the database file from Linux to Windows and vice versa.
I choose C++ for this project, and I think fun and interesting to learn more about C++.
Ingredients
Ingredient | Detail |
---|---|
C++ compiler | GCC for Linux and MSVC for Windows |
CMake | https://cmake.org/ |
linuxdeployqt | https://github.com/probonopd/linuxdeployqtbb |
SQLite | https://www.sqlite.org/index.html |
PicoSHA2 | https://github.com/okdshin/PicoSHA2 |
plusaes | https://github.com/kkAyataka/plusaes |
argparse | https://github.com/p-ranav/argparse |
tabulate | https://github.com/p-ranav/tabulate |
clip | https://github.com/dacap/clip |
All ingredients pack into Git submodule except compiler, CMake, and linuxdeployqt.
Encryption
Here some pseudo code in python syntax, just give you rough idea how I implement.
token: str = "ghp_TOKEN"
password: str = "very_secure_password"
final_token: str = "GAH_FLAG_" + token # add flag to token for later decrypt check
hash_password: list[int] = sha256(password) # convert password to array of number (0 - 255)
# ENCRYPT
encrypted_size: list[int] = [len(final_token)] # the aes_decrypt need original string length
encrypted_token: list[int] = aes_encrypt(final_token, hash_password) # encrypt final_token with hash_password, resulting array of number 0 - 255
final_encrypted_token: str = list_number_to_hex_string(encrypted_token + encrypted_size) # convert array number to hex string and store to database
# DECRYPT
list_number_token: list[int] = hex_string_to_list_number(final_encrypted_token)
decrypted_size: int = list_number_token.pop()
decrypted_token: str = aes_decrypt(list_number_token, hash_password, decrypted_size)
if string_start_with(decrypted_token, "GAH_FLAG_"):
print(string_replace(decrypted_token, "GAH_FLAG_", ""))
else:
print("password wrong")
You can see the C++ version gah.cpp#L223
Git integration
Now we have fully functional what the initial goal is, store the token and copy to clipboard when we need it.
Did you know you can set username and token in GitHub remote or GitHub clone but only support for HTTPS protocol as far as I know, something like this.
# git clone
git clone https://username:token@github.com/username/repository.git
# git push
git push https://username:token@github.com/username/repository.git branch
# git pull
git pull https://username:token@github.com/username/repository.git branch
With that you can create something like this.
command: str = "git push https://" + username + ":" + token + "@" + remove_http(remote) + " " + branch;
exec(command);
And the result when implemented.
In Windows is pretty easy to build and distribute the binary file into single exe since all library is a static library, Linux in another hand is hard, like Linus Torvalds say in DebConf 14_ QA with Linus Torvalds talk [YouTube video].
We basically don’t make binaries for Linux. Why? Because binaries for Linux desktop applications is a major f*ing pain in the ass.
– Linus Torvalds
Binary file
Basically most Linux applications using shared library (you can call it dependency), and the library it self should be installed and compatible with the binary (the library version when the binary it’s build).
And I found that AppImage is the solution (Flatpak and Snap is also good, but I prefer using AppImage because it easy to understand), AppImage is a Linux format file allow to pack all shared library that you need into execute binary that self extract and container when it execute, more information on https://appimage.org/.
linuxdeployqt allow to automatically pack all shared library that we need on binary file into single AppImage file, although linuxdeployqt mainly to generate AppImage for Qt application but it can use for general application, more information on https://github.com/probonopd/linuxdeployqt.
At first time I use AppImage, but I realize that all library that I use is capable to static link.
GitHub Actions
Now the final thing is GitHub Actions, let the GitHub Actions compile both for Linux and Windows and automatically create release and upload the binary file, here’s how I do it -> build_release.yml, and also others automation for CI and PR test.
AND IT’S FREE for public repository as far as I know.
Flow actions
Roadmap
- Add more Git command flags.
- BASH auto completion.
What I learn
- Use fixed width integer types for better portability and serialization.
- Shared and static library.
- CMake configuration.
- AppImage structure -> https://docs.appimage.org/packaging-guide/manual.html.
- Github actions.
- Many more.
Conclusion
It was fun learning C++, CMake, and AppImage in this project, the thing that bothering me is I’m too lazy to write any validation command before execute, resulting security concern when user input malicious string.
But as long the initial goal is achieve, it’s ok for me. Thank you so much for reading this post, feel free to contribute to this project.
Project repository -> https://github.com/anasrar/gah/, have a nice day.