-
Notifications
You must be signed in to change notification settings - Fork 475
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
POC: use wrapper function for batch verification #5695
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#include "sodium.h" | ||
int ed25519_batch_wrapper(const unsigned char *messages1D, | ||
const unsigned long long *mlen, | ||
const unsigned char *publicKeys1D, | ||
const unsigned char *signatures1D, | ||
size_t num, | ||
int *valid) { | ||
int ret; | ||
const unsigned char **messages2D, **publicKeys2D, **signatures2D; | ||
messages2D = malloc(num * sizeof(unsigned char *)); | ||
publicKeys2D = malloc(num * sizeof(unsigned char *)); | ||
signatures2D = malloc(num * sizeof(unsigned char *)); | ||
|
||
// fill 2-D arrays for messages, pks, sigs from provided 1-D arrays | ||
unsigned long long mpos = 0; | ||
for (size_t i = 0; i < num; i++) { | ||
messages2D[i] = &messages1D[mpos]; | ||
mpos += mlen[i]; | ||
publicKeys2D[i] = &publicKeys1D[i*crypto_sign_ed25519_PUBLICKEYBYTES]; | ||
signatures2D[i] = &signatures1D[i*crypto_sign_ed25519_BYTES]; | ||
} | ||
ret = crypto_sign_ed25519_open_batch(messages2D, mlen, publicKeys2D, signatures2D, num, valid); | ||
|
||
free(messages2D); | ||
free(publicKeys2D); | ||
free(signatures2D); | ||
return ret; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,23 +30,28 @@ package crypto | |
// #cgo windows,amd64 CFLAGS: -I${SRCDIR}/libs/windows/amd64/include | ||
// #cgo windows,amd64 LDFLAGS: ${SRCDIR}/libs/windows/amd64/lib/libsodium.a | ||
// #include <stdint.h> | ||
// #include "sodium.h" | ||
// enum { | ||
// sizeofPtr = sizeof(void*), | ||
// sizeofULongLong = sizeof(unsigned long long), | ||
// }; | ||
// int ed25519_batch_wrapper(const unsigned char *messages1D, | ||
// const unsigned long long *mlen, | ||
// const unsigned char *publicKeys1D, | ||
// const unsigned char *signatures1D, | ||
// size_t num, | ||
// int *valid_p); | ||
import "C" | ||
import ( | ||
"errors" | ||
"runtime" | ||
"unsafe" | ||
) | ||
|
||
// BatchVerifier enqueues signatures to be validated in batch. | ||
type BatchVerifier struct { | ||
messages []Hashable // contains a slice of messages to be hashed. Each message is varible length | ||
publicKeys []SignatureVerifier // contains a slice of public keys. Each individual public key is 32 bytes. | ||
signatures []Signature // contains a slice of signatures keys. Each individual signature is 64 bytes. | ||
messageHashReps []byte // contains a slice of concatenated bytes of the HashRep of the messages to be hashed. Each message is varible length | ||
messageLens []C.ulonglong // the lengths of each message in messageHashReps | ||
publicKeys []byte // contains a slice of concatenated public keys. Each individual public key is 32 bytes. | ||
signatures []byte // contains a slice of concatenated signatures. Each individual signature is 64 bytes. | ||
Comment on lines
+53
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this change from proper indexed type to byte? |
||
} | ||
|
||
const minBatchVerifierAlloc = 16 | ||
|
@@ -75,38 +80,44 @@ func MakeBatchVerifierWithHint(hint int) *BatchVerifier { | |
hint = minBatchVerifierAlloc | ||
} | ||
return &BatchVerifier{ | ||
messages: make([]Hashable, 0, hint), | ||
publicKeys: make([]SignatureVerifier, 0, hint), | ||
signatures: make([]Signature, 0, hint), | ||
messageHashReps: make([]byte, 0), // XXX can we get a better hint? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The hint is 16 for streamed transactions, and cannot be improved without guessing. As for the message size, for blocks it can be calculated, but not for the case of streamed transactions. |
||
messageLens: make([]C.ulonglong, hint), | ||
publicKeys: make([]byte, 0, hint*ed25519PublicKeySize), | ||
signatures: make([]byte, 0, hint*ed25519SignatureSize), | ||
} | ||
} | ||
|
||
// EnqueueSignature enqueues a signature to be enqueued | ||
func (b *BatchVerifier) EnqueueSignature(sigVerifier SignatureVerifier, message Hashable, sig Signature) { | ||
// do we need to reallocate ? | ||
if len(b.messages) == cap(b.messages) { | ||
if len(b.messageLens) == cap(b.messageLens) { | ||
b.expand() | ||
} | ||
b.messages = append(b.messages, message) | ||
b.publicKeys = append(b.publicKeys, sigVerifier) | ||
b.signatures = append(b.signatures, sig) | ||
msgHashRep := HashRep(message) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the opposite of what we need to do. |
||
b.messageHashReps = append(b.messageHashReps, msgHashRep...) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the first guaranteed extra copy we are doing here. In the current code, this copy does not exist. the |
||
b.messageLens = append(b.messageLens, C.ulonglong(len(msgHashRep))) | ||
b.publicKeys = append(b.publicKeys, sigVerifier[:]...) | ||
b.signatures = append(b.signatures, sig[:]...) | ||
} | ||
|
||
func (b *BatchVerifier) expand() { | ||
messages := make([]Hashable, len(b.messages), len(b.messages)*2) | ||
publicKeys := make([]SignatureVerifier, len(b.publicKeys), len(b.publicKeys)*2) | ||
signatures := make([]Signature, len(b.signatures), len(b.signatures)*2) | ||
copy(messages, b.messages) | ||
messageHashReps := make([]byte, len(b.messageHashReps), len(b.messageHashReps)*2) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potentially, many more copies are made here, which were not done in the past. This is a regression, and will also impact the block validation. |
||
messageLens := make([]C.ulonglong, len(b.messageLens), len(b.messageLens)*2) | ||
publicKeys := make([]byte, len(b.publicKeys), len(b.publicKeys)*2*ed25519PublicKeySize) | ||
signatures := make([]byte, len(b.signatures), len(b.signatures)*2*ed25519SignatureSize) | ||
copy(messageLens, b.messageLens) | ||
copy(messageHashReps, b.messageHashReps) | ||
copy(publicKeys, b.publicKeys) | ||
copy(signatures, b.signatures) | ||
b.messages = messages | ||
b.messageHashReps = messageHashReps | ||
b.messageLens = messageLens | ||
b.publicKeys = publicKeys | ||
b.signatures = signatures | ||
} | ||
|
||
// GetNumberOfEnqueuedSignatures returns the number of signatures currently enqueued into the BatchVerifier | ||
func (b *BatchVerifier) GetNumberOfEnqueuedSignatures() int { | ||
return len(b.messages) | ||
return len(b.messageLens) | ||
} | ||
|
||
// Verify verifies that all the signatures are valid. in that case nil is returned | ||
|
@@ -123,11 +134,7 @@ func (b *BatchVerifier) VerifyWithFeedback() (failed []bool, err error) { | |
if b.GetNumberOfEnqueuedSignatures() == 0 { | ||
return nil, nil | ||
} | ||
var messages = make([][]byte, b.GetNumberOfEnqueuedSignatures()) | ||
for i := range b.messages { | ||
messages[i] = HashRep(b.messages[i]) | ||
} | ||
allValid, failed := batchVerificationImpl(messages, b.publicKeys, b.signatures) | ||
allValid, failed := batchVerificationImpl(b.messageHashReps, b.messageLens, b.publicKeys, b.signatures) | ||
if allValid { | ||
return failed, nil | ||
} | ||
|
@@ -137,50 +144,23 @@ func (b *BatchVerifier) VerifyWithFeedback() (failed []bool, err error) { | |
// batchVerificationImpl invokes the ed25519 batch verification algorithm. | ||
// it returns true if all the signatures were authentically signed by the owners | ||
// otherwise, returns false, and sets the indexes of the failed sigs in failed | ||
func batchVerificationImpl(messages [][]byte, publicKeys []SignatureVerifier, signatures []Signature) (allSigsValid bool, failed []bool) { | ||
|
||
numberOfSignatures := len(messages) | ||
|
||
messagesAllocation := C.malloc(C.size_t(C.sizeofPtr * numberOfSignatures)) | ||
messagesLenAllocation := C.malloc(C.size_t(C.sizeofULongLong * numberOfSignatures)) | ||
publicKeysAllocation := C.malloc(C.size_t(C.sizeofPtr * numberOfSignatures)) | ||
signaturesAllocation := C.malloc(C.size_t(C.sizeofPtr * numberOfSignatures)) | ||
valid := C.malloc(C.size_t(C.sizeof_int * numberOfSignatures)) | ||
|
||
defer func() { | ||
// release staging memory | ||
C.free(messagesAllocation) | ||
C.free(messagesLenAllocation) | ||
C.free(publicKeysAllocation) | ||
C.free(signaturesAllocation) | ||
C.free(valid) | ||
}() | ||
|
||
// load all the data pointers into the array pointers. | ||
for i := 0; i < numberOfSignatures; i++ { | ||
*(*uintptr)(unsafe.Pointer(uintptr(messagesAllocation) + uintptr(i*C.sizeofPtr))) = uintptr(unsafe.Pointer(&messages[i][0])) | ||
*(*C.ulonglong)(unsafe.Pointer(uintptr(messagesLenAllocation) + uintptr(i*C.sizeofULongLong))) = C.ulonglong(len(messages[i])) | ||
*(*uintptr)(unsafe.Pointer(uintptr(publicKeysAllocation) + uintptr(i*C.sizeofPtr))) = uintptr(unsafe.Pointer(&publicKeys[i][0])) | ||
*(*uintptr)(unsafe.Pointer(uintptr(signaturesAllocation) + uintptr(i*C.sizeofPtr))) = uintptr(unsafe.Pointer(&signatures[i][0])) | ||
} | ||
func batchVerificationImpl(messageHashReps []byte, messageLens []C.ulonglong, publicKeys []byte, signatures []byte) (allSigsValid bool, failed []bool) { | ||
numberOfSignatures := len(messageLens) | ||
|
||
valid := make([]C.int, numberOfSignatures) | ||
|
||
// call the batch verifier | ||
allValid := C.crypto_sign_ed25519_open_batch( | ||
(**C.uchar)(unsafe.Pointer(messagesAllocation)), | ||
(*C.ulonglong)(unsafe.Pointer(messagesLenAllocation)), | ||
(**C.uchar)(unsafe.Pointer(publicKeysAllocation)), | ||
(**C.uchar)(unsafe.Pointer(signaturesAllocation)), | ||
C.size_t(len(messages)), | ||
(*C.int)(unsafe.Pointer(valid))) | ||
|
||
runtime.KeepAlive(messages) | ||
runtime.KeepAlive(publicKeys) | ||
runtime.KeepAlive(signatures) | ||
allValid := C.ed25519_batch_wrapper( | ||
(*C.uchar)(&messageHashReps[0]), | ||
(*C.ulonglong)(&messageLens[0]), | ||
(*C.uchar)(&publicKeys[0]), | ||
(*C.uchar)(&signatures[0]), | ||
C.size_t(numberOfSignatures), | ||
(*C.int)(&valid[0])) | ||
|
||
failed = make([]bool, numberOfSignatures) | ||
for i := 0; i < numberOfSignatures; i++ { | ||
cint := *(*C.int)(unsafe.Pointer(uintptr(valid) + uintptr(i*C.sizeof_int))) | ||
failed[i] = (cint == 0) | ||
failed[i] = (valid[i] == 0) | ||
} | ||
return allValid == 0, failed | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These "same" operations are done in go. What is special about doing this in a C wrapper function?