From 27d5fe95a66520687318ebb6e34c256412f6a5c0 Mon Sep 17 00:00:00 2001 From: Universal Studio <10200399+TMUniversal@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:57:07 +0200 Subject: [PATCH 1/4] style: format readme --- README.md | 103 ++++++++++++++++++++++++------------------------------ 1 file changed, 46 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 635b4d7..12d710e 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,9 @@ --- PaperCrypt is a Go-based command-line tool designed to enhance the security of your sensitive data through the -generation of printable backup documents. -These documents, referred to as "PaperCrypt" Documents, combine the robust +generation of printable backup documents. These documents, referred to as "PaperCrypt" Documents, combine the robust encryption capabilities of the [OpenPGP](https://gopenpgp.org/) -with the resilience and simplicity of a physical hardcopy. -This ensures the confidentiality and integrity of your data, +with the resilience and simplicity of a physical hardcopy. This ensures the confidentiality and integrity of your data, while also providing a physical backup that 's not susceptible to digital threats. > Please note that to decrypt the data from a PaperCrypt Document, you will need the original passphrase used during the @@ -34,10 +32,10 @@ while also providing a physical backup that 's not susceptible to digital threat - **Data Integrity**: To verify the integrity of the data, PaperCrypt embeds checksums within the encrypted data section of its documents. This ensures that the data remains unaltered during backup and restoration processes. -- **Offline Security**: By generating printable backup documents, PaperCrypt offers an offline solution to - safeguard your sensitive data against online threats, as well as an option to store your data in an off-site - location. This provides a layer of security, as it ensures that your data remains safe and accessible even in the - event of a catastrophic failure, malicious attack, or natural disaster. +- **Offline Security**: By generating printable backup documents, PaperCrypt offers an offline solution to safeguard + your sensitive data against online threats, as well as an option to store your data in an off-site location. This + provides a layer of security, as it ensures that your data remains safe and accessible even in the event of a + catastrophic failure, malicious attack, or natural disaster. ## Version Compatibility @@ -46,7 +44,8 @@ PaperCrypt v3 introduces a new container format (version 3). Note the following - PaperCrypt v3 only decodes v3 documents. - v1 and v2 can be decoded by PaperCrypt v2. -It is recommended to use the exact same version of PaperCrypt to decode a document that was used to encode it. That version is indicated on the document itself. +It is recommended to use the exact same version of PaperCrypt to decode a document that was used to encode it. That +version is indicated on the document itself. ## Installation @@ -67,8 +66,8 @@ brew install --cask papercrypt #### Scoop (Windows) -Make sure you have [scoop](https://scoop.sh/) installed, -alongside `git` (`scoop install git`) to be able to add the bucket. +Make sure you have [scoop](https://scoop.sh/) installed, alongside `git` (`scoop install git`) to be able to add the +bucket. ```bash scoop bucket add tmuniversal https://github.com/tmuniversal/scoop-bucket.git @@ -124,8 +123,8 @@ You can also run PaperCrypt using Docker, with the following command: docker run --rm -it -v $(pwd):/data ghcr.io/tmuniversal/papercrypt:latest ``` -With `-v $(pwd):/data` mounting the current working directory as `/data` in the container, -allowing the container to read and write to host storage. +With `-v $(pwd):/data` mounting the current working directory as `/data` in the container, allowing the container to +read and write to host storage. On Windows, the command is slightly different: @@ -138,8 +137,8 @@ Note that `-t` is required so that the program can prompt for a passphrase. ### Verifying artifacts First, you'll need to download the archive and signature file (`.sig`) for your version from -the [releases page](https://github.com/TMUniversal/papercrypt/releases), pay attention to the -version (`papercrypt version`), your OS and architecture. You will also need the public key ([`cosign.pub`]). +the [releases page](https://github.com/TMUniversal/papercrypt/releases), pay attention to the version +(`papercrypt version`), your OS and architecture. You will also need the public key ([`cosign.pub`]). The pre-built binaries are signed through [`cosign`](https://github.com/sigstore/cosign#installation). @@ -166,20 +165,18 @@ cosign verify-blob \ General notes: - `--in` and `--out` can be omitted, in which case `stdin` and `stdout` are used. -- This means `papercrypt decode --in - --out - < qr.txt > data.json` is equivalent - to `papercrypt decode < qr.txt > data.json` +- This means `papercrypt decode --in - --out - < qr.txt > data.json` is equivalent to + `papercrypt decode < qr.txt > data.json` - Commands, as well as their flags, can be abbreviated to their shortest unique prefix: - - `papercrypt generate` can be abbreviated to `papercrypt g` -- that is `papercrypt generate --in data.json --out output.pdf` can be abbreviated - to `papercrypt g -i data.json -o output.pdf` + - `papercrypt generate` can be abbreviated to `papercrypt g` +- that is `papercrypt generate --in data.json --out output.pdf` can be abbreviated to + `papercrypt g -i data.json -o output.pdf` ### Generating a key phrase -A 24 word mnemonic phrase is suitable for real-world use, -but you can use any string of words or characters. +A 24 word mnemonic phrase is suitable for real-world use, but you can use any string of words or characters. -Generate one with your tool of choice, -you can run: +Generate one with your tool of choice, you can run: ```bash papercrypt generate-key --words 24 --out mnemonic.txt @@ -191,9 +188,8 @@ to generate a 24 word mnemonic phrase. #### The passphrase sheet -PaperCrypt is able to generate a printable _Phrase Sheet_, -which is a two-page document containing 135 words from the EFF large word list, -chosen with a seeded random number generator. +PaperCrypt is able to generate a printable _Phrase Sheet_, which is a two-page document containing 135 words from the +EFF large word list, chosen with a seeded random number generator. If no seed is passed to the command, one will be generated using the system's entropy source. @@ -203,9 +199,9 @@ If no seed is passed to the command, one will be generated using the system's en papercrypt phrase-sheet --out phrase-sheet.pdf ExampleAbcA= ``` -Here, `ExampleAbcA=` is the base64-encoded seed, which is used to generate the word list. -The seed will is also present on the generated PDF document, -so you can regenerate the same word list later, even if you allowed the seed to be chosen at random. +Here, `ExampleAbcA=` is the base64-encoded seed, which is used to generate the word list. The seed will is also present +on the generated PDF document, so you can regenerate the same word list later, even if you allowed the seed to be chosen +at random. Using the phrase sheet, you can select a number of words from to form your mnemonic phrase. @@ -234,8 +230,7 @@ papercrypt generate --in data.json --out output.pdf to generate the file containing your data, and the decryption instructions. -The program then asks you for an encryption key, -for which you can use your mnemonic phrase from earlier. +The program then asks you for an encryption key, for which you can use your mnemonic phrase from earlier. > You can also pass the data through `stdin`, simply omit the `--in` flag. > The caveat is that, when on Windows, you can't be prompted for your passphrase, @@ -249,11 +244,10 @@ Please see the [examples](examples) directory for the generated PDF files. ### Restoring a PaperCrypt document -To restore your data from a PaperCrypt document, -you must first re-construct the document from the printed copy. -This can be done either by saving the QR code as an image file, -and [passing it to the command-line](#using-the-qr-code), -or by copy-pasting the text from the printed document (would have to run [OCR](https://www.adobe.com/acrobat/guides/what-is-ocr.html "optical character recognition")). +To restore your data from a PaperCrypt document, you must first re-construct the document from the printed copy. This +can be done either by saving the QR code as an image file, and [passing it to the command-line](#using-the-qr-code), or +by copy-pasting the text from the printed document (would have to +run [OCR](https://www.adobe.com/acrobat/guides/what-is-ocr.html "optical character recognition")). #### Using the QR code @@ -268,9 +262,8 @@ papercrypt scan --in 2d.png --out data.txt
QR-Code Data Format (Click to expand) -The QR code uses a custom data format to fit as much information as possible into the QR code, -while keeping the metadata intact. -This format is not designed to be human-readable. +The QR code uses a custom data format to fit as much information as possible into the QR code, while keeping the +metadata intact. This format is not designed to be human-readable. **Encoding pipeline:** @@ -278,14 +271,11 @@ This format is not designed to be human-readable. MarshalBinary → PC envelope (Base45, gzip if smaller) → QR code ``` -The envelope wraps the Base45-encoded payload with a CRC-32 integrity check. -The envelope header is the magic `PC` followed by the info field and the -envelope version, each encoded as a single base36 character (`0-9A-Z`, -alphabet `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`). The info character -carries the envelope type in its least significant bit, the content -encoding type in the next two bits (base45 = `1`), and the content -compression type in the fourth bit (`1` = gzip). The payload is -gzip-compressed only when that makes it smaller: +The envelope wraps the Base45-encoded payload with a CRC-32 integrity check. The envelope header is the magic `PC` +followed by the info field and the envelope version, each encoded as a single base36 character (`0-9A-Z`, alphabet +`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`). The info character carries the envelope type in its least significant bit, the +content encoding type in the next two bits (base45 = `1`), and the content compression type in the fourth bit (`1` = +gzip). The payload is gzip-compressed only when that makes it smaller: ```text PC + base36(info) + base36(version) + base45(CRC-32 of payload) + base45(payload) @@ -294,7 +284,7 @@ PC + base36(info) + base36(version) + base45(CRC-32 of payload) + base45(payload **Binary container wire format** (produced by `MarshalBinary`): | Offset | Size | Field | -| ------ | ---- | ---------------------------------------------- | +|--------|------|------------------------------------------------| | 0 | 2 | Magic: `PC` | | 2 | 1 | Container format version (`05`) | | 3 | 3 | Program Version (major, minor, patch as uint8) | @@ -379,21 +369,20 @@ papercrypt decode -i data.txt -o data.json -P "super-secret-key" ## Contributing Contributions to PaperCrypt are welcomed and encouraged! If you have suggestions for improvements, bug fixes, or new -features, please feel free to submit a pull request. -Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for more information. +features, please feel free to submit a pull request. Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for more information. ## License -PaperCrypt is licensed under the terms of the GNU Affero General Public License, version 3.0 or -later ([GNU AGPL-3.0-or-later](LICENSE)). +PaperCrypt is licensed under the terms of the GNU Affero General Public License, version 3.0 or later +([GNU AGPL-3.0-or-later](LICENSE)). [![License Logo](https://www.gnu.org/graphics/agplv3-with-text-162x68.png)](https://www.gnu.org/licenses/agpl-3.0.en.html) ## Acknowledgments -PaperCrypt is developed leveraging the power of Go and a suite of dependable open source libraries. -We extend our gratitude to the developers behind -[GopenPGP](https://github.com/ProtonMail/gopenpgp), [GoFPDF](https://github.com/jung-kurt/gofpdf), -and other foundational components. +PaperCrypt is developed leveraging the power of Go and a suite of dependable open source libraries. We extend our +gratitude to the developers behind +[GopenPGP](https://github.com/ProtonMail/gopenpgp), [GoFPDF](https://github.com/jung-kurt/gofpdf), and other +foundational components. [`cosign.pub`]: https://github.com/TMUniversal/papercrypt/blob/main/cosign.pub From 397e23eafe02e61055750d2e355d897c85c890b4 Mon Sep 17 00:00:00 2001 From: Universal Studio <10200399+TMUniversal@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:59:18 +0200 Subject: [PATCH 2/4] refactor: move packages out of internal/ --- AGENTS.md | 48 ++++++++++++------- cmd/decode.go | 4 +- cmd/generate.go | 4 +- cmd/generate_key.go | 10 ++-- cmd/phrase_sheet.go | 4 +- cmd/root.go | 2 +- cmd/scan_code.go | 8 ++-- .../codematrix => codematrix}/codematrix.go | 0 .../codematrix_test.go | 2 +- {internal/codematrix => codematrix}/decode.go | 0 {internal/codematrix => codematrix}/encode.go | 0 {internal/crc24 => crc24}/crc.go | 0 {internal/crc24 => crc24}/crc24.go | 0 {internal/crc24 => crc24}/crc_test.go | 0 .../file_format => file_format}/container.go | 4 +- .../container_binary.go | 0 .../container_binary_test.go | 2 +- .../container_decode.go | 0 .../container_envelope.go | 2 +- .../container_json.go | 0 .../container_pdf.go | 10 ++-- .../container_text.go | 4 +- .../envelope/compression.go | 0 .../envelope/encoder.go | 0 .../envelope/envelope.go | 0 .../envelope/envelope_test.go | 0 .../envelope/header.go | 0 .../format_version.go | 0 .../file_format => file_format}/serial.go | 0 .../file_format => file_format}/serialize.go | 16 +++---- .../serialize_test.go | 0 papercrypt.go | 2 +- {internal/pdf => pdf}/generator.go | 0 {internal/pdf => pdf}/mode_pgp.go | 0 {internal/pdf => pdf}/mode_raw.go | 0 {internal/pdf => pdf}/pdf.go | 0 .../phrase_sheet.go | 2 +- {internal/terminal => terminal}/outputs.go | 0 .../terminal => terminal}/read_password.go | 0 .../read_password_unix.go | 0 .../read_password_windows.go | 0 {internal/terminal => terminal}/styles.go | 0 42 files changed, 69 insertions(+), 55 deletions(-) rename {internal/codematrix => codematrix}/codematrix.go (100%) rename {internal/codematrix => codematrix}/codematrix_test.go (98%) rename {internal/codematrix => codematrix}/decode.go (100%) rename {internal/codematrix => codematrix}/encode.go (100%) rename {internal/crc24 => crc24}/crc.go (100%) rename {internal/crc24 => crc24}/crc24.go (100%) rename {internal/crc24 => crc24}/crc_test.go (100%) rename {internal/file_format => file_format}/container.go (97%) rename {internal/file_format => file_format}/container_binary.go (100%) rename {internal/file_format => file_format}/container_binary_test.go (99%) rename {internal/file_format => file_format}/container_decode.go (100%) rename {internal/file_format => file_format}/container_envelope.go (95%) rename {internal/file_format => file_format}/container_json.go (100%) rename {internal/file_format => file_format}/container_pdf.go (92%) rename {internal/file_format => file_format}/container_text.go (98%) rename {internal/file_format => file_format}/envelope/compression.go (100%) rename {internal/file_format => file_format}/envelope/encoder.go (100%) rename {internal/file_format => file_format}/envelope/envelope.go (100%) rename {internal/file_format => file_format}/envelope/envelope_test.go (100%) rename {internal/file_format => file_format}/envelope/header.go (100%) rename {internal/file_format => file_format}/format_version.go (100%) rename {internal/file_format => file_format}/serial.go (100%) rename {internal/file_format => file_format}/serialize.go (94%) rename {internal/file_format => file_format}/serialize_test.go (100%) rename {internal/pdf => pdf}/generator.go (100%) rename {internal/pdf => pdf}/mode_pgp.go (100%) rename {internal/pdf => pdf}/mode_raw.go (100%) rename {internal/pdf => pdf}/pdf.go (100%) rename {internal/phrase_sheet => phrase_sheet}/phrase_sheet.go (99%) rename {internal/terminal => terminal}/outputs.go (100%) rename {internal/terminal => terminal}/read_password.go (100%) rename {internal/terminal => terminal}/read_password_unix.go (100%) rename {internal/terminal => terminal}/read_password_windows.go (100%) rename {internal/terminal => terminal}/styles.go (100%) diff --git a/AGENTS.md b/AGENTS.md index 36d1c70..77b614d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,38 +11,52 @@ Use `task` for all verification; do not substitute raw `go test`/`go vet` for th - `task build` - `task test` — vet + unit + e2e + raw + cleanup (CI parity) - `task test:unit` — `-short -race -coverpkg=./...`; tune via `TEST_OPTIONS`, `SOURCE_FILES`, `TEST_PATTERN` - - Focused: `task test:unit SOURCE_FILES=./internal/file_format/envelope/... TEST_PATTERN=TestGzipCompressorRejectsOversizedOutput` + - Focused: + `task test:unit SOURCE_FILES=./internal/file_format/envelope/... TEST_PATTERN=TestGzipCompressorRejectsOversizedOutput` - `task test:unit:full` — same without `-short` - `task ci` (setup + build + test), `task test:fuzz`, `task cover`, `task reltest` - E2E requires `pdftoppm` (macOS: `brew install poppler`); `task test` flows a PDF through pdftoppm → `scan` → `decode`. -Pre-commit hook: `task dev` installs `.git/hooks/pre-commit` (runs `gofumpt` + `golangci-lint run --new --fix`). Not installed by default. +Pre-commit hook: `task dev` installs `.git/hooks/pre-commit` (runs `gofumpt` + `golangci-lint run --new --fix`). Not +installed by default. ## Style -- Comments: `why` only, never restate what code does. The revive config deliberately drops `exported`/`package-comments` doc rules so "what" comments can be removed. +- Comments: `why` only, never restate what code does. The revive config deliberately drops `exported`/`package-comments` + doc rules so "what" comments can be removed. - Every `.go` file carries the AGPL license header — copy from a neighboring file for new files. - Lint rules that fail in surprising ways: - - revive `redefines-builtin-id` is ON — no params/vars named `max`, `min`, `any`, etc. - - revive `error-strings` is ON — error messages start lowercase, no trailing punctuation. - - `golines` is a formatter with a short line budget and will not auto-wrap long literals — wrap long `errors.New`/`fmt.Errorf` args manually (see the `ErrDecompressedSizeExceeded` var block). - - forbidigo bans `ioutil.*`; depguard bans `github.com/pkg/errors` (use stdlib `errors`). - - tagliatelle requires snake_case yaml/json tags. - - gosec `G304` is excluded only for `internal/filesystem.go` and `cmd/decode_test.go`. + - revive `redefines-builtin-id` is ON — no params/vars named `max`, `min`, `any`, etc. + - revive `error-strings` is ON — error messages start lowercase, no trailing punctuation. + - `golines` is a formatter with a short line budget and will not auto-wrap long literals — wrap long `errors.New`/ + `fmt.Errorf` args manually (see the `ErrDecompressedSizeExceeded` var block). + - forbidigo bans `ioutil.*`; depguard bans `github.com/pkg/errors` (use stdlib `errors`). + - tagliatelle requires snake_case yaml/json tags. + - gosec `G304` is excluded only for `internal/filesystem.go` and `cmd/decode_test.go`. ## Architecture -- Entrypoint `papercrypt.go` sets go-embedded assets (fonts, LICENSE, EFF word list, THIRD_PARTY.md) onto `cmd` package pointers, then calls `cmd.Execute()`. -- `internal/file_format`: binary container wire format v5 — magic `PC`, format version byte `05` (`CurrentBinaryFormatVersion`; decode rejects any other byte). Table in README. `container_envelope.go` bridges the QR envelope to the container; `container_decode.go` reverses the pipeline. -- `internal/file_format/envelope`: `Wrap`/`Unwrap` with an injectable `ContentEncoder` (currently Base45), gzip only when it shrinks the payload. Header = `PC` + base36(info) + base36(version) + base45(CRC-32) + base45(payload) — documented in README; keep in sync. -- Decompression capped at 1 GiB (`maxDecompressedSize`); `scan --unlimited` disables it. On a cap hit, `envelope.ErrDecompressedSizeExceeded` fires and scan appends a `use --unlimited` hint. -- `internal/codematrix` = QR encode (boombuler/barcode) / decode (gozxing); `internal/pdf` = gofpdf with embedded Noto Sans/Inconsolata. +- Entrypoint `papercrypt.go` sets go-embedded assets (fonts, LICENSE, EFF word list, THIRD_PARTY.md) onto `cmd` package + pointers, then calls `cmd.Execute()`. +- `file_format`: binary container wire format v5 — magic `PC`, format version byte `05` (`CurrentBinaryFormatVersion`; + decode rejects any other byte). Table in README. `container_envelope.go` bridges the QR envelope to the container; + `container_decode.go` reverses the pipeline. +- `file_format/envelope`: `Wrap`/`Unwrap` with an injectable `ContentEncoder` (currently Base45), gzip only when it + shrinks the payload. Header = `PC` + base36 (info) + base36 (version) + base45 (CRC-32) + base45 (payload) — + documented in README; keep in sync. +- Decompression capped at 1 GiB (`maxDecompressedSize`); `scan --unlimited` disables it. On a cap hit, + `envelope.ErrDecompressedSizeExceeded` fires and scan appends a `use --unlimited` hint. +- `codematrix` = QR encode (boombuler/barcode) / decode (gozxing); `pdf` = gofpdf with embedded Noto Sans/Inconsolata. ## Tracked artifacts -- `examples/*.pdf` are committed; regenerate via `task docs:examples` (requires `pdfcpu`) after envelope/container format changes. The checked-in PDFs predate the base36 envelope header and carry old-format QRs. -- `coverage.txt`, `dist/`, `bin/`, `manpages/`, `completions/` are generated; `task clean` removes them. `task test` leaves no residue. +- `examples/*.pdf` are committed; regenerate via `task docs:examples` (requires `pdfcpu`) after envelope/container + format changes. The checked-in PDFs predate the base36 envelope header and carry old-format QRs. +- `coverage.txt`, `dist/`, `bin/`, `manpages/`, `completions/` are generated; `task clean` removes them. `task test` + leaves no residue. ## Compatibility -- Software major v3 decodes only v3 documents (README); distinct from the container wire format byte above (`05`). Keep envelope/container wire formats backward compatible within the branch; the base36 header alphabet and the 1 GiB cap are recent changes. +- Software major v3 decodes only v3 documents (README); distinct from the container wire format byte above (`05`). Keep + envelope/container wire formats backward compatible within the branch; the base36 header alphabet and the 1 GiB cap + are recent changes. diff --git a/cmd/decode.go b/cmd/decode.go index 8140616..6311a93 100644 --- a/cmd/decode.go +++ b/cmd/decode.go @@ -26,9 +26,9 @@ import ( "github.com/caarlos0/log" "github.com/spf13/cobra" + "github.com/tmuniversal/papercrypt/v3/file_format" "github.com/tmuniversal/papercrypt/v3/internal" - "github.com/tmuniversal/papercrypt/v3/internal/file_format" - "github.com/tmuniversal/papercrypt/v3/internal/terminal" + "github.com/tmuniversal/papercrypt/v3/terminal" ) var ( diff --git a/cmd/generate.go b/cmd/generate.go index 1547982..cef2b78 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -30,9 +30,9 @@ import ( "github.com/ProtonMail/gopenpgp/v3/crypto" "github.com/caarlos0/log" "github.com/spf13/cobra" + "github.com/tmuniversal/papercrypt/v3/file_format" "github.com/tmuniversal/papercrypt/v3/internal" - "github.com/tmuniversal/papercrypt/v3/internal/file_format" - "github.com/tmuniversal/papercrypt/v3/internal/terminal" + "github.com/tmuniversal/papercrypt/v3/terminal" ) var ( diff --git a/cmd/generate_key.go b/cmd/generate_key.go index 08396e3..a36f9d5 100644 --- a/cmd/generate_key.go +++ b/cmd/generate_key.go @@ -31,8 +31,8 @@ import ( "github.com/caarlos0/log" "github.com/spf13/cobra" "github.com/tmuniversal/papercrypt/v3/internal" - "github.com/tmuniversal/papercrypt/v3/internal/phrase_sheet" - terminal2 "github.com/tmuniversal/papercrypt/v3/internal/terminal" + "github.com/tmuniversal/papercrypt/v3/phrase_sheet" + "github.com/tmuniversal/papercrypt/v3/terminal" ) var words int @@ -45,7 +45,7 @@ var ( const wordListURL = "https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt" -var wordListURLFormatted = terminal2.URL(wordListURL) +var wordListURLFormatted = terminal.URL(wordListURL) var generateKeyCmd = &cobra.Command{ Aliases: []string{"key", "gen", "k"}, @@ -79,7 +79,7 @@ which can be found here: %s.`, wordString := strings.Join(keyPhrase, " ") if outFile == os.Stdout { - wordString = terminal2.Bold(wordString) + wordString = terminal.Bold(wordString) } n, err := outFile.WriteString(wordString) @@ -91,7 +91,7 @@ which can be found here: %s.`, _, _ = fmt.Fprintln(outFile) } - terminal2.PrintWrittenSizeToDebug(n, outFile) + terminal.PrintWrittenSizeToDebug(n, outFile) return nil }, } diff --git a/cmd/phrase_sheet.go b/cmd/phrase_sheet.go index cf745b6..8509c54 100644 --- a/cmd/phrase_sheet.go +++ b/cmd/phrase_sheet.go @@ -32,8 +32,8 @@ import ( "github.com/caarlos0/log" "github.com/spf13/cobra" "github.com/tmuniversal/papercrypt/v3/internal" - "github.com/tmuniversal/papercrypt/v3/internal/phrase_sheet" - "github.com/tmuniversal/papercrypt/v3/internal/terminal" + "github.com/tmuniversal/papercrypt/v3/phrase_sheet" + "github.com/tmuniversal/papercrypt/v3/terminal" ) const ( diff --git a/cmd/root.go b/cmd/root.go index 7e0b8cf..72fe249 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -25,7 +25,7 @@ import ( "github.com/caarlos0/log" "github.com/spf13/cobra" - "github.com/tmuniversal/papercrypt/v3/internal/terminal" + "github.com/tmuniversal/papercrypt/v3/terminal" ) var ( diff --git a/cmd/scan_code.go b/cmd/scan_code.go index 366e228..5ccef62 100644 --- a/cmd/scan_code.go +++ b/cmd/scan_code.go @@ -29,11 +29,11 @@ import ( "github.com/caarlos0/log" "github.com/spf13/cobra" + "github.com/tmuniversal/papercrypt/v3/codematrix" + "github.com/tmuniversal/papercrypt/v3/file_format" + "github.com/tmuniversal/papercrypt/v3/file_format/envelope" "github.com/tmuniversal/papercrypt/v3/internal" - "github.com/tmuniversal/papercrypt/v3/internal/codematrix" - "github.com/tmuniversal/papercrypt/v3/internal/file_format" - "github.com/tmuniversal/papercrypt/v3/internal/file_format/envelope" - "github.com/tmuniversal/papercrypt/v3/internal/terminal" + "github.com/tmuniversal/papercrypt/v3/terminal" ) var ( diff --git a/internal/codematrix/codematrix.go b/codematrix/codematrix.go similarity index 100% rename from internal/codematrix/codematrix.go rename to codematrix/codematrix.go diff --git a/internal/codematrix/codematrix_test.go b/codematrix/codematrix_test.go similarity index 98% rename from internal/codematrix/codematrix_test.go rename to codematrix/codematrix_test.go index 29d3eee..51dec43 100644 --- a/internal/codematrix/codematrix_test.go +++ b/codematrix/codematrix_test.go @@ -27,7 +27,7 @@ import ( "strings" "testing" - "github.com/tmuniversal/papercrypt/v3/internal/file_format/envelope" + "github.com/tmuniversal/papercrypt/v3/file_format/envelope" ) func TestRoundtrip(t *testing.T) { diff --git a/internal/codematrix/decode.go b/codematrix/decode.go similarity index 100% rename from internal/codematrix/decode.go rename to codematrix/decode.go diff --git a/internal/codematrix/encode.go b/codematrix/encode.go similarity index 100% rename from internal/codematrix/encode.go rename to codematrix/encode.go diff --git a/internal/crc24/crc.go b/crc24/crc.go similarity index 100% rename from internal/crc24/crc.go rename to crc24/crc.go diff --git a/internal/crc24/crc24.go b/crc24/crc24.go similarity index 100% rename from internal/crc24/crc24.go rename to crc24/crc24.go diff --git a/internal/crc24/crc_test.go b/crc24/crc_test.go similarity index 100% rename from internal/crc24/crc_test.go rename to crc24/crc_test.go diff --git a/internal/file_format/container.go b/file_format/container.go similarity index 97% rename from internal/file_format/container.go rename to file_format/container.go index 2fc4b19..284ee07 100644 --- a/internal/file_format/container.go +++ b/file_format/container.go @@ -28,7 +28,7 @@ import ( ) const ( - BytesPerLine = 24 + DefaultBytesPerLine = 24 ) const ( @@ -96,7 +96,7 @@ func (p *PaperCrypt) GetBinarySerialized() (string, error) { return "", errors.New("no data to serialize") } - return SerializeBinary(&p.Data, BytesPerLine), nil + return SerializeBinary(&p.Data, DefaultBytesPerLine), nil } func (p *PaperCrypt) GetDataLength() int { diff --git a/internal/file_format/container_binary.go b/file_format/container_binary.go similarity index 100% rename from internal/file_format/container_binary.go rename to file_format/container_binary.go diff --git a/internal/file_format/container_binary_test.go b/file_format/container_binary_test.go similarity index 99% rename from internal/file_format/container_binary_test.go rename to file_format/container_binary_test.go index b92c3fb..5da7a89 100644 --- a/internal/file_format/container_binary_test.go +++ b/file_format/container_binary_test.go @@ -27,7 +27,7 @@ import ( "testing" "time" - "github.com/tmuniversal/papercrypt/v3/internal/file_format/envelope" + "github.com/tmuniversal/papercrypt/v3/file_format/envelope" ) func TestBinaryRoundtrip(t *testing.T) { diff --git a/internal/file_format/container_decode.go b/file_format/container_decode.go similarity index 100% rename from internal/file_format/container_decode.go rename to file_format/container_decode.go diff --git a/internal/file_format/container_envelope.go b/file_format/container_envelope.go similarity index 95% rename from internal/file_format/container_envelope.go rename to file_format/container_envelope.go index 529b245..ff1a392 100644 --- a/internal/file_format/container_envelope.go +++ b/file_format/container_envelope.go @@ -24,7 +24,7 @@ import ( "errors" "strings" - "github.com/tmuniversal/papercrypt/v3/internal/file_format/envelope" + "github.com/tmuniversal/papercrypt/v3/file_format/envelope" ) func UnmarshalEnvelope(data string, opts ...envelope.CompressorOption) (*PaperCrypt, error) { diff --git a/internal/file_format/container_json.go b/file_format/container_json.go similarity index 100% rename from internal/file_format/container_json.go rename to file_format/container_json.go diff --git a/internal/file_format/container_pdf.go b/file_format/container_pdf.go similarity index 92% rename from internal/file_format/container_pdf.go rename to file_format/container_pdf.go index 2951574..01d0133 100644 --- a/internal/file_format/container_pdf.go +++ b/file_format/container_pdf.go @@ -29,10 +29,10 @@ import ( "github.com/makiuchi-d/gozxing" "github.com/makiuchi-d/gozxing/datamatrix" - "github.com/tmuniversal/papercrypt/v3/internal/codematrix" - "github.com/tmuniversal/papercrypt/v3/internal/crc24" - "github.com/tmuniversal/papercrypt/v3/internal/file_format/envelope" - "github.com/tmuniversal/papercrypt/v3/internal/pdf" + "github.com/tmuniversal/papercrypt/v3/codematrix" + "github.com/tmuniversal/papercrypt/v3/crc24" + "github.com/tmuniversal/papercrypt/v3/file_format/envelope" + "github.com/tmuniversal/papercrypt/v3/pdf" ) // GetPDF returns the binary representation of the paper crypt @@ -73,7 +73,7 @@ func (p *PaperCrypt) GetPDF(no2D bool, lowerCaseEncoding bool) ([]byte, error) { DataQRImage: qrImage, DataMatrixImage: dm.Bytes(), TextParts: parts, - BytesPerLine: BytesPerLine, + BytesPerLine: DefaultBytesPerLine, CRC24Polynomial: crc24.CRC24Polynomial, CRC24Initial: crc24.CRC24Initial, } diff --git a/internal/file_format/container_text.go b/file_format/container_text.go similarity index 98% rename from internal/file_format/container_text.go rename to file_format/container_text.go index e16be38..6e9a37f 100644 --- a/internal/file_format/container_text.go +++ b/file_format/container_text.go @@ -33,9 +33,9 @@ import ( "github.com/ProtonMail/gopenpgp/v3/crypto" "github.com/caarlos0/log" + "github.com/tmuniversal/papercrypt/v3/crc24" "github.com/tmuniversal/papercrypt/v3/internal" - "github.com/tmuniversal/papercrypt/v3/internal/crc24" - "github.com/tmuniversal/papercrypt/v3/internal/terminal" + "github.com/tmuniversal/papercrypt/v3/terminal" ) func (p *PaperCrypt) GetText(lowerCaseEncoding bool) ([]byte, error) { diff --git a/internal/file_format/envelope/compression.go b/file_format/envelope/compression.go similarity index 100% rename from internal/file_format/envelope/compression.go rename to file_format/envelope/compression.go diff --git a/internal/file_format/envelope/encoder.go b/file_format/envelope/encoder.go similarity index 100% rename from internal/file_format/envelope/encoder.go rename to file_format/envelope/encoder.go diff --git a/internal/file_format/envelope/envelope.go b/file_format/envelope/envelope.go similarity index 100% rename from internal/file_format/envelope/envelope.go rename to file_format/envelope/envelope.go diff --git a/internal/file_format/envelope/envelope_test.go b/file_format/envelope/envelope_test.go similarity index 100% rename from internal/file_format/envelope/envelope_test.go rename to file_format/envelope/envelope_test.go diff --git a/internal/file_format/envelope/header.go b/file_format/envelope/header.go similarity index 100% rename from internal/file_format/envelope/header.go rename to file_format/envelope/header.go diff --git a/internal/file_format/format_version.go b/file_format/format_version.go similarity index 100% rename from internal/file_format/format_version.go rename to file_format/format_version.go diff --git a/internal/file_format/serial.go b/file_format/serial.go similarity index 100% rename from internal/file_format/serial.go rename to file_format/serial.go diff --git a/internal/file_format/serialize.go b/file_format/serialize.go similarity index 94% rename from internal/file_format/serialize.go rename to file_format/serialize.go index 519020a..aa6c327 100644 --- a/internal/file_format/serialize.go +++ b/file_format/serialize.go @@ -30,7 +30,7 @@ import ( "strings" "github.com/ccoveille/go-safecast/v2" - "github.com/tmuniversal/papercrypt/v3/internal/crc24" + crc25 "github.com/tmuniversal/papercrypt/v3/crc24" ) type lineData struct { @@ -81,13 +81,13 @@ func SerializeBinary(data *[]byte, bytesPerLine int) string { line += fmt.Sprintf("%02X ", (*data)[i+j]) } - lineCRC24 := crc24.Checksum(dataLine) + lineCRC24 := crc25.Checksum(dataLine) line += fmt.Sprintf("%06X\n", lineCRC24) dataBlock = append(dataBlock, []byte(line)...) } - dataCRC24 := crc24.Checksum(*data) + dataCRC24 := crc25.Checksum(*data) finalLineNumber := max(int(lines+1), min(1, int(lines))) dataBlock = append(dataBlock, fmt.Appendf(nil, "%d: %06X\n", finalLineNumber, dataCRC24)...) @@ -132,7 +132,7 @@ func DeserializeBinary(data *[]byte) ([]byte, error) { // as lineParts contains sub-arrays of encoded bytes, the length of lineParts is equal to the number of bytes in the line + 1 (for the checksum) // a line must never contain no data, this a line must contain at least two parts, one byte and the checksum // (the last line, containing only the block checksum, is already handled above) - if len(lineParts) > BytesPerLine+1 || len(lineParts) < 2 { + if len(lineParts) > DefaultBytesPerLine+1 || len(lineParts) < 2 { return nil, fmt.Errorf("unexpected line length: line %s: %s", lineNumber, parts[1]) } @@ -161,13 +161,13 @@ func DeserializeBinary(data *[]byte) ([]byte, error) { CRC24: checksumData, } - if crc24.ValidateCRC24(lineData.Data, lineData.CRC24) { + if crc25.ValidateCRC24(lineData.Data, lineData.CRC24) { result = append(result, lineData) } else { return nil, fmt.Errorf( "invalid line checksum: line %d has checksum %06X, expected %06X", lineData.LineNumber, - crc24.Checksum(lineData.Data), + crc25.Checksum(lineData.Data), lineData.CRC24, ) } @@ -210,11 +210,11 @@ func DeserializeBinary(data *[]byte) ([]byte, error) { resultData = append(resultData, line.Data...) } - if !crc24.ValidateCRC24(resultData, blockCrc) { + if !crc25.ValidateCRC24(resultData, blockCrc) { return nil, fmt.Errorf( "invalid block checksum: expected %06X, found %06X (%d bytes)", blockCrc, - crc24.Checksum(resultData), + crc25.Checksum(resultData), len(resultData), ) } diff --git a/internal/file_format/serialize_test.go b/file_format/serialize_test.go similarity index 100% rename from internal/file_format/serialize_test.go rename to file_format/serialize_test.go diff --git a/papercrypt.go b/papercrypt.go index 611f420..c048282 100644 --- a/papercrypt.go +++ b/papercrypt.go @@ -30,7 +30,7 @@ import ( "github.com/charmbracelet/colorprofile" "github.com/tmuniversal/papercrypt/v3/cmd" "github.com/tmuniversal/papercrypt/v3/internal" - "github.com/tmuniversal/papercrypt/v3/internal/pdf" + "github.com/tmuniversal/papercrypt/v3/pdf" ) // LicenseText is the license of the application as a string diff --git a/internal/pdf/generator.go b/pdf/generator.go similarity index 100% rename from internal/pdf/generator.go rename to pdf/generator.go diff --git a/internal/pdf/mode_pgp.go b/pdf/mode_pgp.go similarity index 100% rename from internal/pdf/mode_pgp.go rename to pdf/mode_pgp.go diff --git a/internal/pdf/mode_raw.go b/pdf/mode_raw.go similarity index 100% rename from internal/pdf/mode_raw.go rename to pdf/mode_raw.go diff --git a/internal/pdf/pdf.go b/pdf/pdf.go similarity index 100% rename from internal/pdf/pdf.go rename to pdf/pdf.go diff --git a/internal/phrase_sheet/phrase_sheet.go b/phrase_sheet/phrase_sheet.go similarity index 99% rename from internal/phrase_sheet/phrase_sheet.go rename to phrase_sheet/phrase_sheet.go index fa5a705..1e94b0c 100644 --- a/internal/phrase_sheet/phrase_sheet.go +++ b/phrase_sheet/phrase_sheet.go @@ -36,7 +36,7 @@ import ( "github.com/makiuchi-d/gozxing" "github.com/makiuchi-d/gozxing/datamatrix" "github.com/tmuniversal/papercrypt/v3/internal" - "github.com/tmuniversal/papercrypt/v3/internal/pdf" + "github.com/tmuniversal/papercrypt/v3/pdf" ) // GenerateFromSeed uses a seeded, non-cryptographic PRNG so the sheet is diff --git a/internal/terminal/outputs.go b/terminal/outputs.go similarity index 100% rename from internal/terminal/outputs.go rename to terminal/outputs.go diff --git a/internal/terminal/read_password.go b/terminal/read_password.go similarity index 100% rename from internal/terminal/read_password.go rename to terminal/read_password.go diff --git a/internal/terminal/read_password_unix.go b/terminal/read_password_unix.go similarity index 100% rename from internal/terminal/read_password_unix.go rename to terminal/read_password_unix.go diff --git a/internal/terminal/read_password_windows.go b/terminal/read_password_windows.go similarity index 100% rename from internal/terminal/read_password_windows.go rename to terminal/read_password_windows.go diff --git a/internal/terminal/styles.go b/terminal/styles.go similarity index 100% rename from internal/terminal/styles.go rename to terminal/styles.go From 3373428935492e1a17d96d50c2946aaa07823810 Mon Sep 17 00:00:00 2001 From: Universal Studio <10200399+TMUniversal@users.noreply.github.com> Date: Sat, 29 Aug 2026 15:00:19 +0200 Subject: [PATCH 3/4] chore: update lint exclusions --- .golangci.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index be1dd00..b9c98a4 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -47,10 +47,8 @@ linters: - path: "cmd/scan_code.go" linters: - tagliatelle - - path: "internal/phrase_sheet.go" # this should be entirely fine, since the seed is generated with crypto/rand - text: "G404.*" - - path: "internal/phrase_sheet/phrase_sheet.go" + - path: "phrase_sheet/phrase_sheet.go" text: "G404.*" - # disable file inclusion via variable (gosec) here, it is a test file path: "cmd/decode_test.go" From b4912bc814f990d4fc8a20b270c5e6dc3ca59ba2 Mon Sep 17 00:00:00 2001 From: Universal Studio <10200399+TMUniversal@users.noreply.github.com> Date: Sat, 29 Aug 2026 15:03:31 +0200 Subject: [PATCH 4/4] refactor: fix import name accidentally incremented by goland --- file_format/serialize.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/file_format/serialize.go b/file_format/serialize.go index aa6c327..299f803 100644 --- a/file_format/serialize.go +++ b/file_format/serialize.go @@ -30,7 +30,7 @@ import ( "strings" "github.com/ccoveille/go-safecast/v2" - crc25 "github.com/tmuniversal/papercrypt/v3/crc24" + "github.com/tmuniversal/papercrypt/v3/crc24" ) type lineData struct { @@ -81,13 +81,13 @@ func SerializeBinary(data *[]byte, bytesPerLine int) string { line += fmt.Sprintf("%02X ", (*data)[i+j]) } - lineCRC24 := crc25.Checksum(dataLine) + lineCRC24 := crc24.Checksum(dataLine) line += fmt.Sprintf("%06X\n", lineCRC24) dataBlock = append(dataBlock, []byte(line)...) } - dataCRC24 := crc25.Checksum(*data) + dataCRC24 := crc24.Checksum(*data) finalLineNumber := max(int(lines+1), min(1, int(lines))) dataBlock = append(dataBlock, fmt.Appendf(nil, "%d: %06X\n", finalLineNumber, dataCRC24)...) @@ -161,13 +161,13 @@ func DeserializeBinary(data *[]byte) ([]byte, error) { CRC24: checksumData, } - if crc25.ValidateCRC24(lineData.Data, lineData.CRC24) { + if crc24.ValidateCRC24(lineData.Data, lineData.CRC24) { result = append(result, lineData) } else { return nil, fmt.Errorf( "invalid line checksum: line %d has checksum %06X, expected %06X", lineData.LineNumber, - crc25.Checksum(lineData.Data), + crc24.Checksum(lineData.Data), lineData.CRC24, ) } @@ -210,11 +210,11 @@ func DeserializeBinary(data *[]byte) ([]byte, error) { resultData = append(resultData, line.Data...) } - if !crc25.ValidateCRC24(resultData, blockCrc) { + if !crc24.ValidateCRC24(resultData, blockCrc) { return nil, fmt.Errorf( "invalid block checksum: expected %06X, found %06X (%d bytes)", blockCrc, - crc25.Checksum(resultData), + crc24.Checksum(resultData), len(resultData), ) }