schastsp/src/git.xdrm.io/schastsp/client/keyset/keyset.go

213 lines
4.5 KiB
Go
Raw Normal View History

package keyset
import (
"io"
"encoding/binary"
"errors"
"git.xdrm.io/schastsp/lib/scha"
"math/rand"
"time"
)
const a = 12
/* Attributes */
type Set struct {
min uint16 // min depth
depth uint16 // cur depth
max uint16 // max depth
sec []byte // secret
consumption uint // consumption level
// 0: none
// 1: need to migrate
// 2: waiting migration
// 3: validated migration
}
/* (1) Creates a new KeySet
*
* @min<uint16> Minimum depth value
* @max<uint16> Maximum depth value
*
* @return outName<outType> outDesc
*
---------------------------------------------------------*/
func Create(min uint16, max uint16) (*Set, error) {
/* (1) Fail if min >= max */
if min >= max {
return nil, errors.New("Max depth is not greater than Min depth")
}
/* (2) Instanciate */
var instance = new(Set)
/* (3) Set attributes */
instance.min = min
instance.max = max
/* (4) Generate values if <nil> secret */
if instance.sec == nil {
instance.generate()
}
return instance, nil
}
/* (2) Generates a pseudo-random KeySet
*
---------------------------------------------------------*/
func (s *Set) generate() {
/* (1) Seed randomness */
rand.Seed(time.Now().UTC().UnixNano())
/* (1) Generate new secret
---------------------------------------------------------*/
/* (1) Reset current secret */
s.sec = make([]byte, scha.HSIZE)
/* (2) Generate each char. until same length as hash digest */
for i := uint16(0); i < scha.HSIZE; i++ {
s.sec[i] = byte(rand.Int() % 256)
}
/* (2) Manage other attributes
---------------------------------------------------------*/
/* (1) Random depth pick init */
var randMin, randMax uint16 = s.min + (s.max-s.min)/2, s.max
/* (2) Select "random" depth */
s.depth = randMin + uint16(rand.Intn(int(randMax-randMin)))
/* (3) Reset comsumption level */
s.consumption = 0
}
/* (3) Get current hash
*
* @return digest<[]byte]> Current hash representing the set
*
---------------------------------------------------------*/
func (s Set) Hash() ([]byte, error) {
/* (1) Get digest */
digest, err := scha.Hash(s.sec, uint(s.depth), nil, nil)
/* (2) Dispatch error */
if err != nil {
return nil, err
}
/* (3) Else -> return digest */
return digest, nil
}
/* (4) Decrement depth
*
* @return remaining<uint> Remaining hashes before migration
*
---------------------------------------------------------*/
func (s *Set) Decrement() uint16 {
/* (1) Decrement the depth */
s.depth--
/* (2) If near minDepth (10 far): set consumption to 1 */
if s.depth <= s.min+10 {
s.consumption = 1
}
/* (3) Return remaining attempts */
return s.depth - s.max
}
/* (5) Serialisation
*
* @return serial<string> String representation
*
*
* === FORMAT ===
*
* | hsize | secret | depth |
* +-----------+------------+---------+
* | 16 bits | hsize bits | 16 bits |
*
* == ENDIANNESS ==
*
* network endianness -> big-endian
*
---------------------------------------------------------*/
func (s *Set) Write(writer io.Writer) error {
var err error;
/* (1) Copy hash size */
err = binary.Write(writer, binary.BigEndian, scha.HSIZE)
if err != nil { return err }
/* (2) Copy secret */
err = binary.Write(writer, binary.BigEndian, s.sec)
if err != nil { return err }
/* (3) Copy depth */
err = binary.Write(writer, binary.BigEndian, s.depth)
if err != nil { return err }
return nil
}
/* (6) Builds a KeySet from its serial representation
*
* @serial<string> String representation
*
* @return instance<*Set> The corresponding set
* @return err<error> Optional error
*
* === FORMAT ===
*
* | hsize | secret | depth |
* +-----------+------------+---------+
* | 16 bits | hsize bits | 16 bits |
*
---------------------------------------------------------*/
func (s *Set) Read(reader io.Reader) error {
var (
err error
secretLength uint16
)
/* (1) Read the secret size */
err = binary.Read(reader, binary.BigEndian, &secretLength)
if err != nil {
return err
}
/* (2) Fail if secretLength different than digest size */
if secretLength != scha.HSIZE {
return errors.New("Invalid secret size (must be the same as digest size)")
}
/* (3) Try to copy the secret */
err = binary.Read(reader, binary.BigEndian, &s.sec)
if err != nil { return err }
/* (4) Manage invalid secret size (mismatch secretLength) */
if uint16(len(s.sec)) != secretLength {
return errors.New("Mismatching secret size")
}
/* (5) Try to copy the depth */
err = binary.Read(reader, binary.BigEndian, &s.depth)
if err != nil { return err }
return nil
}