-
Notifications
You must be signed in to change notification settings - Fork 2
[WTEL-9696] cryptostore encryption #470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| package presign | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "encoding/base64" | ||
| "encoding/binary" | ||
| "encoding/hex" | ||
| "fmt" | ||
| "strconv" | ||
|
|
||
| "github.com/webitel/crypto/cryptobox" | ||
| ) | ||
|
|
||
| // modern cryptobox.Cipher implementation | ||
| type Crypto struct { | ||
| box cryptobox.Cipher | ||
| } | ||
|
|
||
| // NewCryptoBox implements modern ciphertext encryption strategy | ||
| func NewCryptoBox(box cryptobox.Cipher) (PreSign, error) { | ||
| cbox := Crypto{box} | ||
| err := cbox.init() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return cbox, nil | ||
| } | ||
|
|
||
| // lazy init | ||
| func (c *Crypto) init() (err error) { | ||
| if c.box != nil { | ||
| return nil // already | ||
| } | ||
| // Load environment configuration | ||
| c.box, err = cryptobox.Default() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // OK | ||
| return nil | ||
| } | ||
|
|
||
| var _ PreSign = Crypto{} | ||
|
|
||
| var hashSum = hash | ||
|
|
||
| func (c Crypto) Generate(data []byte) (string, error) { | ||
|
|
||
| err := c.init() | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| sign := hashSum(data) | ||
| sign, err = c.box.Encrypt( | ||
| context.Background(), sign, | ||
| ) | ||
| if err != nil { | ||
| // failed to encrypt hash of the given data message | ||
| return "", err | ||
| } | ||
|
|
||
| return hex.EncodeToString(sign), nil | ||
| } | ||
|
|
||
| func (c Crypto) Valid(plaintext string, signature string) bool { | ||
|
|
||
| sign, err := hex.DecodeString(signature) | ||
| if err != nil { | ||
| // failed to decode signature | ||
| return false | ||
| } | ||
|
|
||
| err = c.init() | ||
| if err != nil { | ||
| return false | ||
| } | ||
|
|
||
| // v2 | ||
| sign, err = c.box.Decrypt( | ||
| context.Background(), sign, | ||
| ) | ||
| if err != nil { | ||
| // failed to decrypt signature | ||
| return false | ||
| } | ||
|
|
||
| // verify | ||
| want := hashSum([]byte(plaintext)) | ||
| return bytes.Equal(want, sign) | ||
| } | ||
|
|
||
| func (c Crypto) EncryptId(id int64) (string, error) { | ||
| text, err := c.encryptText( | ||
| binary.AppendVarint(nil, id), | ||
| ) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
| return string(text), nil | ||
| } | ||
|
|
||
| func (c Crypto) DecryptId(key string) (int64, error) { | ||
| // v2 encryption | ||
| data, err := c.decryptText([]byte(key)) | ||
| if err != nil { | ||
| return 0, err | ||
| } | ||
| num, n := binary.Varint(data) | ||
| // if n <= 0 { | ||
| // // invalid input ; not integer encrypted | ||
| // return 0, strconv.ErrSyntax | ||
| // } | ||
| if n != len(data) { | ||
| // read too short | ||
| return 0, strconv.ErrSyntax | ||
| } | ||
|
Comment on lines
+110
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- candidate files ---'
git ls-files | grep -E '(^|/)(crypto\.go|.*presign.*|.*crypto.*)' | head -80
printf '%s\n' '--- crypto.go outline ---'
if command -v ast-grep >/dev/null 2>&1; then
ast-grep outline pkg/presign/crypto.go
else
wc -l pkg/presign/crypto.go
fi
printf '%s\n' '--- relevant implementation ---'
cat -n pkg/presign/crypto.go | sed -n '1,180p'
printf '%s\n' '--- related symbols ---'
rg -n -C 4 'DecryptId|binary\.Varint|Varint|EncryptId|presign' pkg/presign --glob '*.go' || true
printf '%s\n' '--- binary.Varint behavioral probe ---'
cat >/tmp/varint_probe.go <<'EOF'
package main
import (
"encoding/binary"
"fmt"
)
func main() {
inputs := [][]byte{
nil,
{},
{0},
{1},
{0x80},
{0x80, 0x01},
{0x80, 0x80},
}
for _, data := range inputs {
num, n := binary.Varint(data)
fmt.Printf("%#v -> num=%d n=%d len=%d\n", data, num, n, len(data))
}
}
EOF
if command -v go >/dev/null 2>&1; then
go run /tmp/varint_probe.go
else
echo 'go unavailable; source-level semantics will be used'
fi
rm -f /tmp/varint_probe.goRepository: webitel/engine Length of output: 15196 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- crypto.go remainder ---'
cat -n pkg/presign/crypto.go | sed -n '150,210p'
printf '%s\n' '--- v2 dispatch ---'
cat -n pkg/presign/v2.go | sed -n '1,110p'
printf '%s\n' '--- legacy id implementation ---'
cat -n pkg/presign/presign.go | sed -n '90,155p'
printf '%s\n' '--- number and byte tests ---'
cat -n pkg/presign/presign_test.go | sed -n '120,225p'
printf '%s\n' '--- cryptobox references and module metadata ---'
rg -n -C 3 'cryptobox|DecryptId|EncryptBytes|DecryptBytes' --glob '*.go' --glob 'go.mod' --glob 'go.sum' .Repository: webitel/engine Length of output: 14195 🌐 Web query:
💡 Result: The webitel/crypto/cryptobox library is designed for application-layer encryption in Go [1][2]. While specific documentation on empty plaintext behavior is not explicitly detailed in the high-level READMEs, the library follows standard cryptographic practices for authenticated encryption (AEAD) [1][2]. In systems using NaCl-based or similar AEAD cryptographic constructions, encrypting an empty plaintext is technically valid and results in a non-empty ciphertext [3]. The ciphertext includes cryptographic overhead, such as authentication tags (e.g., Poly1305) and potentially ephemeral public keys, ensuring that the empty input remains authenticated and secure [3]. Therefore, you should expect the Encrypt function to return a successful result (a non-empty string or byte slice) rather than an error or an empty string, and the Decrypt function to correctly recover the original empty plaintext [3]. If you encounter issues where empty strings are rejected, it is likely due to an application-level validation check rather than a limitation of the underlying cryptographic primitives [4][5]. Always verify that your implementation handles the resulting ciphertext length correctly, as it will be larger than the original zero-length input due to this mandatory cryptographic overhead [3]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
api='https://api.github.com/repos/webitel/crypto/contents/cryptobox?ref=v0.2.0'
printf '%s\n' '--- cryptobox directory ---'
curl -fsSL "$api" | jq -r '.[] | [.name, .download_url] | `@tsv`'
printf '%s\n' '--- matching implementation files ---'
urls=$(curl -fsSL "$api" | jq -r '.[].download_url // empty')
for url in $urls; do
body=$(curl -fsSL "$url")
if printf '%s' "$body" | grep -Eq 'func .*Encrypt|func .*Decrypt|type .*Cipher|interface'; then
printf '%s\n' "--- $url ---"
printf '%s\n' "$body" | grep -n -E -A25 -B8 'func .*Encrypt|func .*Decrypt|type .*Cipher|interface'
fi
doneRepository: webitel/engine Length of output: 316 🏁 Script executed: #!/bin/bash
set -eu
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL \
'https://proxy.golang.org/github.com/webitel/crypto/cryptobox/@v/v0.2.0.zip' \
-o "$tmpdir/cryptobox.zip"
unzip -q "$tmpdir/cryptobox.zip" -d "$tmpdir/src"
printf '%s\n' '--- matching source files ---'
find "$tmpdir/src" -type f -name '*.go' -print
printf '%s\n' '--- Encrypt/Decrypt implementations ---'
rg -n -C 25 'func .*Encrypt|func .*Decrypt|type .*Cipher|interface' \
"$tmpdir/src" --glob '*.go'Repository: webitel/engine Length of output: 41723 Reject empty varint payloads. 🤖 Prompt for AI Agents |
||
| return num, nil | ||
| } | ||
|
|
||
| func (c Crypto) EncryptBytes(v []byte) ([]byte, error) { | ||
| // from business logic: output supposed to be the cipher TEXT (printable, NOT raw bytes) | ||
| return c.encryptText(v) | ||
| } | ||
|
|
||
| func (c Crypto) DecryptBytes(v []byte) ([]byte, error) { | ||
| // expect TEXT bytes ; see: c.EncryptBytes() | ||
| return c.decryptText(v) | ||
| } | ||
|
|
||
| const cipherTag = ".c1" | ||
| var cipherText = base64.RawURLEncoding | ||
|
|
||
| func (c *Crypto) encryptText(data []byte) (text []byte, err error) { | ||
|
|
||
| err = c.init() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| blob, err := c.box.Encrypt( | ||
| context.Background(), data, | ||
| ) | ||
| if err != nil { | ||
| // failed to encrypt sensitive data | ||
| return nil, err | ||
| } | ||
|
|
||
| // v2 | ||
| // return cipherTag + cipherText.EncodeToString(blob), nil | ||
| return cipherText.AppendEncode([]byte(cipherTag), blob), nil | ||
| } | ||
|
|
||
| func (c *Crypto) decryptText(text []byte) (data []byte, err error) { | ||
|
|
||
| text, v2 := bytes.CutPrefix(text, []byte(cipherTag)) | ||
| if !v2 { | ||
| return nil, fmt.Errorf("presign: invalid syntax") | ||
| } | ||
|
|
||
| blob, err := cipherText.AppendDecode(nil, text) | ||
| if err != nil { | ||
| // failed to decode ciphertext | ||
| return nil, err | ||
| } | ||
|
|
||
| err = c.init() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| data, err = c.box.Decrypt( | ||
| context.Background(), blob, | ||
| ) | ||
| if err != nil { | ||
| // failed to decrypt cipherdata | ||
| return nil, err | ||
| } | ||
|
|
||
| // OK | ||
| return data, nil | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,11 @@ | ||
| module github.com/webitel/engine/pkg/presign | ||
|
|
||
| go 1.24.1 | ||
| go 1.25.0 | ||
|
|
||
| require github.com/webitel/crypto/cryptobox v0.2.0 | ||
|
|
||
| require ( | ||
| github.com/webitel/crypto/env v0.1.0 // indirect | ||
| golang.org/x/crypto v0.54.0 // indirect | ||
| golang.org/x/sys v0.47.0 // indirect | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| github.com/webitel/crypto/cryptobox v0.2.0 h1:6KUza8aa9eBreI3aUFLJjkssvi8LYANcemM0vSBuNVE= | ||
| github.com/webitel/crypto/cryptobox v0.2.0/go.mod h1:td/OI3xSEbMl+gW3b84QESdR20uD3KHHsma59ke7mmQ= | ||
| github.com/webitel/crypto/env v0.1.0 h1:bIWQgAwPcThnuX6gOQmWUubVTZrnnavukya8dSXZwRc= | ||
| github.com/webitel/crypto/env v0.1.0/go.mod h1:/jyzvmqAY6KGRwiV4oCWaBAeZOW51j1JdgswUnIPxqo= | ||
| golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= | ||
| golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= | ||
| golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= | ||
| golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: webitel/engine
Length of output: 925
🏁 Script executed:
Repository: webitel/engine
Length of output: 32229
🏁 Script executed:
Repository: webitel/engine
Length of output: 7393
Make
UserPassworda defined type and update its conversions.The alias makes the encrypted scanner match every
*stringtarget, includingName,Login, and host fields. Usetype UserPassword string. Convert the patch assignment and bothencryptTextarguments. Adapt the scanner sodecryptTextreceives*string.🤖 Prompt for AI Agents