GOTPi (Golang + OTP + images) is a lightweight Go CLI and library for experimenting with One-Time Pad (OTP) encryption using images as keys. It allows to generate random noise keys as PNGs (either Grayscale or RGB) and use them to bitwise-encrypt and decrypt image files.
It supports key generation in two modes: standard black and white (Grayscale) or full color (RGB).
| bw | rgb |
|---|---|
![]() |
![]() |
gotpi key-gen |
gotpi key-gen --rgb |
This is inspired by this question on the secutiy of Two-Time Pad on StackOverflow. Indeed this tool is a great way to visualize why key reuse is fatal for OTP security.
If two different messages (
Reusing the key multiple times gives the encryption depth, the more depth given, the more likely it is that information leaks. The attack is called crib dragging.
If two images are encrypted using the same key, hence violating the rule of one-time, then an adversary can compute the xor of the two encryptions and gain an advantage, graphically:
| send-cash.enc.png | vibrant.enc.png | xoring leaks information | ||
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
gotpi enc -f vibrant.png -k rgb-key.png -o vibrant.enc.rgb.png --rgb
![]() |
![]() |
![]() |
gotpi dec -f vibrant.enc.rgb.png -k rgb-key.png -o vibrant.dec.rgb.png --rgb
![]() |
![]() |
![]() |
gotpi enc -f smiley.png -k bw-key.png -o smiley.enc.png
![]() |
![]() |
![]() |
gotpi dec -f smiley.enc.png -k bw-key.png -o smiley.dec.png
go install github.com/micheledinelli/gotpi/cmd/gotpi@latest
gotpi --help
[sub]Command required
usage: One-Time Pad image encryptor <Command> [-h|--help] [-v|--verbose]
[-c|--rgb]
encrypt and decrypt images using OTP images
as keys
Commands:
key-gen Generate a new OTP key image
enc Encrypt an image using an OTP key image
dec Decrypt an image using an OTP key image
xor XOR two images together
Arguments:
-h --help Print help information
-v --verbose Print the encrypted image to terminal. Default: false
-c --rgb use RGB mode instead of black and white. Default: false
go get -u github.com/micheledinelli/gotpi
package main
import (
"image"
"image/png"
"os"
"github.com/micheledinelli/gotpi"
)
func main() {
colored := true
// Generate a key of 256x256 pixels
k := gotpi.KeyGen(256, colored)
// Load an image
path := "image.png"
f, err := os.Open(path)
if err != nil {
panic(err)
}
defer f.Close()
img, _, err := image.Decode(f)
if err != nil {
panic(err)
}
// Encrypt the image
out := gotpi.Encrypt(img, k, colored)
// Save the output
outF, err := os.Create("out.png")
if err != nil {
panic(err)
}
defer outF.Close()
if err := png.Encode(outF, out); err != nil {
panic(err)
}
}If you use a Grayscale key on an RGB image, the output will lose its color data because the key doesn't have enough "depth" to cover the R, G, and B channels.
![]() |
![]() |
![]() |
gotpi enc -f vibrant.png -k bw-key.png -o vibrant.enc.png
![]() |
![]() |
![]() |
gotpi dec -f vibrant.enc.png -k bw-key.png -o vibrant.dec.png
The opposite does work
![]() |
![]() |
![]() |
gotpi enc -f abstract.png -k rgb-key.png -o abstract.enc.rgb.png
![]() |
![]() |
![]() |
gotpi dec -f abstract.enc.rgb.png -k rgb-key.png -o abstract.dec.rgb.png














