package salt import ( "crypto/rand" "fmt" "sync" "time" "github.com/golang/protobuf/proto" pb "github.com/iotaledger/goshimmer/packages/autopeering/salt/proto" ) // SaltByteSize specifies the number of bytes used for the salt. const SaltByteSize = 20 // Salt encapsulates high level functions around salt management. type Salt struct { bytes []byte // value of the salt expirationTime time.Time // expiration time of the salt mutex sync.RWMutex } // NewSalt generates a new salt given a lifetime duration func NewSalt(lifetime time.Duration) (salt *Salt, err error) { salt = &Salt{ bytes: make([]byte, SaltByteSize), expirationTime: time.Now().Add(lifetime), } if _, err = rand.Read(salt.bytes); err != nil { return nil, err } return salt, err } func (s *Salt) GetBytes() []byte { s.mutex.RLock() defer s.mutex.RUnlock() return s.bytes } func (s *Salt) GetExpiration() time.Time { s.mutex.RLock() defer s.mutex.RUnlock() return s.expirationTime } // Expired returns true if the given salt expired func (s *Salt) Expired() bool { return time.Now().After(s.GetExpiration()) } // ToProto encodes the Salt into a proto buffer Salt message func (s *Salt) ToProto() *pb.Salt { return &pb.Salt{ Bytes: s.bytes, ExpTime: uint64(s.expirationTime.Unix()), } } // FromProto decodes a given proto buffer Salt message (in) and returns the corresponding Salt. func FromProto(in *pb.Salt) (*Salt, error) { if l := len(in.GetBytes()); l != SaltByteSize { return nil, fmt.Errorf("invalid salt length: %d, need %d", l, SaltByteSize) } out := &Salt{ bytes: in.GetBytes(), expirationTime: time.Unix(int64(in.GetExpTime()), 0), } return out, nil } // Marshal serializes a given salt (s) into a slice of bytes (data) func (s *Salt) Marshal() ([]byte, error) { return proto.Marshal(s.ToProto()) } // Unmarshal de-serializes a given slice of bytes (data) into a Salt. func Unmarshal(data []byte) (*Salt, error) { s := &pb.Salt{} if err := proto.Unmarshal(data, s); err != nil { return nil, err } return FromProto(s) }