Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions cmd/tle/tle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
11 changes: 7 additions & 4 deletions tlock.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
39 changes: 39 additions & 0 deletions tlock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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-----
Expand Down