Skip to content

Commit bb281b5

Browse files
fix: improve ffi safety (#247)
- *mut ptr to indicate ownership - Boxed slices instead of vecs for safe transfers - expanded checks for null ptrs Closes filecoin-project/rust-fil-ffi-toolkit#9
1 parent 791b682 commit bb281b5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+4813
-14305
lines changed

.circleci/config.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ jobs:
6767
- run: cd rust && cargo install cargo-lipo
6868
- build_project
6969
- compile_tests
70-
- ensure_generated_cgo_up_to_date
7170
publish_linux_staticlib:
7271
executor: golang
7372
steps:
@@ -305,13 +304,6 @@ commands:
305304
name: Build project without CGO
306305
command: env CGO_ENABLED=0 go build .
307306

308-
ensure_generated_cgo_up_to_date:
309-
steps:
310-
- run:
311-
name: Generate CGO bindings (using forked c-for-go) and compare with what's tracked by Git
312-
command: |
313-
make cgo-gen
314-
git diff --exit-code ./generated/
315307
run_tests:
316308
parameters:
317309
run_leak_detector:

Makefile

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ cgo-leakdetect: runner
3434
valgrind --leak-check=full --show-leak-kinds=definite ./runner
3535
.PHONY: cgo-leakdetect
3636

37-
cgo-gen: $(DEPS)
38-
go run github.com/xlab/c-for-go --nostamp filcrypto.yml
39-
.PHONY: cgo-gen
40-
4137
runner: $(DEPS)
4238
rm -f ./runner
4339
go build -o ./runner ./cgoleakdetect/

README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,6 @@ $ make -C extern/filecoin-ffi
8282
$ go mod edit -replace=github.com/filecoin-project/filecoin-ffi=./extern/filecoin-ffi
8383
```
8484

85-
## Updating CGO Bindings
86-
87-
The CGO bindings are generated using [c-for-go](https://github.com/xlab/c-for-go)
88-
and committed to Git. To generate bindings yourself, install the c-for-go
89-
binary, ensure that it's on your path, and then run `make cgo-gen`. CI builds
90-
will fail if generated CGO diverges from what's checked into Git.
91-
9285
## Updating the Changelog
9386

9487
The `mkreleaselog` script (in the project root) can be used to generate a good

bls.go

Lines changed: 38 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,16 @@ package ffi
99
// #include "./filcrypto.h"
1010
import "C"
1111
import (
12-
"github.com/filecoin-project/filecoin-ffi/generated"
12+
"github.com/filecoin-project/filecoin-ffi/cgo"
1313
)
1414

1515
// Hash computes the digest of a message
1616
func Hash(message Message) Digest {
17-
resp := generated.FilHash(message, uint(len(message)))
18-
resp.Deref()
19-
resp.Digest.Deref()
20-
21-
defer generated.FilDestroyHashResponse(resp)
22-
23-
var out Digest
24-
copy(out[:], resp.Digest.Inner[:])
25-
return out
17+
digest := cgo.Hash(cgo.AsSliceRefUint8(message))
18+
if digest == nil {
19+
return Digest{}
20+
}
21+
return *digest
2622
}
2723

2824
// Verify verifies that a signature is the aggregated signature of digests - pubkeys
@@ -38,9 +34,11 @@ func Verify(signature *Signature, digests []Digest, publicKeys []PublicKey) bool
3834
copy(flattenedPublicKeys[(PublicKeyBytes*idx):(PublicKeyBytes*(1+idx))], publicKey[:])
3935
}
4036

41-
isValid := generated.FilVerify(signature[:], flattenedDigests, uint(len(flattenedDigests)), flattenedPublicKeys, uint(len(flattenedPublicKeys)))
42-
43-
return isValid > 0
37+
return cgo.Verify(
38+
cgo.AsSliceRefUint8(signature[:]),
39+
cgo.AsSliceRefUint8(flattenedDigests),
40+
cgo.AsSliceRefUint8(flattenedPublicKeys),
41+
)
4442
}
4543

4644
// HashVerify verifies that a signature is the aggregated signature of hashed messages.
@@ -57,9 +55,12 @@ func HashVerify(signature *Signature, messages []Message, publicKeys []PublicKey
5755
copy(flattenedPublicKeys[(PublicKeyBytes*idx):(PublicKeyBytes*(1+idx))], publicKey[:])
5856
}
5957

60-
isValid := generated.FilHashVerify(signature[:], flattenedMessages, uint(len(flattenedMessages)), messagesSizes, uint(len(messagesSizes)), flattenedPublicKeys, uint(len(flattenedPublicKeys)))
61-
62-
return isValid > 0
58+
return cgo.HashVerify(
59+
cgo.AsSliceRefUint8(signature[:]),
60+
cgo.AsSliceRefUint8(flattenedMessages),
61+
cgo.AsSliceRefUint(messagesSizes),
62+
cgo.AsSliceRefUint8(flattenedPublicKeys),
63+
)
6364
}
6465

6566
// Aggregate aggregates signatures together into a new signature. If the
@@ -72,84 +73,43 @@ func Aggregate(signatures []Signature) *Signature {
7273
copy(flattenedSignatures[(SignatureBytes*idx):(SignatureBytes*(1+idx))], sig[:])
7374
}
7475

75-
resp := generated.FilAggregate(flattenedSignatures, uint(len(flattenedSignatures)))
76-
if resp == nil {
77-
return nil
78-
}
79-
80-
defer generated.FilDestroyAggregateResponse(resp)
81-
82-
resp.Deref()
83-
resp.Signature.Deref()
84-
85-
var out Signature
86-
copy(out[:], resp.Signature.Inner[:])
87-
return &out
76+
return cgo.Aggregate(cgo.AsSliceRefUint8(flattenedSignatures))
8877
}
8978

9079
// PrivateKeyGenerate generates a private key
9180
func PrivateKeyGenerate() PrivateKey {
92-
resp := generated.FilPrivateKeyGenerate()
93-
resp.Deref()
94-
resp.PrivateKey.Deref()
95-
defer generated.FilDestroyPrivateKeyGenerateResponse(resp)
96-
97-
var out PrivateKey
98-
copy(out[:], resp.PrivateKey.Inner[:])
99-
return out
81+
key := cgo.PrivateKeyGenerate()
82+
if key == nil {
83+
return PrivateKey{}
84+
}
85+
return *key
10086
}
10187

102-
// PrivateKeyGenerate generates a private key in a predictable manner
88+
// PrivateKeyGenerate generates a private key in a predictable manner.
10389
func PrivateKeyGenerateWithSeed(seed PrivateKeyGenSeed) PrivateKey {
104-
var ary generated.Fil32ByteArray
105-
copy(ary.Inner[:], seed[:])
106-
107-
resp := generated.FilPrivateKeyGenerateWithSeed(ary)
108-
resp.Deref()
109-
resp.PrivateKey.Deref()
110-
defer generated.FilDestroyPrivateKeyGenerateResponse(resp)
111-
112-
var out PrivateKey
113-
copy(out[:], resp.PrivateKey.Inner[:])
114-
return out
90+
ary := cgo.AsByteArray32(seed[:])
91+
key := cgo.PrivateKeyGenerateWithSeed(&ary)
92+
if key == nil {
93+
return PrivateKey{}
94+
}
95+
return *key
11596
}
11697

11798
// PrivateKeySign signs a message
11899
func PrivateKeySign(privateKey PrivateKey, message Message) *Signature {
119-
resp := generated.FilPrivateKeySign(privateKey[:], message, uint(len(message)))
120-
resp.Deref()
121-
resp.Signature.Deref()
122-
123-
defer generated.FilDestroyPrivateKeySignResponse(resp)
124-
125-
var signature Signature
126-
copy(signature[:], resp.Signature.Inner[:])
127-
return &signature
100+
return cgo.PrivateKeySign(cgo.AsSliceRefUint8(privateKey[:]), cgo.AsSliceRefUint8(message))
128101
}
129102

130103
// PrivateKeyPublicKey gets the public key for a private key
131-
func PrivateKeyPublicKey(privateKey PrivateKey) PublicKey {
132-
resp := generated.FilPrivateKeyPublicKey(privateKey[:])
133-
resp.Deref()
134-
resp.PublicKey.Deref()
135-
136-
defer generated.FilDestroyPrivateKeyPublicKeyResponse(resp)
137-
138-
var publicKey PublicKey
139-
copy(publicKey[:], resp.PublicKey.Inner[:])
140-
return publicKey
104+
func PrivateKeyPublicKey(privateKey PrivateKey) *PublicKey {
105+
return cgo.PrivateKeyPublicKey(cgo.AsSliceRefUint8(privateKey[:]))
141106
}
142107

143108
// CreateZeroSignature creates a zero signature, used as placeholder in filecoin.
144109
func CreateZeroSignature() Signature {
145-
resp := generated.FilCreateZeroSignature()
146-
resp.Deref()
147-
resp.Signature.Deref()
148-
149-
defer generated.FilDestroyZeroSignatureResponse(resp)
150-
151-
var sig Signature
152-
copy(sig[:], resp.Signature.Inner[:])
153-
154-
return sig
110+
signature := cgo.CreateZeroSignature()
111+
if signature == nil {
112+
return Signature{}
113+
}
114+
return *signature
155115
}

bls_test.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,30 +51,30 @@ func TestBLSSigningAndVerification(t *testing.T) {
5151
aggregateSign := Aggregate([]Signature{*fooSignature, *barSignature})
5252

5353
// assert the foo message was signed with the foo key
54-
assert.True(t, Verify(fooSignature, []Digest{fooDigest}, []PublicKey{fooPublicKey}))
54+
assert.True(t, Verify(fooSignature, []Digest{fooDigest}, []PublicKey{*fooPublicKey}))
5555

5656
// assert the bar message was signed with the bar key
57-
assert.True(t, Verify(barSignature, []Digest{barDigest}, []PublicKey{barPublicKey}))
57+
assert.True(t, Verify(barSignature, []Digest{barDigest}, []PublicKey{*barPublicKey}))
5858

5959
// assert the foo message was signed with the foo key
60-
assert.True(t, HashVerify(fooSignature, []Message{fooMessage}, []PublicKey{fooPublicKey}))
60+
assert.True(t, HashVerify(fooSignature, []Message{fooMessage}, []PublicKey{*fooPublicKey}))
6161

6262
// assert the bar message was signed with the bar key
63-
assert.True(t, HashVerify(barSignature, []Message{barMessage}, []PublicKey{barPublicKey}))
63+
assert.True(t, HashVerify(barSignature, []Message{barMessage}, []PublicKey{*barPublicKey}))
6464

6565
// assert the foo message was not signed by the bar key
66-
assert.False(t, Verify(fooSignature, []Digest{fooDigest}, []PublicKey{barPublicKey}))
66+
assert.False(t, Verify(fooSignature, []Digest{fooDigest}, []PublicKey{*barPublicKey}))
6767

6868
// assert the bar/foo message was not signed by the foo/bar key
69-
assert.False(t, Verify(barSignature, []Digest{barDigest}, []PublicKey{fooPublicKey}))
70-
assert.False(t, Verify(barSignature, []Digest{fooDigest}, []PublicKey{barPublicKey}))
71-
assert.False(t, Verify(fooSignature, []Digest{barDigest}, []PublicKey{fooPublicKey}))
69+
assert.False(t, Verify(barSignature, []Digest{barDigest}, []PublicKey{*fooPublicKey}))
70+
assert.False(t, Verify(barSignature, []Digest{fooDigest}, []PublicKey{*barPublicKey}))
71+
assert.False(t, Verify(fooSignature, []Digest{barDigest}, []PublicKey{*fooPublicKey}))
7272

7373
//assert the foo and bar message was signed with the foo and bar key
74-
assert.True(t, HashVerify(aggregateSign, []Message{fooMessage, barMessage}, []PublicKey{fooPublicKey, barPublicKey}))
74+
assert.True(t, HashVerify(aggregateSign, []Message{fooMessage, barMessage}, []PublicKey{*fooPublicKey, *barPublicKey}))
7575

7676
//assert the bar and foo message was not signed by the foo and bar key
77-
assert.False(t, HashVerify(aggregateSign, []Message{fooMessage, barMessage}, []PublicKey{fooPublicKey}))
77+
assert.False(t, HashVerify(aggregateSign, []Message{fooMessage, barMessage}, []PublicKey{*fooPublicKey}))
7878
}
7979

8080
func BenchmarkBLSVerify(b *testing.B) {
@@ -90,7 +90,7 @@ func BenchmarkBLSVerify(b *testing.B) {
9090

9191
b.ResetTimer()
9292
for i := 0; i < b.N; i++ {
93-
if !Verify(sig, []Digest{digest}, []PublicKey{pubk}) {
93+
if !Verify(sig, []Digest{digest}, []PublicKey{*pubk}) {
9494
b.Fatal("failed to verify")
9595
}
9696
}
@@ -132,7 +132,7 @@ func benchmarkBLSVerifyBatchSize(size int) func(b *testing.B) {
132132
sig := PrivateKeySign(priv, msg)
133133
sigs = append(sigs, *sig)
134134
pubk := PrivateKeyPublicKey(priv)
135-
pubks = append(pubks, pubk)
135+
pubks = append(pubks, *pubk)
136136
}
137137

138138
t := time.Now()
@@ -161,7 +161,7 @@ func BenchmarkBLSHashAndVerify(b *testing.B) {
161161
b.ResetTimer()
162162
for i := 0; i < b.N; i++ {
163163
digest := Hash(msg)
164-
if !Verify(sig, []Digest{digest}, []PublicKey{pubk}) {
164+
if !Verify(sig, []Digest{digest}, []PublicKey{*pubk}) {
165165
b.Fatal("failed to verify")
166166
}
167167
}
@@ -179,7 +179,7 @@ func BenchmarkBLSHashVerify(b *testing.B) {
179179

180180
b.ResetTimer()
181181
for i := 0; i < b.N; i++ {
182-
if !HashVerify(sig, []Message{msg}, []PublicKey{pubk}) {
182+
if !HashVerify(sig, []Message{msg}, []PublicKey{*pubk}) {
183183
b.Fatal("failed to verify")
184184
}
185185
}

build.sh

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ rustup target add x86_64-apple-darwin --toolchain $(cat rust-toolchain)
99
rustup target add aarch64-apple-darwin --toolchain $(cat rust-toolchain)
1010
cargo update -p "filecoin-proofs-api"
1111
cargo install cargo-lipo
12-
cargo install cbindgen
13-
cbindgen --clean --config cbindgen.toml --crate filcrypto --output ../include/filcrypto.h
1412
cd ..
1513
FFI_BUILD_FROM_SOURCE=1 make
16-
make cgo-gen
1714
go mod tidy

cgo/bls.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package cgo
2+
3+
/*
4+
#cgo LDFLAGS: -L${SRCDIR}/..
5+
#cgo pkg-config: ${SRCDIR}/../filcrypto.pc
6+
#include "../filcrypto.h"
7+
#include <stdlib.h>
8+
*/
9+
import "C"
10+
11+
func Hash(message SliceRefUint8) *[96]byte {
12+
resp := C.hash(message)
13+
defer resp.destroy()
14+
return resp.copyAsArray()
15+
}
16+
17+
func Aggregate(flattenedSignatures SliceRefUint8) *[96]byte {
18+
resp := C.aggregate(flattenedSignatures)
19+
defer resp.destroy()
20+
return resp.copyAsArray()
21+
}
22+
23+
func Verify(signature SliceRefUint8, flattenedDigests SliceRefUint8, flattenedPublicKeys SliceRefUint8) bool {
24+
resp := C.verify(signature, flattenedDigests, flattenedPublicKeys)
25+
return bool(resp)
26+
}
27+
28+
func HashVerify(signature SliceRefUint8, flattenedMessages SliceRefUint8, messageSizes SliceRefUint, flattenedPublicKeys SliceRefUint8) bool {
29+
resp := C.hash_verify(signature, flattenedMessages, messageSizes, flattenedPublicKeys)
30+
return bool(resp)
31+
}
32+
33+
func PrivateKeyGenerate() *[32]byte {
34+
resp := C.private_key_generate()
35+
defer resp.destroy()
36+
return resp.copyAsArray()
37+
}
38+
39+
func PrivateKeyGenerateWithSeed(rawSeed *ByteArray32) *[32]byte {
40+
resp := C.private_key_generate_with_seed(rawSeed)
41+
defer resp.destroy()
42+
return resp.copyAsArray()
43+
}
44+
45+
func PrivateKeySign(rawPrivateKey SliceRefUint8, message SliceRefUint8) *[96]byte {
46+
resp := C.private_key_sign(rawPrivateKey, message)
47+
defer resp.destroy()
48+
return resp.copyAsArray()
49+
}
50+
51+
func PrivateKeyPublicKey(rawPrivateKey SliceRefUint8) *[48]byte {
52+
resp := C.private_key_public_key(rawPrivateKey)
53+
defer resp.destroy()
54+
return resp.copyAsArray()
55+
}
56+
57+
func CreateZeroSignature() *[96]byte {
58+
resp := C.create_zero_signature()
59+
defer resp.destroy()
60+
return resp.copyAsArray()
61+
}

0 commit comments

Comments
 (0)