From 77b01dddc97069123a5927d69678c8c18215d8d0 Mon Sep 17 00:00:00 2001 From: alienx5499 Date: Sat, 14 Feb 2026 12:44:25 +0530 Subject: [PATCH] fix peek error, cipherw constant, and file close handling --- cmd/tle/tle.go | 8 ++++++-- tlock.go | 11 +++++++---- tlock_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/cmd/tle/tle.go b/cmd/tle/tle.go index 0cd505c..fbe8c92 100644 --- a/cmd/tle/tle.go +++ b/cmd/tle/tle.go @@ -48,7 +48,9 @@ func run() error { return fmt.Errorf("failed to open input file %q: %v", name, err) } defer func(f *os.File) { - err = f.Close() + if closeErr := f.Close(); closeErr != nil && err == nil { + err = fmt.Errorf("close input file: %w", closeErr) + } }(f) src = f } @@ -60,7 +62,9 @@ func run() error { return fmt.Errorf("failed to open output file %q: %v", name, err) } defer func(f *os.File) { - err = f.Close() + if closeErr := f.Close(); closeErr != nil && err == nil { + err = fmt.Errorf("close output file: %w", closeErr) + } }(f) dst = f } diff --git a/tlock.go b/tlock.go index 569e2f4..b43068a 100644 --- a/tlock.go +++ b/tlock.go @@ -89,7 +89,8 @@ func (t Tlock) Encrypt(dst io.Writer, src io.Reader, roundNumber uint64) (err er func (t Tlock) Decrypt(dst io.Writer, src io.Reader) error { rr := bufio.NewReader(src) - if start, _ := rr.Peek(len(armor.Header)); string(start) == armor.Header { + start, err := rr.Peek(len(armor.Header)) + if err == nil && string(start) == armor.Header { src = armor.NewReader(rr) } else { src = rr @@ -223,6 +224,8 @@ func TimeUnlock(scheme crypto.Scheme, publicKey kyber.Point, beacon chain.Beacon // ============================================================================= // These constants define the size of the different CipherDEK fields. +// cipherWLen is distinct from cipherVLen even if equal; keep constants separate +// to preserve format invariants. const ( cipherVLen = 16 cipherWLen = 16 @@ -261,10 +264,10 @@ func BytesToCiphertext(scheme crypto.Scheme, b []byte) (*ibe.Ciphertext, error) cipherV := make([]byte, cipherVLen) copy(cipherV, b[kyberPointLen:kyberPointLen+cipherVLen]) - cipherW := make([]byte, cipherVLen) + cipherW := make([]byte, cipherWLen) copy(cipherW, b[kyberPointLen+cipherVLen:]) - if len(b[kyberPointLen+cipherVLen:]) != cipherVLen { - return nil, fmt.Errorf("invalid ciphertext length: %d", len(b[kyberPointLen+cipherVLen:])) + if len(b[kyberPointLen+cipherVLen:]) != cipherWLen { + return nil, fmt.Errorf("invalid ciphertext W length: got %d, want %d", len(b[kyberPointLen+cipherVLen:]), cipherWLen) } u := scheme.KeyGroup.Point() diff --git a/tlock_test.go b/tlock_test.go index de14ec6..3dc15c3 100644 --- a/tlock_test.go +++ b/tlock_test.go @@ -376,6 +376,45 @@ UnBmtsw6U2LlKh8iDf0E1PfwDenmKFfQaAGm0WLxdlzP8Q== }) } +func TestBytesToCiphertext_MalformedInput(t *testing.T) { + network, err := fixed.FromInfo(`{"public_key":"83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a","period":3,"genesis_time":1692803367,"genesis_seed":"f477d5c89f21a17c863a7f937c6a6d15859414d2be09cd448d4279af331c5d3e","chain_hash":"52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971","scheme":"bls-unchained-g1-rfc9380","beacon_id":"quicknet"}`) + require.NoError(t, err) + scheme := network.Scheme() + kyberPointLen := scheme.KeyGroup.PointLen() + validLen := kyberPointLen + 16 + 16 + + tests := []struct { + name string + input []byte + substr string + }{ + {"truncated missing W", make([]byte, kyberPointLen+16), "incorrect length"}, + {"empty", nil, "incorrect length"}, + {"too long", make([]byte, validLen+10), "incorrect length"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := tlock.BytesToCiphertext(scheme, tt.input) + require.Error(t, err) + require.Contains(t, err.Error(), tt.substr, "error should be explicit about length") + require.Contains(t, err.Error(), "got", "crypto errors should include actual value") + }) + } +} + +func TestDecrypt_PeekShortRead(t *testing.T) { + network, err := fixed.FromInfo(`{"public_key":"83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a","period":3,"genesis_time":1692803367,"genesis_seed":"f477d5c89f21a17c863a7f937c6a6d15859414d2be09cd448d4279af331c5d3e","chain_hash":"52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971","scheme":"bls-unchained-g1-rfc9380","beacon_id":"quicknet"}`) + require.NoError(t, err) + + // input shorter than armor header (32 bytes); Peek will fail/short-read + // should fallback to non-armor path and age.Decrypt will return error + shortInput := strings.NewReader("short") + var dst bytes.Buffer + + err = tlock.New(network).Decrypt(&dst, shortInput) + require.Error(t, err, "short input should not panic; Decrypt should return age error") +} + func TestInteropWithJS(t *testing.T) { t.Run("on Mainnet with G1 sigs", func(t *testing.T) { cipher := `-----BEGIN AGE ENCRYPTED FILE-----