Skip to content

Commit 534345d

Browse files
authored
Merge pull request #49 from threefoldtech/development_crypto_propertytests
Property testing crypto package
2 parents aaa01ed + 9b0b4d6 commit 534345d

1 file changed

Lines changed: 313 additions & 0 deletions

File tree

pkg/crypto/crypto_property_test.go

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
package crypto
2+
3+
import (
4+
"bytes"
5+
"crypto/ed25519"
6+
"math/rand"
7+
"reflect"
8+
"testing"
9+
"testing/quick"
10+
)
11+
12+
const (
13+
// NaClSealedBoxOverhead represents the overhead added by NaCl sealed box encryption
14+
// (32 bytes ephemeral key + 16 bytes MAC)
15+
NaClSealedBoxOverhead = 48
16+
)
17+
18+
type keyPair struct {
19+
PublicKey ed25519.PublicKey
20+
PrivateKey ed25519.PrivateKey
21+
}
22+
23+
// Generate implements quick.Generator for keyPair
24+
func (keyPair) Generate(rand *rand.Rand, size int) reflect.Value {
25+
pub, priv, err := ed25519.GenerateKey(rand)
26+
if err != nil {
27+
// Return zero value if key generation fails
28+
// This will cause the test to fail gracefully rather than panic
29+
return reflect.ValueOf(keyPair{})
30+
}
31+
kp := keyPair{
32+
PublicKey: pub,
33+
PrivateKey: priv,
34+
}
35+
return reflect.ValueOf(kp)
36+
}
37+
38+
// randomMessage generates a random byte slice for testing
39+
type randomMessage []byte
40+
41+
// Generate implements quick.Generator for randomMessage
42+
func (randomMessage) Generate(rand *rand.Rand, size int) reflect.Value {
43+
// Generate message size between 1 and 1024 bytes
44+
msgSize := 1 + rand.Intn(1024)
45+
message := make([]byte, msgSize)
46+
rand.Read(message)
47+
return reflect.ValueOf(randomMessage(message))
48+
}
49+
func encryptThenDecrypt(message []byte, publicKey ed25519.PublicKey, privateKey ed25519.PrivateKey) ([]byte, []byte, error) {
50+
encrypted, err := Encrypt(message, publicKey)
51+
if err != nil {
52+
return nil, nil, err
53+
}
54+
55+
decrypted, err := Decrypt(encrypted, privateKey)
56+
if err != nil {
57+
return encrypted, nil, err
58+
}
59+
60+
return encrypted, decrypted, nil
61+
}
62+
63+
func encryptThenDecryptECDH(message []byte, privateKey1 ed25519.PrivateKey, publicKey1 ed25519.PublicKey, privateKey2 ed25519.PrivateKey, publicKey2 ed25519.PublicKey) ([]byte, []byte, error) {
64+
encrypted, err := EncryptECDH(message, privateKey1, publicKey2)
65+
if err != nil {
66+
return nil, nil, err
67+
}
68+
69+
decrypted, err := DecryptECDH(encrypted, privateKey2, publicKey1)
70+
if err != nil {
71+
return encrypted, nil, err
72+
}
73+
74+
return encrypted, decrypted, nil
75+
}
76+
77+
// isValidKeyPair checks if a keyPair has valid non-zero keys
78+
func isValidKeyPair(kp keyPair) bool {
79+
return len(kp.PublicKey) > 0 && len(kp.PrivateKey) > 0
80+
}
81+
82+
// Property-based tests using testing/quick
83+
84+
func TestEncryptionDecryptionRoundtrip(t *testing.T) {
85+
f := func(kp keyPair, msg randomMessage) bool {
86+
if !isValidKeyPair(kp) {
87+
return true
88+
}
89+
90+
message := []byte(msg)
91+
_, decrypted, err := encryptThenDecrypt(message, kp.PublicKey, kp.PrivateKey)
92+
if err != nil {
93+
return false
94+
}
95+
return bytes.Equal(decrypted, message)
96+
}
97+
98+
if err := quick.Check(f, nil); err != nil {
99+
t.Error("encrypt/decrypt roundtrip error message:", err)
100+
}
101+
}
102+
103+
func TestEncryptionNonDeterminism(t *testing.T) {
104+
f := func(kp keyPair, msg randomMessage) bool {
105+
if !isValidKeyPair(kp) {
106+
return true
107+
}
108+
109+
message := []byte(msg)
110+
encrypted1, _, err1 := encryptThenDecrypt(message, kp.PublicKey, kp.PrivateKey)
111+
encrypted2, _, err2 := encryptThenDecrypt(message, kp.PublicKey, kp.PrivateKey)
112+
113+
if err1 != nil || err2 != nil {
114+
return false
115+
}
116+
117+
return !bytes.Equal(encrypted1, encrypted2)
118+
}
119+
120+
if err := quick.Check(f, nil); err != nil {
121+
t.Error("encryption is non-deterministic failed with error:", err)
122+
}
123+
}
124+
125+
func TestEncryptionCiphertextSize(t *testing.T) {
126+
f := func(kp keyPair, msg randomMessage) bool {
127+
if !isValidKeyPair(kp) {
128+
return true
129+
}
130+
131+
message := []byte(msg)
132+
encrypted, _, err := encryptThenDecrypt(message, kp.PublicKey, kp.PrivateKey)
133+
if err != nil {
134+
return false
135+
}
136+
137+
expectedSize := len(message) + NaClSealedBoxOverhead
138+
return len(encrypted) == expectedSize
139+
}
140+
141+
if err := quick.Check(f, nil); err != nil {
142+
t.Error("ciphertext has expected size overhead:", err)
143+
}
144+
}
145+
146+
func TestECDHEncryptionRoundtrip(t *testing.T) {
147+
f := func(kp1, kp2 keyPair, msg randomMessage) bool {
148+
if !isValidKeyPair(kp1) || !isValidKeyPair(kp2) {
149+
return true
150+
}
151+
152+
message := []byte(msg)
153+
_, decrypted, err := encryptThenDecryptECDH(message, kp1.PrivateKey, kp1.PublicKey, kp2.PrivateKey, kp2.PublicKey)
154+
if err != nil {
155+
return false
156+
}
157+
return bytes.Equal(decrypted, message)
158+
}
159+
160+
if err := quick.Check(f, nil); err != nil {
161+
t.Error("ECDH encrypt/decrypt roundtrip error message:", err)
162+
}
163+
}
164+
165+
func TestECDHSymmetry(t *testing.T) {
166+
f := func(kp1, kp2 keyPair, msg randomMessage) bool {
167+
if !isValidKeyPair(kp1) || !isValidKeyPair(kp2) {
168+
return true
169+
}
170+
171+
message := []byte(msg)
172+
_, decrypted1, err := encryptThenDecryptECDH(message, kp1.PrivateKey, kp1.PublicKey, kp2.PrivateKey, kp2.PublicKey)
173+
if err != nil {
174+
return false
175+
}
176+
177+
_, decrypted2, err := encryptThenDecryptECDH(message, kp2.PrivateKey, kp2.PublicKey, kp1.PrivateKey, kp1.PublicKey)
178+
if err != nil {
179+
return false
180+
}
181+
182+
return bytes.Equal(decrypted1, message) &&
183+
bytes.Equal(decrypted2, message) &&
184+
bytes.Equal(decrypted1, decrypted2)
185+
}
186+
187+
if err := quick.Check(f, nil); err != nil {
188+
t.Error("ECDH encryption is symmetric failed with error:", err)
189+
}
190+
}
191+
192+
func TestSignatureRoundtrip(t *testing.T) {
193+
f := func(kp keyPair, msg randomMessage) bool {
194+
if !isValidKeyPair(kp) {
195+
return true
196+
}
197+
198+
message := []byte(msg)
199+
signature, err := Sign(kp.PrivateKey, message)
200+
if err != nil {
201+
return false
202+
}
203+
204+
err = Verify(kp.PublicKey, message, signature)
205+
return err == nil
206+
}
207+
208+
if err := quick.Check(f, nil); err != nil {
209+
t.Error("sign/verify roundtrip succeeds for valid signatures failed with error:", err)
210+
}
211+
}
212+
213+
func TestSignatureDeterminism(t *testing.T) {
214+
f := func(kp keyPair, msg randomMessage) bool {
215+
if !isValidKeyPair(kp) {
216+
return true
217+
}
218+
219+
message := []byte(msg)
220+
sig1, err1 := Sign(kp.PrivateKey, message)
221+
sig2, err2 := Sign(kp.PrivateKey, message)
222+
223+
if err1 != nil || err2 != nil {
224+
return false
225+
}
226+
227+
return bytes.Equal(sig1, sig2)
228+
}
229+
230+
if err := quick.Check(f, nil); err != nil {
231+
t.Error("ed25519 signatures are deterministic failed with error:", err)
232+
}
233+
}
234+
235+
func TestWrongKeyRejection(t *testing.T) {
236+
f := func(kp1, kp2 keyPair, msg randomMessage) bool {
237+
if !isValidKeyPair(kp1) || !isValidKeyPair(kp2) {
238+
return true
239+
}
240+
241+
if bytes.Equal(kp1.PublicKey, kp2.PublicKey) {
242+
return true // Skip if keys are the same
243+
}
244+
245+
message := []byte(msg)
246+
signature, err := Sign(kp1.PrivateKey, message)
247+
if err != nil {
248+
return false
249+
}
250+
251+
err = Verify(kp2.PublicKey, message, signature)
252+
return err != nil
253+
}
254+
255+
if err := quick.Check(f, nil); err != nil {
256+
t.Error("wrong public key rejects signature failed with error:", err)
257+
}
258+
}
259+
260+
func TestMessageIntegrity(t *testing.T) {
261+
f := func(kp keyPair, msg1, msg2 randomMessage) bool {
262+
if !isValidKeyPair(kp) {
263+
return true
264+
}
265+
266+
message1 := []byte(msg1)
267+
message2 := []byte(msg2)
268+
269+
if bytes.Equal(message1, message2) {
270+
return true // Skip if messages are the same
271+
}
272+
273+
signature, err := Sign(kp.PrivateKey, message1)
274+
if err != nil {
275+
return false
276+
}
277+
278+
err = Verify(kp.PublicKey, message2, signature)
279+
return err != nil
280+
}
281+
282+
if err := quick.Check(f, nil); err != nil {
283+
t.Error("tampered message fails verification failed with error:", err)
284+
}
285+
}
286+
287+
func TestEncryptionNotAllZeros(t *testing.T) {
288+
f := func(kp keyPair, msg randomMessage) bool {
289+
if !isValidKeyPair(kp) {
290+
return true
291+
}
292+
293+
message := []byte(msg)
294+
encrypted, _, err := encryptThenDecrypt(message, kp.PublicKey, kp.PrivateKey)
295+
if err != nil {
296+
return false
297+
}
298+
299+
allZeros := true
300+
for _, b := range encrypted {
301+
if b != 0 {
302+
allZeros = false
303+
break
304+
}
305+
}
306+
307+
return !allZeros
308+
}
309+
310+
if err := quick.Check(f, nil); err != nil {
311+
t.Error("encryption doesn't produce all-zero output failed with error:", err)
312+
}
313+
}

0 commit comments

Comments
 (0)