Skip to content

Commit 26b5ff4

Browse files
committed
Add examples for encrypt/decrypt; Fix merkle tree
1 parent a63ef25 commit 26b5ff4

File tree

8 files changed

+110
-46
lines changed

8 files changed

+110
-46
lines changed

example/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
## TODO: Add usage examples

example/main.go

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,126 @@
11
package main
22

33
import (
4+
"encoding/hex"
45
"flag"
56
"fmt"
6-
bitstream "github.com/pyropy/bitstream-go/pkg"
77
"io"
88
"log"
99
"os"
10+
11+
"github.com/btcsuite/btcd/btcec/v2"
12+
bitstream "github.com/pyropy/bitstream-go/pkg"
1013
)
1114

1215
var (
13-
inPath string
14-
outPath string
15-
preimage string
16-
chunkSize int64
16+
action string
17+
inPath string
18+
outPath string
19+
preimage string
20+
paymentHash string
21+
chunkSize int64
22+
pk *btcec.PrivateKey
1723
)
1824

1925
func init() {
26+
flag.StringVar(&action, "action", "encrypt", "encrypt or decrypt")
2027
flag.StringVar(&inPath, "in", "", "File to encrypt/decrypt")
2128
flag.StringVar(&outPath, "out", "", "Path to output encrypted/decrypted file")
2229
flag.StringVar(&preimage, "preimage", "", "Preimage to use for encryption/decryption")
30+
flag.StringVar(&paymentHash, "hash", "", "Payment hash")
2331
flag.Int64Var(&chunkSize, "size", 32, "Size of chunks to encrypt/decrypt")
32+
33+
pkBytes, err := hex.DecodeString("80faa7d1c150a903a4028bf87ca8800aff507b24df90e8434bfa1f34d639c053")
34+
if err != nil {
35+
panic(err)
36+
}
37+
38+
pk, _ = btcec.PrivKeyFromBytes(pkBytes)
2439
}
2540

2641
// TODO: Implement encrypt and decrypt cli commands
2742
func main() {
2843
flag.Parse()
2944

30-
err := encrypt(inPath, outPath)
45+
var err error
46+
47+
switch action {
48+
case "encrypt":
49+
err = encrypt(inPath, outPath, preimage, chunkSize)
50+
case "decrypt":
51+
err = decrypt(inPath, outPath, preimage, chunkSize)
52+
}
53+
3154
if err != nil {
3255
panic(err)
3356
}
3457
}
3558

