Skip to content

signer panic in go1.24 unless crypto.PrivateKey.PublicKey() called #807

@sul3n3t

Description

@sul3n3t

Instructions

I created a minimal reproduction here using flow-go-sdk v1.3.3: https://github.com/sul3n3t/flow-panic

Problem

While updating to go 1.24, some existing code triggered a panic during signing. The panic is caused because stdlib crypto code expects fields of the public component of a private key to be present.

Steps to Reproduce

package flowpanic_test

import (
	"testing"

	"github.com/onflow/flow-go-sdk/crypto"
)

func TestSign(t *testing.T) {
	privateKey, err := crypto.GeneratePrivateKey(crypto.ECDSA_P256, make([]byte, 32))
	if err != nil {
		t.Fatal(err)
	}
	// privateKey.PublicKey() // necessary on go 1.24 to avoid panic

	signer, err := crypto.NewInMemorySigner(privateKey, crypto.SHA3_256)
	if err != nil {
		t.Fatal(err)
	}
	_, err = signer.Sign([]byte("some message"))
	if err != nil {
		t.Fatal(err)
	}
}

Results in

--- FAIL: TestSign (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x10 pc=0x1045aab08]

goroutine 36 [running]:
testing.tRunner.func1.2({0x1047afac0, 0x104958b50})
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/testing/testing.go:1734 +0x1ac
testing.tRunner.func1()
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/testing/testing.go:1737 +0x334
panic({0x1047afac0?, 0x104958b50?})
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/runtime/panic.go:787 +0x124
math/big.(*Int).Sign(...)
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/math/big/int.go:48
crypto/ecdsa.pointFromAffine({0x1047f9910?, 0x104959080?}, 0x0, 0x0)
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/crypto/ecdsa/ecdsa.go:416 +0x38
crypto/ecdsa.privateKeyToFIPS[...](0x140001a6e00, 0x1400019d440)
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/crypto/ecdsa/ecdsa.go:405 +0x3c
crypto/ecdsa.signFIPS[...](0x140001a6e00, 0x14000064cf8, {0x1047f7960?, 0x14000198160}, {0x140001b4480, 0x20, 0x20})
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/crypto/ecdsa/ecdsa.go:244 +0x70
crypto/ecdsa.SignASN1({0x1047f7960, 0x14000198160}, 0x1400019d440, {0x140001b4480, 0x20, 0x20})
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/crypto/ecdsa/ecdsa.go:227 +0x234
crypto/ecdsa.Sign({0x1047f7960?, 0x14000198160?}, 0x1?, {0x140001b4480?, 0x14000064e28?, 0x1045bafa8?})
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/crypto/ecdsa/ecdsa_legacy.go:65 +0x2c
github.com/onflow/crypto.(*prKeyECDSA).signHash(0x140002001e0, {0x140001b4480?, 0x14000064ea8?, 0x104487d7c?})
	/Users/justin/go/pkg/mod/github.com/onflow/crypto@v0.25.1/ecdsa.go:80 +0x54
github.com/onflow/crypto.(*prKeyECDSA).Sign(0x140002001e0, {0x140001f4660, 0xc, 0xc}, {0x1047f9880, 0x14000216000})
	/Users/justin/go/pkg/mod/github.com/onflow/crypto@v0.25.1/ecdsa.go:118 +0x1c0
github.com/onflow/flow-go-sdk/crypto.InMemorySigner.Sign(...)
	/Users/justin/go/pkg/mod/github.com/onflow/flow-go-sdk@v1.3.3/crypto/crypto.go:133
github.com/sul3n3t/flow-panic_test.TestSign(0x14000184c40)
	/Users/justin/go/src/flow-panic/sign_test.go:20 +0x124
testing.tRunner(0x14000184c40, 0x1047f4bc8)
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/testing/testing.go:1792 +0xe4
created by testing.(*T).Run in goroutine 1
	/Users/justin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.darwin-arm64/src/testing/testing.go:1851 +0x374
FAIL	github.com/sul3n3t/flow-panic	0.209s

Acceptance Criteria

The test is expected to pass without calling the .PublicKey() method

Context

I came across this while updating go in a much larger repo. I have a workaround by calling crypto.PrivateKey#PublicKey().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions