needle.gno
2.81 Kb ยท 91 lines
1package needle
2
3import (
4 "bytes"
5 "crypto/sha256"
6 "errors"
7)
8
9const (
10 // HashLength is the length in bytes of the hash prefix in any message
11 HashLength = 32
12 // PayloadLength is the length of the remaining bytes of the message.
13 PayloadLength = 160
14 // NeedleLength is the number of bytes required for a valid needle.
15 NeedleLength = HashLength + PayloadLength
16)
17
18// Needle is a container for a 160 byte payload
19// and a 32 byte sha256 hash of the payload.
20type Needle struct {
21 hash [HashLength]byte
22 payload [PayloadLength]byte
23}
24
25var (
26 // ErrorInvalidHash is an error for in invalid hash
27 ErrorInvalidHash = errors.New("invalid hash")
28 // ErrorByteSliceLength is an error for an invalid byte slice length passed in to New or FromBytes
29 ErrorByteSliceLength = errors.New("invalid byte slice length")
30)
31
32// New creates a Needle used for submitting a payload to a Haystack sever. It takes a Payload
33// byte slice that is 160 bytes in length and returns a reference to a
34// Needle and an error. The purpose of this function is to make it
35// easy to create a new Needle from a payload. This function handles creating a sha256
36// hash of the payload, which is used by the Needle to submit to a haystack server.
37func New(p []byte) (*Needle, error) {
38 if len(p) != PayloadLength {
39 return nil, ErrorByteSliceLength
40 }
41 var n Needle
42 sum := sha256.Sum256(p)
43 copy(n.hash[:], sum[:])
44 copy(n.payload[:], p)
45 return &n, nil
46}
47
48// FromBytes is intended convert raw bytes (from UDP or storage) into a Needle.
49// It takes a byte slice and expects it to be exactly the length of NeedleLength.
50// The byte slice should consist of the first 32 bytes being the sha256 hash of the
51// payload and the payload bytes. This function verifies the length of the byte slice,
52// copies the bytes into a private [192]byte array, and validates the Needle. It returns
53// a reference to a Needle and an error.
54func FromBytes(b []byte) (*Needle, error) {
55 if len(b) != NeedleLength {
56 return nil, ErrorByteSliceLength
57 }
58 var n Needle
59 copy(n.hash[:], b[:HashLength])
60 copy(n.payload[:], b[HashLength:])
61 if err := n.validate(); err != nil {
62 return nil, err
63 }
64 return &n, nil
65}
66
67// Hash returns a copy of the bytes of the sha256 256 hash of the Needle payload.
68func (n *Needle) Hash() []byte {
69 return n.Bytes()[:HashLength]
70}
71
72// Payload returns a byte slice of the Needle payload
73func (n *Needle) Payload() []byte {
74 return n.Bytes()[HashLength:]
75}
76
77// Bytes returns a byte slice of the entire 192 byte hash + payload
78func (n *Needle) Bytes() []byte {
79 b := make([]byte, NeedleLength)
80 copy(b, n.hash[:])
81 copy(b[HashLength:], n.payload[:])
82 return b
83}
84
85// validate checks that a Needle has a valid hash, it returns either nil or an error.
86func (n *Needle) validate() error {
87 if hash := sha256.Sum256(n.Payload()); !bytes.Equal(n.Hash(), hash[:]) {
88 return ErrorInvalidHash
89 }
90 return nil
91}