|
16 | 16 |
|
17 | 17 | package crypto |
18 | 18 |
|
19 | | -import "errors" |
| 19 | +// #cgo CFLAGS: -Wall -std=c99 |
| 20 | +// #cgo darwin,amd64 CFLAGS: -I${SRCDIR}/libs/darwin/amd64/include |
| 21 | +// #cgo darwin,amd64 LDFLAGS: ${SRCDIR}/libs/darwin/amd64/lib/libsodium.a |
| 22 | +// #cgo darwin,arm64 CFLAGS: -I${SRCDIR}/libs/darwin/arm64/include |
| 23 | +// #cgo darwin,arm64 LDFLAGS: ${SRCDIR}/libs/darwin/arm64/lib/libsodium.a |
| 24 | +// #cgo linux,amd64 CFLAGS: -I${SRCDIR}/libs/linux/amd64/include |
| 25 | +// #cgo linux,amd64 LDFLAGS: ${SRCDIR}/libs/linux/amd64/lib/libsodium.a |
| 26 | +// #cgo linux,arm64 CFLAGS: -I${SRCDIR}/libs/linux/arm64/include |
| 27 | +// #cgo linux,arm64 LDFLAGS: ${SRCDIR}/libs/linux/arm64/lib/libsodium.a |
| 28 | +// #cgo linux,arm CFLAGS: -I${SRCDIR}/libs/linux/arm/include |
| 29 | +// #cgo linux,arm LDFLAGS: ${SRCDIR}/libs/linux/arm/lib/libsodium.a |
| 30 | +// #cgo windows,amd64 CFLAGS: -I${SRCDIR}/libs/windows/amd64/include |
| 31 | +// #cgo windows,amd64 LDFLAGS: ${SRCDIR}/libs/windows/amd64/lib/libsodium.a |
| 32 | +// #include <stdint.h> |
| 33 | +// #include "sodium.h" |
| 34 | +// enum { |
| 35 | +// sizeofPtr = sizeof(void*), |
| 36 | +// sizeofULongLong = sizeof(unsigned long long), |
| 37 | +// }; |
| 38 | +import "C" |
| 39 | +import ( |
| 40 | + "errors" |
| 41 | + "unsafe" |
| 42 | +) |
20 | 43 |
|
21 | 44 | // BatchVerifier enqueues signatures to be validated in batch. |
22 | 45 | type BatchVerifier struct { |
23 | | - messages []Hashable // contains a slice of messages to be hashed. Each message is varible length |
24 | | - publicKeys []SignatureVerifier // contains a slice of public keys. Each individual public key is 32 bytes. |
25 | | - signatures []Signature // contains a slice of signatures keys. Each individual signature is 64 bytes. |
| 46 | + messages []Hashable // contains a slice of messages to be hashed. Each message is varible length |
| 47 | + publicKeys []SignatureVerifier // contains a slice of public keys. Each individual public key is 32 bytes. |
| 48 | + signatures []Signature // contains a slice of signatures keys. Each individual signature is 64 bytes. |
| 49 | + useBatchVerification bool |
26 | 50 | } |
27 | 51 |
|
28 | 52 | const minBatchVerifierAlloc = 16 |
29 | 53 |
|
30 | 54 | // Batch verifications errors |
31 | 55 | var ( |
32 | 56 | ErrBatchVerificationFailed = errors.New("At least one signature didn't pass verification") |
33 | | - ErrZeroTranscationsInBatch = errors.New("Could not validate empty signature set") |
| 57 | + ErrZeroTransactionInBatch = errors.New("Could not validate empty signature set") |
34 | 58 | ) |
35 | 59 |
|
| 60 | +//export ed25519_randombytes_unsafe |
| 61 | +func ed25519_randombytes_unsafe(p unsafe.Pointer, len C.size_t) { |
| 62 | + randBuf := (*[1 << 30]byte)(p)[:len:len] |
| 63 | + RandBytes(randBuf) |
| 64 | +} |
| 65 | + |
| 66 | +// MakeBatchVerifierWithAlgorithmDefaultSize create a BatchVerifier instance. This function pre-allocates |
| 67 | +// amount of free space to enqueue signatures without expanding. this function always use the batch |
| 68 | +// verification algorithm |
| 69 | +func MakeBatchVerifierWithAlgorithmDefaultSize() *BatchVerifier { |
| 70 | + return MakeBatchVerifier(minBatchVerifierAlloc, true) |
| 71 | +} |
| 72 | + |
36 | 73 | // MakeBatchVerifierDefaultSize create a BatchVerifier instance. This function pre-allocates |
37 | | -// amount of free space to enqueue signatures without exapneding |
38 | | -func MakeBatchVerifierDefaultSize() *BatchVerifier { |
39 | | - return MakeBatchVerifier(minBatchVerifierAlloc) |
| 74 | +// amount of free space to enqueue signatures without expanding |
| 75 | +func MakeBatchVerifierDefaultSize(enableBatchVerification bool) *BatchVerifier { |
| 76 | + return MakeBatchVerifier(minBatchVerifierAlloc, enableBatchVerification) |
40 | 77 | } |
41 | 78 |
|
42 | 79 | // MakeBatchVerifier create a BatchVerifier instance. This function pre-allocates |
43 | 80 | // a given space so it will not expaned the storage |
44 | | -func MakeBatchVerifier(hint int) *BatchVerifier { |
| 81 | +func MakeBatchVerifier(hint int, enableBatchVerification bool) *BatchVerifier { |
45 | 82 | // preallocate enough storage for the expected usage. We will reallocate as needed. |
46 | 83 | if hint < minBatchVerifierAlloc { |
47 | 84 | hint = minBatchVerifierAlloc |
48 | 85 | } |
49 | 86 | return &BatchVerifier{ |
50 | | - messages: make([]Hashable, 0, hint), |
51 | | - publicKeys: make([]SignatureVerifier, 0, hint), |
52 | | - signatures: make([]Signature, 0, hint), |
| 87 | + messages: make([]Hashable, 0, hint), |
| 88 | + publicKeys: make([]SignatureVerifier, 0, hint), |
| 89 | + signatures: make([]Signature, 0, hint), |
| 90 | + useBatchVerification: enableBatchVerification, |
53 | 91 | } |
54 | 92 | } |
55 | 93 |
|
@@ -85,14 +123,69 @@ func (b *BatchVerifier) GetNumberOfEnqueuedSignatures() int { |
85 | 123 | // if the batch is zero an appropriate error is return. |
86 | 124 | func (b *BatchVerifier) Verify() error { |
87 | 125 | if b.GetNumberOfEnqueuedSignatures() == 0 { |
88 | | - return ErrZeroTranscationsInBatch |
| 126 | + return ErrZeroTransactionInBatch |
89 | 127 | } |
90 | 128 |
|
| 129 | + if b.useBatchVerification { |
| 130 | + var messages = make([][]byte, b.GetNumberOfEnqueuedSignatures()) |
| 131 | + for i, m := range b.messages { |
| 132 | + messages[i] = hashRep(m) |
| 133 | + } |
| 134 | + if batchVerificationImpl(messages, b.publicKeys, b.signatures) { |
| 135 | + return nil |
| 136 | + } |
| 137 | + return ErrBatchVerificationFailed |
| 138 | + } |
| 139 | + return b.verifyOneByOne() |
| 140 | +} |
| 141 | + |
| 142 | +func (b *BatchVerifier) verifyOneByOne() error { |
91 | 143 | for i := range b.messages { |
92 | | - verifier := SignatureVerifier(b.publicKeys[i]) |
93 | | - if !verifier.Verify(b.messages[i], b.signatures[i]) { |
| 144 | + verifier := b.publicKeys[i] |
| 145 | + if !verifier.Verify(b.messages[i], b.signatures[i], false) { |
94 | 146 | return ErrBatchVerificationFailed |
95 | 147 | } |
96 | 148 | } |
97 | 149 | return nil |
98 | 150 | } |
| 151 | + |
| 152 | +// batchVerificationImpl invokes the ed25519 batch verification algorithm. |
| 153 | +// it returns true if all the signatures were authentically signed by the owners |
| 154 | +func batchVerificationImpl(messages [][]byte, publicKeys []SignatureVerifier, signatures []Signature) bool { |
| 155 | + |
| 156 | + numberOfSignatures := len(messages) |
| 157 | + |
| 158 | + messagesAllocation := C.malloc(C.size_t(C.sizeofPtr * numberOfSignatures)) |
| 159 | + messagesLenAllocation := C.malloc(C.size_t(C.sizeofULongLong * numberOfSignatures)) |
| 160 | + publicKeysAllocation := C.malloc(C.size_t(C.sizeofPtr * numberOfSignatures)) |
| 161 | + signaturesAllocation := C.malloc(C.size_t(C.sizeofPtr * numberOfSignatures)) |
| 162 | + valid := C.malloc(C.size_t(C.sizeof_int * numberOfSignatures)) |
| 163 | + |
| 164 | + defer func() { |
| 165 | + // release staging memory |
| 166 | + C.free(messagesAllocation) |
| 167 | + C.free(messagesLenAllocation) |
| 168 | + C.free(publicKeysAllocation) |
| 169 | + C.free(signaturesAllocation) |
| 170 | + C.free(valid) |
| 171 | + }() |
| 172 | + |
| 173 | + // load all the data pointers into the array pointers. |
| 174 | + for i := 0; i < numberOfSignatures; i++ { |
| 175 | + *(*uintptr)(unsafe.Pointer(uintptr(messagesAllocation) + uintptr(i*C.sizeofPtr))) = uintptr(unsafe.Pointer(&messages[i][0])) |
| 176 | + *(*C.ulonglong)(unsafe.Pointer(uintptr(messagesLenAllocation) + uintptr(i*C.sizeofULongLong))) = C.ulonglong(len(messages[i])) |
| 177 | + *(*uintptr)(unsafe.Pointer(uintptr(publicKeysAllocation) + uintptr(i*C.sizeofPtr))) = uintptr(unsafe.Pointer(&publicKeys[i][0])) |
| 178 | + *(*uintptr)(unsafe.Pointer(uintptr(signaturesAllocation) + uintptr(i*C.sizeofPtr))) = uintptr(unsafe.Pointer(&signatures[i][0])) |
| 179 | + } |
| 180 | + |
| 181 | + // call the batch verifier |
| 182 | + allValid := C.crypto_sign_ed25519_open_batch( |
| 183 | + (**C.uchar)(unsafe.Pointer(messagesAllocation)), |
| 184 | + (*C.ulonglong)(unsafe.Pointer(messagesLenAllocation)), |
| 185 | + (**C.uchar)(unsafe.Pointer(publicKeysAllocation)), |
| 186 | + (**C.uchar)(unsafe.Pointer(signaturesAllocation)), |
| 187 | + C.size_t(len(messages)), |
| 188 | + (*C.int)(unsafe.Pointer(valid))) |
| 189 | + |
| 190 | + return allValid == 0 |
| 191 | +} |
0 commit comments