2018-04-21 09:58:47 +00:00
|
|
|
package keyset
|
|
|
|
|
|
|
|
import (
|
2018-04-21 14:08:08 +00:00
|
|
|
"git.xdrm.io/schastsp/lib/context"
|
2018-04-21 09:58:47 +00:00
|
|
|
"io"
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
"git.xdrm.io/schastsp/lib/scha"
|
|
|
|
"math/rand"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const a = 12
|
|
|
|
|
|
|
|
/* Attributes */
|
|
|
|
type Set struct {
|
2018-04-21 14:08:08 +00:00
|
|
|
ctx *context.Context // current context
|
|
|
|
depth uint16 // cur depth
|
|
|
|
max uint16 // max depth
|
2018-04-21 09:58:47 +00:00
|
|
|
|
2018-04-21 14:08:08 +00:00
|
|
|
sec []byte // secret
|
|
|
|
consumption uint // consumption level
|
2018-04-21 09:58:47 +00:00
|
|
|
// 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
|
|
|
|
*
|
|
|
|
---------------------------------------------------------*/
|
2018-04-21 14:08:08 +00:00
|
|
|
func Create(ctx *context.Context, max uint16) (*Set, error) {
|
2018-04-21 09:58:47 +00:00
|
|
|
|
2018-04-21 14:08:08 +00:00
|
|
|
/* (1) Fail if min+thre >= max */
|
2018-04-21 14:10:06 +00:00
|
|
|
if max < ctx.MinDepth()+ctx.DepthThreshold() {
|
2018-04-21 14:08:08 +00:00
|
|
|
return nil, errors.New("Maximum depth must be greater than Min+threshold (from given Context)")
|
2018-04-21 09:58:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* (2) Instanciate */
|
|
|
|
var instance = new(Set)
|
|
|
|
|
|
|
|
/* (3) Set attributes */
|
2018-04-21 14:08:08 +00:00
|
|
|
instance.ctx = ctx
|
2018-04-21 09:58:47 +00:00
|
|
|
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 */
|
2018-04-21 14:08:08 +00:00
|
|
|
var randMin, randMax uint16 = s.ctx.MinDepth() + (s.max-s.ctx.MinDepth())/2, s.max
|
2018-04-21 09:58:47 +00:00
|
|
|
|
|
|
|
/* (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 */
|
2018-04-21 14:08:08 +00:00
|
|
|
if s.depth <= s.ctx.MinDepth()+s.ctx.DepthThreshold() {
|
2018-04-21 09:58:47 +00:00
|
|
|
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
|
|
|
|
*
|
|
|
|
---------------------------------------------------------*/
|
2018-04-21 14:10:06 +00:00
|
|
|
func (s *Set) Store(writer io.Writer) error {
|
2018-04-21 09:58:47 +00:00
|
|
|
|
|
|
|
var err error;
|
|
|
|
|
2018-04-21 14:08:08 +00:00
|
|
|
/* (1) Copy secret size */
|
2018-04-21 09:58:47 +00:00
|
|
|
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 |
|
|
|
|
*
|
|
|
|
---------------------------------------------------------*/
|
2018-04-21 14:10:06 +00:00
|
|
|
func (s *Set) Fetch(reader io.Reader) error {
|
2018-04-21 09:58:47 +00:00
|
|
|
|
2018-04-21 14:08:08 +00:00
|
|
|
var err error
|
|
|
|
var secretLength uint16
|
2018-04-21 09:58:47 +00:00
|
|
|
|
|
|
|
/* (1) Read the secret size */
|
|
|
|
err = binary.Read(reader, binary.BigEndian, &secretLength)
|
2018-04-21 14:08:08 +00:00
|
|
|
if err != nil { return err }
|
2018-04-21 09:58:47 +00:00
|
|
|
|
|
|
|
/* (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
|
|
|
|
|
|
|
|
}
|