Skip to content
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

ellswift: introduce ElligatorSwift encoding and decoding funcs #2219

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Crypt-iQ
Copy link
Collaborator

@Crypt-iQ Crypt-iQ commented Jul 25, 2024

The BIP324 ElligatorSwift test vectors are also included. They can be found here and here. This code could be more optimized, possibly by not using so many new(FieldVal) invocations among some other things.

@Crypt-iQ Crypt-iQ added the btcec label Jul 25, 2024
@coveralls
Copy link

coveralls commented Jul 25, 2024

Pull Request Test Coverage Report for Build 11147308122

Details

  • 162 of 275 (58.91%) changed or added relevant lines in 1 file are covered.
  • 2 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.01%) to 57.244%

Changes Missing Coverage Covered Lines Changed/Added Lines %
btcec/ellswift.go 162 275 58.91%
Files with Coverage Reduction New Missed Lines %
connmgr/connmanager.go 2 87.32%
Totals Coverage Status
Change from base Build 10970025081: 0.01%
Covered Lines: 30023
Relevant Lines: 52447

💛 - Coveralls

@lnliz
Copy link

lnliz commented Jul 31, 2024

This is great! fwiw, I had great success using the csv files from the BIP324 directly for the tests and it gave me great test coverage too, see esp the big test running through all the "packet_encoding_test_vectors.csv" rows here

@saubyk
Copy link

saubyk commented Aug 21, 2024

cc: @ellemouton @ProofOfKeags for review

btcec/ellswift_test.go Outdated Show resolved Hide resolved
Copy link
Contributor

@ProofOfKeags ProofOfKeags left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks to be a correct implementation of the BIP324 gear.

btcec/ellswift.go Show resolved Hide resolved
btcec/ellswift.go Show resolved Hide resolved
btcec/ellswift.go Show resolved Hide resolved
btcec/ellswift.go Outdated Show resolved Hide resolved
btcec/ellswift.go Show resolved Hide resolved
btcec/ellswift.go Show resolved Hide resolved
btcec/ellswift.go Outdated Show resolved Hide resolved
@Crypt-iQ Crypt-iQ force-pushed the ellswift branch 2 times, most recently from 7227ec8 to 20aa98d Compare September 27, 2024 04:00
@Crypt-iQ
Copy link
Collaborator Author

Crypt-iQ commented Oct 2, 2024

This is great! fwiw, I had great success using the csv files from the BIP324 directly for the tests and it gave me great test coverage too, see esp the big test running through all the "packet_encoding_test_vectors.csv" rows here

I don't have a strong opinion on whether we should add the csv files directly. In other parts of the codebase (i.e. other BIP test vectors), we just have the test vectors in the format that I have in this PR

The BIP324 ElligatorSwift test vectors are also included.
@saubyk
Copy link

saubyk commented Oct 2, 2024

cc @ellemouton for review comments on this pr. Thanks

@saubyk
Copy link

saubyk commented Oct 7, 2024

cc: @kcalvinalvin for review

)

func init() {
c.SetByteSlice(cBytes[:])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: instead of relying on the init function you should also be able to do this in the var section

_ = c.SetByteSlice(cBytes[:])

return nil, err
}

if initiating {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think would it make sense to only have the ellswiftOurs / ellswiftTheirs order toggle in the if to make clear that everything else stays the same?

Something like this:

	var msg []byte

	if initiating {
		// Initiating, place our public key encoding first.
		var msg []byte
		msg = append(msg, ellswiftOurs[:]...)
		msg = append(msg, ellswiftTheirs[:]...)
	} else {
		msg = append(msg, ellswiftTheirs[:]...)
		msg = append(msg, ellswiftOurs[:]...)
	}

	msg = append(msg, ecdhPoint[:]...)
	return chainhash.TaggedHash(ellswiftTag, msg), nil

Copy link
Collaborator

@kcalvinalvin kcalvinalvin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through bip324 and compared the functions specified in the bip and in the reference code with the code here

return chainhash.TaggedHash(ellswiftTag, msg), nil
}

var msg []byte
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can pre-allocate here

msg := make([]byte, 0, 64+64+32)

// point exists on the curve, it returns true and false otherwise.
// TODO: Use quadratic residue formula instead (see: BIP340)?
func liftX(x *FieldVal) (*PublicKey, bool) {
ySqr := new(FieldVal).Add(x).Square().Mul(x).AddInt(7)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using the DecompressY() function?

DecompressY(x, false, y)
pubKey := NewPublicKey(x, y)
return pubKey, pubKey.IsOnCurve()

var randPrivKeyBytes [64]byte

// Generate a random private key
_, err := rand.Read(randPrivKeyBytes[:])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dumb question but is the reason why we don't have to get a random number between 1..p-1 is because PrivKeyFromBytes does a modulo if the given bytes are over p-1?

)

// TestXSwiftECVectors checks the BIP324 test vectors for the XSwiftEC function.
func TestXSwiftECVectors(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sanity checked all the values in both of the tests to see if they match the test vector in the bip

quadYSqr := new(FieldVal).Add(ySqr).MulInt(4)
firstX := new(FieldVal).Add(u).Add(quadYSqr)

firstXCurve := new(FieldVal).Add(firstX).Square().Mul(firstX).AddInt(7)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe refactor the check for if x is on the curve into a separate function since it's used 3 times?

}

// Should have found a square above.
panic("unreachable - no calculated x-values were square")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about the panic here. If there's a bug somewhere in the library and we reach this part of the code, it'll crash the nodes. Returning an error will allow us to gracefully shut the node down in case we reach here.

rhs := new(FieldVal).Add(uSqr).MulInt(3).Mul(s)

// Add the two terms together and multiply by -s.
lhs.Add(rhs).Normalize().Mul(s).Negate(1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it not matter if we call Normalize() during or after? Asking since

lhs.Add(rhs).Mul(s).Negate(1).Normalize()

passes the tests and the BIP specifies that the modulo be done afterwards

return nil
}

switch caseNum & 5 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like we can reduce code duplication here

@litecomb
Copy link

litecomb commented Nov 2, 2024

WriteV2MessageN should allow specify the garbage []byte, if nil default garbage is used
ReadV2MessageN should allow reading the garbage []byte

Rationale: this will permit custom nonstandard packets communication in the protocol, while still being compatible with bitcoin.

@litecomb
Copy link

litecomb commented Nov 9, 2024

// transportVersion is the transport version we are currently using.
transportVersion = []byte{}

packet likely needed here to make inbound connections work with respect to bitcoin 0.27.1 acting as client

@litecomb
Copy link

litecomb commented Nov 9, 2024

when bitcoin 27.1 is connecting as a client, we don't find his garbage terminator inside CompleteHandshake() and it gets stuck.
Also I don't get why // Send version packet. is generated packet but not sent the packet.
But at least when btcd is the client it seems to work. But not when btcd is the server.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants