diff --git a/crypto/ed25519/migration.go b/crypto/ed25519/migration.go index 9fc1f406e..e2aca8abf 100644 --- a/crypto/ed25519/migration.go +++ b/crypto/ed25519/migration.go @@ -10,78 +10,127 @@ import ( ) // vrf w/o prove -type vrfNoProve interface { +type VrfNoProve interface { Verify(pubKey ed25519.PublicKey, proof []byte, message []byte) (bool, []byte) ProofToHash(proof []byte) ([]byte, error) + ValidateProof(proof []byte) error } -var vrfs = map[int]vrfNoProve{ - voivrf.ProofSize: &voi{}, - r2vrf.ProofSize: &r2{}, +var globalVrf = NewVersionedVrfNoProve() + +func VRFVerify(pubKey ed25519.PublicKey, proof []byte, message []byte) (bool, []byte) { + return globalVrf.Verify(pubKey, proof, message) +} + +func ProofToHash(proof []byte) ([]byte, error) { + return globalVrf.ProofToHash(proof) +} + +// ValidateProof returns an error if the proof is not empty, but its +// size != vrf.ProofSize. +func ValidateProof(h []byte) error { + if len(h) > 0 { + if err := globalVrf.ValidateProof(h); err != nil { + return fmt.Errorf("expected size to be %d bytes, got %d bytes", + voivrf.ProofSize, + len(h), + ) + } + } + return nil } -func getVrf(proof []byte) (vrfNoProve, error) { +// versioned vrf +var _ VrfNoProve = (*versionedVrfNoProve)(nil) + +type versionedVrfNoProve struct { + version int +} + +func NewVersionedVrfNoProve() VrfNoProve { + return &versionedVrfNoProve{} +} + +func (v versionedVrfNoProve) getVrf(proof []byte) (VrfNoProve, error) { + vrfs := map[int]VrfNoProve{ + 0: &r2VrfNoProve{}, + 1: &voiVrfNoProve{}, + } + proofSizeToVersion := map[int]int{ + r2vrf.ProofSize: 0, + voivrf.ProofSize: 1, + } + proofSize := len(proof) - if vrf, exists := vrfs[proofSize]; exists { - return vrf, nil + if version, exists := proofSizeToVersion[proofSize]; exists && version >= v.version { + v.version = version + return vrfs[version], nil } return nil, fmt.Errorf("invalid proof size: %d", proofSize) } -func VRFVerify(pubKey ed25519.PublicKey, proof []byte, message []byte) (bool, []byte) { - vrf, err := getVrf(proof) +func (v versionedVrfNoProve) Verify(pubKey ed25519.PublicKey, proof []byte, message []byte) (bool, []byte) { + vrf, err := v.getVrf(proof) if err != nil { return false, nil } - return vrf.Verify(pubKey, proof, message) } -func ProofToHash(proof []byte) ([]byte, error) { - vrf, err := getVrf(proof) +func (v versionedVrfNoProve) ProofToHash(proof []byte) ([]byte, error) { + vrf, err := v.getVrf(proof) if err != nil { return nil, err } - return vrf.ProofToHash(proof) } -// ValidateProof returns an error if the proof is not empty, but its -// size != vrf.ProofSize. -func ValidateProof(h []byte) error { - if len(h) > 0 { - if _, err := getVrf(h); err != nil { - return fmt.Errorf("expected size to be %d bytes, got %d bytes", - voivrf.ProofSize, - len(h), - ) - } +func (v versionedVrfNoProve) ValidateProof(proof []byte) error { + vrf, err := v.getVrf(proof) + if err != nil { + return err } - return nil + return vrf.ValidateProof(proof) } // github.com/oasisprotocol/curve25519-voi -var _ vrfNoProve = (*voi)(nil) +var _ VrfNoProve = (*voiVrfNoProve)(nil) -type voi struct{} +type voiVrfNoProve struct{} -func (_ voi) Verify(pubKey ed25519.PublicKey, proof []byte, message []byte) (bool, []byte) { +func (_ voiVrfNoProve) Verify(pubKey ed25519.PublicKey, proof []byte, message []byte) (bool, []byte) { return voivrf.Verify(pubKey, proof, message) } -func (_ voi) ProofToHash(proof []byte) ([]byte, error) { +func (_ voiVrfNoProve) ProofToHash(proof []byte) ([]byte, error) { return voivrf.ProofToHash(proof) } +func (_ voiVrfNoProve) ValidateProof(proof []byte) error { + proofSize := len(proof) + if proofSize != voivrf.ProofSize { + return fmt.Errorf("invalid proof size: %d", proofSize) + } + return nil +} + // github.com/r2ishiguro/vrf -var _ vrfNoProve = (*r2)(nil) +var _ VrfNoProve = (*r2VrfNoProve)(nil) -type r2 struct{} +type r2VrfNoProve struct{} -func (_ r2) Verify(pubKey ed25519.PublicKey, proof []byte, message []byte) (bool, []byte) { +func (_ r2VrfNoProve) Verify(pubKey ed25519.PublicKey, proof []byte, message []byte) (bool, []byte) { return r2vrf.Verify(pubKey, proof, message) } -func (_ r2) ProofToHash(proof []byte) ([]byte, error) { +func (_ r2VrfNoProve) ProofToHash(proof []byte) ([]byte, error) { return r2vrf.ProofToHash(proof) } + +func (_ r2VrfNoProve) ValidateProof(proof []byte) error { + proofSize := len(proof) + if proofSize != r2vrf.ProofSize { + return fmt.Errorf("invalid proof size: %d", proofSize) + } + return nil +} diff --git a/crypto/ed25519/migration_test.go b/crypto/ed25519/migration_test.go index fdf5be3aa..7e3008341 100644 --- a/crypto/ed25519/migration_test.go +++ b/crypto/ed25519/migration_test.go @@ -11,7 +11,11 @@ import ( r2vrf "github.com/Finschia/ostracon/crypto/ed25519/internal/r2ishiguro" ) -func TestVRFVerify(t *testing.T) { +func TestVerify(t *testing.T) { + pubkey, message := []byte("pubkey"), []byte("message") + valid, _ := ed25519.VRFVerify(pubkey, make([]byte, 1), message) + require.False(t, valid) + cases := map[string]struct { proof []byte valid bool @@ -29,14 +33,16 @@ func TestVRFVerify(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - pubkey, message := []byte("pubkey"), []byte("message") - valid, _ := ed25519.VRFVerify(pubkey, tc.proof, message) + valid, _ := ed25519.NewVersionedVrfNoProve().Verify(pubkey, tc.proof, message) require.Equal(t, tc.valid, valid) }) } } func TestProofToHash(t *testing.T) { + _, err := ed25519.ProofToHash(make([]byte, 1)) + require.Error(t, err) + cases := map[string]struct { proof []byte valid bool @@ -55,7 +61,7 @@ func TestProofToHash(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - _, err := ed25519.ProofToHash(tc.proof) + _, err := ed25519.NewVersionedVrfNoProve().ProofToHash(tc.proof) if !tc.valid { require.Error(t, err) return @@ -66,14 +72,19 @@ func TestProofToHash(t *testing.T) { } func TestValidateProof(t *testing.T) { + err := ed25519.ValidateProof(make([]byte, 1)) + require.Error(t, err) + err = ed25519.ValidateProof(make([]byte, 0)) + require.NoError(t, err) + cases := map[string]struct { proof []byte valid bool }{ - "invalid": { + "invalid format": { proof: make([]byte, 1), }, - "voi proof": { + "voi invalid proof": { proof: make([]byte, voivrf.ProofSize), valid: true, }, @@ -85,7 +96,7 @@ func TestValidateProof(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - err := ed25519.ValidateProof(tc.proof) + err := ed25519.NewVersionedVrfNoProve().ValidateProof(tc.proof) if !tc.valid { require.Error(t, err) return @@ -94,3 +105,18 @@ func TestValidateProof(t *testing.T) { }) } } + +func TestVersionControl(t *testing.T) { + vrf := ed25519.NewVersionedVrfNoProve() + + // old one is valid for now + oldProof := make([]byte, r2vrf.ProofSize) + require.NoError(t, vrf.ValidateProof(oldProof)) + + // new one is valid + newProof := make([]byte, voivrf.ProofSize) + require.NoError(t, vrf.ValidateProof(newProof)) + + // old one is not valid anymore + require.NoError(t, vrf.ValidateProof(oldProof)) +}