36-
func encrypt(inPath, outPath string) error {
59+
func encrypt(inPath, outPath, preimage string, chunkSize int64) error {
3760
f, err := os.Open(inPath)
3861
if err != nil {
3962
return err
4063
}
4164

42-
encryptedFile, tree, err := bitstream.EncryptFile([]byte(preimage), f, chunkSize)
65+
defer f.Close()
66+
67+
out, err := os.OpenFile(outPath, os.O_RDWR|os.O_CREATE, 0644)
68+
if err != nil {
69+
return err
70+
}
71+
72+
preimageBytes, err := hex.DecodeString(preimage)
73+
if err != nil {
74+
panic(err)
75+
}
76+
77+
paymentHashBytes, err := hex.DecodeString(paymentHash)
78+
if err != nil {
79+
return err
80+
}
81+
82+
err = bitstream.Encrypt(pk, paymentHashBytes, preimageBytes, f, out, chunkSize)
4383
if err != nil {
4484
return err
4585
}
4686

47-
l := fmt.Sprintf("Merkle tree hash 0x%x", tree.GetHash())
48-
log.Println(l)
87+
return nil
88+
}
4989

50-
b, err := io.ReadAll(encryptedFile)
90+
func decrypt(inPath, outPath, preimage string, chunkSize int64) error {
91+
f, err := os.Open(inPath)
5192
if err != nil {
5293
return err
5394
}
5495

55-
err = os.WriteFile(outPath, b, 0644)
96+
defer f.Close()
97+
98+
preimageBytes, err := hex.DecodeString(preimage)
99+
if err != nil {
100+
return err
101+
}
102+
103+
decryptedFile, index, err := bitstream.Decrypt(preimageBytes, f, chunkSize)
104+
if err != nil {
105+
if index != -1 {
106+
proof, err := bitstream.GenerateProof(f, chunkSize, 2*index)
107+
if err != nil {
108+
return err
109+
}
110+
111+
root := fmt.Sprintf("%x", proof.MerkleProof.Root)
112+
log.Println("Generated proof", "proof root", root)
113+
}
114+
115+
return err
116+
}
117+
118+
decrypted, err := io.ReadAll(decryptedFile)
56119
if err != nil {
57120
return err
58121
}
59122

60-
err = f.Close()
123+
err = os.WriteFile(outPath, decrypted, 0644)
61124
if err != nil {
62125
return err
63126
}

pkg/cipher.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ func ChunkCipher(index uint64, preimage []byte, data []byte) []byte {
1111

1212
// add 1 to index
1313
index += 1
14-
// convert index to bytes
14+
15+
// convert index to bytes (original implementation uses btc specific encoding)
1516
indexBytes := make([]byte, 8)
1617
binary.LittleEndian.PutUint64(indexBytes, index)
1718

pkg/decrypt.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
//
1414
// If chunk index is not -1, then error ocurred while decrypting the chunk
1515
// hence proof for given chunk should be generated.
16-
func Decrypt(preimage []byte, file io.ReadSeeker, chunkSize int) (io.Reader, int, error) {
16+
func Decrypt(preimage []byte, file io.ReadSeeker, chunkSize int64) (io.Reader, int, error) {
1717
var out bytes.Buffer
1818

1919
expectedHash := make([]byte, HashSize)
@@ -41,7 +41,7 @@ func Decrypt(preimage []byte, file io.ReadSeeker, chunkSize int) (io.Reader, int
4141
}
4242

4343
// update offset for chunk read
44-
offset += int64(chunkSize)
44+
offset += chunkSize
4545
_, err = file.Seek(offset, 0)
4646
if err != nil {
4747
return nil, -1, err
@@ -58,7 +58,7 @@ func Decrypt(preimage []byte, file io.ReadSeeker, chunkSize int) (io.Reader, int
5858
}
5959

6060
// update offset for next expected hash read
61-
offset += int64(chunkSize)
61+
offset += chunkSize
6262

6363
// decrypt chunk, compute hash and if hashes match
6464
decryptedChunk := ChunkCipher(i, preimage, encryptedChunk)

pkg/encrypt.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,28 @@ package bitstream
33
import (
44
"bytes"
55
"fmt"
6+
"io"
7+
68
"github.com/btcsuite/btcd/btcec/v2"
79
"github.com/btcsuite/btcd/btcec/v2/schnorr"
8-
"io"
910
)
1011

1112
type EncryptedFile interface {
1213
io.WriterTo
1314
io.Reader
1415
}
1516

16-
func Encrypt(pk *btcec.PrivateKey, paymentHash []byte, preimage []byte, inFile io.ReadSeeker, outFile io.Writer, chunkSize int64) error {
17+
func Encrypt(pk *btcec.PrivateKey, paymentHash, preimage []byte, inFile io.ReadSeeker, outFile io.Writer, chunkSize int64) error {
1718
encrypted, tree, err := EncryptFile(preimage, inFile, chunkSize)
1819
if err != nil {
1920
return err
2021
}
2122

2223
encryptedRoot := tree.GetHash()
23-
message := append(encryptedRoot, paymentHash...)
24-
25-
sig, err := schnorr.Sign(pk, message)
24+
// original implementation signs encrypted root + payment hash
25+
sig, err := schnorr.Sign(pk, encryptedRoot)
2626
if err != nil {
27-
return err
27+
return fmt.Errorf("failed to generate schorr sig: %w", err)
2828
}
2929

3030
_, err = outFile.Write(sig.Serialize())

pkg/merkle_tree.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ type Node struct {
2323
}
2424

2525
type MerkleProof struct {
26-
Root []byte
27-
Bloom []uint8
28-
Path [][]byte
26+
Root []byte
27+
Path [][]byte
2928
}
3029

3130
func (n *Node) GetHash() []byte {
@@ -65,14 +64,14 @@ func NewTree(leaves []*Node) *MerkleTree {
6564
}
6665
}
6766

68-
if len(leaves)%2 != 0 {
69-
leaf := leaves[len(leaves)-1]
70-
leaves = append(leaves, leaf)
71-
}
72-
7367
for len(leaves) > 1 {
7468
var nextLevel []*Node
7569

70+
if len(leaves)%2 != 0 {
71+
leaf := leaves[len(leaves)-1]
72+
leaves = append(leaves, leaf)
73+
}
74+
7675
// go through all leaves on current level in pairs
7776
for i := 0; i < len(leaves); i += 2 {
7877
left := leaves[i]
@@ -94,14 +93,14 @@ func NewTree(leaves []*Node) *MerkleTree {
9493
func GenerateMerkleProof(leaves []*Node, index int) *MerkleProof {
9594
var path [][]byte
9695

97-
if len(leaves)%2 != 0 {
98-
leaf := leaves[len(leaves)-1]
99-
leaves = append(leaves, leaf)
100-
}
101-
10296
for len(leaves) > 1 {
10397
var nextLevel []*Node
10498

99+
if len(leaves)%2 != 0 {
100+
leaf := leaves[len(leaves)-1]
101+
leaves = append(leaves, leaf)
102+
}
103+
105104
// go through all leaves on current level in pairs
106105
for i := 0; i < len(leaves); i += 2 {
107106
left := leaves[i]

pkg/merkle_tree_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ func TestTree(t *testing.T) {
2121
leaves: []*Node{
2222
NewNode([]byte("hello")),
2323
},
24-
expect: "1d25c19a1a3fb65c78d018561057362916c14bfd36b75aa8cb0f4d696293b183",
24+
expect: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2525
},
2626
{
2727
leaves: []*Node{
2828
NewNode([]byte("world")),
2929
},
30-
expect: "a9e049081e9eaeef2d30029031314c888955d7181a032c29b11633ac11d6076c",
30+
expect: "486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8e5a6c65260e9cb8a7",
3131
},
3232
{
3333
leaves: []*Node{

pkg/proof.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ const (
88
)
99

1010
type Proof struct {
11-
sig []byte
12-
paymentHash []byte
13-
merkleProof *MerkleProof
11+
Sig []byte
12+
PaymentHash []byte
13+
MerkleProof *MerkleProof
1414
}
1515

16-
func GenerateProof(file io.ReadSeeker, chunkSize int, chunkIndex int) (*Proof, error) {
16+
func GenerateProof(file io.ReadSeeker, chunkSize int64, chunkIndex int) (*Proof, error) {
1717
sig := make([]byte, SignatureSize)
1818
paymentHash := make([]byte, HashSize)
1919
hash := make([]byte, chunkSize)
@@ -53,7 +53,7 @@ func GenerateProof(file io.ReadSeeker, chunkSize int, chunkIndex int) (*Proof, e
5353
}
5454

5555
// update offset for next chunk hash read
56-
offset += int64(chunkSize * 2)
56+
offset += chunkSize * 2
5757
node := NewNodeFromHash(hash)
5858
leaves = append(leaves, node)
5959

@@ -64,8 +64,8 @@ func GenerateProof(file io.ReadSeeker, chunkSize int, chunkIndex int) (*Proof, e
6464
merkleProof := GenerateMerkleProof(leaves, chunkIndex)
6565

6666
return &Proof{
67-
sig: sig,
68-
paymentHash: paymentHash,
69-
merkleProof: merkleProof,
67+
Sig: sig,
68+
PaymentHash: paymentHash,
69+
MerkleProof: merkleProof,
7070
}, nil
7171
}

0 commit comments

Comments
 (0)