[server] [client] [lib.scha] big update to review
This commit is contained in:
parent
3c9964b0cb
commit
08581354a4
|
@ -66,22 +66,20 @@ func New(ctx *context.T, saveDir string) (*T, error) {
|
||||||
func (c *T) Send(w io.Writer) error {
|
func (c *T) Send(w io.Writer) error {
|
||||||
|
|
||||||
/* (1) Generate the request */
|
/* (1) Generate the request */
|
||||||
var x1, x2 []byte
|
x1 := make([]byte, scha.HSIZE)
|
||||||
|
x2 := make([]byte, scha.HSIZE)
|
||||||
|
|
||||||
err := c.generateRequest(x1, x2)
|
err := c.generateRequest(x1, x2)
|
||||||
if err != nil {
|
if err != nil { return err }
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (2) Write request into writer */
|
/* (2) Write request into writer */
|
||||||
_, err = w.Write(x1)
|
written, err := w.Write(x1)
|
||||||
if err != nil {
|
if err != nil { return err }
|
||||||
return err
|
if written != len(x1) { return errors.New("Cannot write x1") }
|
||||||
}
|
|
||||||
_, err = w.Write(x2)
|
written, err = w.Write(x2)
|
||||||
if err != nil {
|
if err != nil { return err }
|
||||||
return err
|
if written != len(x1) { return errors.New("Cannot write x2") }
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -100,8 +98,9 @@ func (c *T) Receive(r io.Reader) error {
|
||||||
errCode := make([]byte, 1)
|
errCode := make([]byte, 1)
|
||||||
read, err := r.Read(errCode)
|
read, err := r.Read(errCode)
|
||||||
if err != nil { return err }
|
if err != nil { return err }
|
||||||
if uint16(read) != 1 { return errors.New("Cannot read enough bytes") }
|
if uint16(read) != 1 { return errors.New("Cannot read error code") }
|
||||||
|
|
||||||
|
fmt.Printf("ERROR CODE : %d\n", errCode[0]);
|
||||||
/* (2) Manage success
|
/* (2) Manage success
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
if errCode[0] == 0 {
|
if errCode[0] == 0 {
|
||||||
|
@ -125,13 +124,13 @@ func (c *T) Receive(r io.Reader) error {
|
||||||
y1 := make([]byte, scha.HSIZE)
|
y1 := make([]byte, scha.HSIZE)
|
||||||
read, err = r.Read(y1)
|
read, err = r.Read(y1)
|
||||||
if err != nil { return err }
|
if err != nil { return err }
|
||||||
if uint16(read) != scha.HSIZE { return errors.New("Cannot read enough bytes") }
|
if uint16(read) != scha.HSIZE { return errors.New("Cannot read y1") }
|
||||||
|
|
||||||
/* (2) Read y2 */
|
/* (2) Read y2 */
|
||||||
y2 := make([]byte, scha.HSIZE)
|
y2 := make([]byte, scha.HSIZE)
|
||||||
read, err = r.Read(y2)
|
read, err = r.Read(y2)
|
||||||
if err != nil { return err }
|
if err != nil { return err }
|
||||||
if uint16(read) != scha.HSIZE { return errors.New("Cannot read enough bytes") }
|
if uint16(read) != scha.HSIZE { return errors.New("Cannot read enough y2") }
|
||||||
|
|
||||||
/* (3) Manage rescue mode */
|
/* (3) Manage rescue mode */
|
||||||
err = c.rescue(y1, y2)
|
err = c.rescue(y1, y2)
|
||||||
|
@ -140,3 +139,18 @@ func (c *T) Receive(r io.Reader) error {
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* (5) TMP
|
||||||
|
*
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
func (c *T) TMP() []byte {
|
||||||
|
hash, err := c.key.CurrentHash()
|
||||||
|
if err != nil { panic(err) }
|
||||||
|
|
||||||
|
c.key.Decrement()
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
|
@ -226,7 +226,7 @@ func (c *T) generateRequest(x1 []byte, x2 []byte) error {
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
/* (1) Get current time id */
|
/* (1) Get current time id */
|
||||||
timeId, timeMod := timeid.Generate(c.ctx.Window())
|
timeId, timeMod := timeid.Generate(c.ctx.Window())
|
||||||
|
fmt.Printf("Client.time[%d, %d]\n", timeId, timeMod);
|
||||||
/* (2) Convert time id to byte array */
|
/* (2) Convert time id to byte array */
|
||||||
timeIdBytes := make([]byte, 4)
|
timeIdBytes := make([]byte, 4)
|
||||||
binary.BigEndian.PutUint32(timeIdBytes, timeId)
|
binary.BigEndian.PutUint32(timeIdBytes, timeId)
|
||||||
|
@ -237,22 +237,20 @@ func (c *T) generateRequest(x1 []byte, x2 []byte) error {
|
||||||
/* (5) Calculate x1 and x2
|
/* (5) Calculate x1 and x2
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
/* (1) Calculate x1 = h ^ h(timeId) */
|
/* (1) Calculate x1 = h ^ h(timeId) */
|
||||||
x1 = xor.ByteArray(h0, hashedTimeId)
|
copy(x1, xor.ByteArray(h0, hashedTimeId))
|
||||||
|
|
||||||
if DEBUG {
|
if DEBUG {
|
||||||
fmt.Printf("\n=== x1 ===\n")
|
fmt.Printf("\n=== x1 ===\n")
|
||||||
fmt.Printf(" hash is h0 = %x\n", h0)
|
fmt.Printf(" hash is h0 = %x\n", h0)
|
||||||
fmt.Printf(" time id is n = %x[%d]\n", timeIdBytes, timeId)
|
fmt.Printf(" time id is n = %x[%d]\n", timeIdBytes, timeId)
|
||||||
fmt.Printf(" h(t) = %x\n", hashedTimeId)
|
fmt.Printf(" h(t) = %x\n", hashedTimeId)
|
||||||
fmt.Printf(" ---\n")
|
fmt.Printf(" x1 is h0+h(t) = %X\n", x1)
|
||||||
fmt.Printf(" x1 is h0+h(t) = %x\n", x1)
|
|
||||||
fmt.Printf(" ---\n")
|
|
||||||
fmt.Printf(" check x1+h(t) eq h0 = %x\n", xor.ByteArray(x1, hashedTimeId))
|
fmt.Printf(" check x1+h(t) eq h0 = %x\n", xor.ByteArray(x1, hashedTimeId))
|
||||||
fmt.Printf(" check x1+h0 eq h(t) = %x\n", xor.ByteArray(x1, h0))
|
fmt.Printf(" check x1+h0 eq h(t) = %x\n", xor.ByteArray(x1, h0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (2) Calculate x2 = h ^ h(timeId) ^ timeMod */
|
/* (2) Calculate x2 = h ^ h(timeId) ^ timeMod */
|
||||||
x2 = xor.ByteArray(h1, hashedTimeId)
|
copy(x2, xor.ByteArray(h1, hashedTimeId))
|
||||||
|
|
||||||
// only add time mod if code = 0
|
// only add time mod if code = 0
|
||||||
if c.key.MigrationCode() == 0 {
|
if c.key.MigrationCode() == 0 {
|
||||||
|
@ -264,10 +262,10 @@ func (c *T) generateRequest(x1 []byte, x2 []byte) error {
|
||||||
fmt.Printf(" next is h1 = %x\n", h1)
|
fmt.Printf(" next is h1 = %x\n", h1)
|
||||||
fmt.Printf(" time mod is m = %x[%d]\n", timeMod, timeMod)
|
fmt.Printf(" time mod is m = %x[%d]\n", timeMod, timeMod)
|
||||||
fmt.Printf(" h(t) = %x\n", hashedTimeId)
|
fmt.Printf(" h(t) = %x\n", hashedTimeId)
|
||||||
fmt.Printf(" ---\n")
|
if c.key.MigrationCode() == 0 { fmt.Printf(" x2 is h1+h(t)+m = %X\n", x2)
|
||||||
fmt.Printf(" x2 is h1+h(t)+m = %x\n", x2)
|
} else { fmt.Printf(" x2 is h1+h(t) = %X\n", x2) }
|
||||||
fmt.Printf(" ---\n")
|
if c.key.MigrationCode() == 0 { fmt.Printf(" check x2+x1 %% 2 eq m = %d (%t)\n", uint8(xor.ByteArray(x1, x2)[0]%2), xor.ByteArray(x1, x2)[0]%2 == byte(timeMod))
|
||||||
fmt.Printf(" check x2+x1 %% 2 eq m = %d (%t)\n", uint8(xor.ByteArray(x1, x2)[0]%2), xor.ByteArray(x1, x2)[0]%2 == byte(timeMod))
|
} else { fmt.Printf(" check x2+x1 %% 2 eq m = %d (%t)\n", uint8(xor.ByteArray(x1, x2)[0]%2), xor.ByteArray(x1, x2)[0]%2 == byte(timeMod)) }
|
||||||
fmt.Printf(" check x2+x1 %% 3 eq o = %d (%t)\n", uint8(xor.ByteArray(x1, x2)[1]%3), uint8(xor.ByteArray(x1, x2)[1]%3) == c.key.MigrationCode())
|
fmt.Printf(" check x2+x1 %% 3 eq o = %d (%t)\n", uint8(xor.ByteArray(x1, x2)[1]%3), uint8(xor.ByteArray(x1, x2)[1]%3) == c.key.MigrationCode())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +287,7 @@ func (c *T) rescue(y1 []byte, y2 []byte) error {
|
||||||
|
|
||||||
/* (2) Try to guess time id from timeM */
|
/* (2) Try to guess time id from timeM */
|
||||||
timeId := timeid.Guess(c.ctx.Window(), timeMod)
|
timeId := timeid.Guess(c.ctx.Window(), timeMod)
|
||||||
var timeIdBytes []byte
|
timeIdBytes := make([]byte, 4)
|
||||||
binary.BigEndian.PutUint32(timeIdBytes, timeId)
|
binary.BigEndian.PutUint32(timeIdBytes, timeId)
|
||||||
|
|
||||||
/* (3) Hash timeId */
|
/* (3) Hash timeId */
|
||||||
|
|
|
@ -57,7 +57,7 @@ func Create(ctx *context.T) (*T, error) {
|
||||||
func (s T) CurrentHash() ([]byte, error) {
|
func (s T) CurrentHash() ([]byte, error) {
|
||||||
|
|
||||||
/* (1) Get digest */
|
/* (1) Get digest */
|
||||||
digest, err := scha.Hash(s.sec, uint(s.depth))
|
digest, err := scha.Hash(s.sec, s.depth)
|
||||||
|
|
||||||
/* (2) Dispatch error */
|
/* (2) Dispatch error */
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -50,7 +50,7 @@ func hash(input []byte) []byte{
|
||||||
* @return err<error> If consistence error
|
* @return err<error> If consistence error
|
||||||
*
|
*
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
func Hash(input []byte, depth uint, options... []byte) ([]byte, error) {
|
func Hash(input []byte, depth uint16, options... []byte) ([]byte, error) {
|
||||||
|
|
||||||
|
|
||||||
/* (1) Manage errors errors
|
/* (1) Manage errors errors
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.xdrm.io/schastsp/lib/scha"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -57,6 +58,7 @@ func New(ctx *context.T, savePath string) (*T, error) {
|
||||||
inst.conf = absolutePath;
|
inst.conf = absolutePath;
|
||||||
|
|
||||||
/* (5) Try to fetch hash from conf */
|
/* (5) Try to fetch hash from conf */
|
||||||
|
inst.hash = make([]byte, scha.HSIZE)
|
||||||
err = inst.fetch();
|
err = inst.fetch();
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
|
|
||||||
|
@ -74,4 +76,63 @@ func New(ctx *context.T, savePath string) (*T, error) {
|
||||||
* @return err<error> Error or NIL if not
|
* @return err<error> Error or NIL if not
|
||||||
*
|
*
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
func (s *T) HandleRequest(req io.Reader, res io.Writer) error { return nil }
|
func (s *T) HandleRequest(req io.Reader, res io.Writer) error {
|
||||||
|
|
||||||
|
/* (1) Manage request
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Read x1 */
|
||||||
|
x1 := make([]byte, scha.HSIZE);
|
||||||
|
read, err := req.Read(x1);
|
||||||
|
if err != nil { return err }
|
||||||
|
if uint16(read) != scha.HSIZE { return errors.New("Cannot read enough bytes") }
|
||||||
|
|
||||||
|
/* (2) Read x2 */
|
||||||
|
x2 := make([]byte, scha.HSIZE);
|
||||||
|
read, err = req.Read(x2);
|
||||||
|
if err != nil { return err }
|
||||||
|
if uint16(read) != scha.HSIZE { return errors.New("Cannot read enough bytes") }
|
||||||
|
|
||||||
|
/* (3) Manage request */
|
||||||
|
errCode, err := s.manageRequest(x1, x2)
|
||||||
|
if err != nil { return err }
|
||||||
|
|
||||||
|
/* (4) Valid authentication */
|
||||||
|
if errCode == 0 {
|
||||||
|
|
||||||
|
response := []byte{0x00}
|
||||||
|
written, err := res.Write(response)
|
||||||
|
if written != 1 || err != nil { return err }
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* (2) If not authenticated -> build resynchronisation response
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Init y1 + y2 */
|
||||||
|
y1 := make([]byte, scha.HSIZE)
|
||||||
|
y2 := make([]byte, scha.HSIZE)
|
||||||
|
|
||||||
|
/* (2) Generate response */
|
||||||
|
err = s.generateResynchronisationResponse(y1, y2)
|
||||||
|
if err != nil { return err }
|
||||||
|
|
||||||
|
/* (3) Write error code */
|
||||||
|
responseCode := []byte{errCode}
|
||||||
|
written, err := res.Write(responseCode);
|
||||||
|
if err != nil { return err }
|
||||||
|
if written != len(responseCode) { return errors.New("Cannot write response code") }
|
||||||
|
|
||||||
|
/* (4) Write y1 into response */
|
||||||
|
written, err = res.Write(y1)
|
||||||
|
if err != nil { return err }
|
||||||
|
if written != len(y1) { return errors.New("Cannot write y1") }
|
||||||
|
|
||||||
|
/* (5) Write y2 into response */
|
||||||
|
written, err = res.Write(y2)
|
||||||
|
if err != nil { return err }
|
||||||
|
if written != len(y1) { return errors.New("Cannot write y2") }
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"git.xdrm.io/schastsp/lib/timeid"
|
||||||
|
"git.xdrm.io/schastsp/lib/xor"
|
||||||
"git.xdrm.io/schastsp/lib/scha"
|
"git.xdrm.io/schastsp/lib/scha"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"os"
|
"os"
|
||||||
|
@ -49,7 +52,7 @@ func (s *T) fetch() error {
|
||||||
|
|
||||||
/* (3) Try to fetch hash from file */
|
/* (3) Try to fetch hash from file */
|
||||||
fetchedHash := make([]byte, scha.HSIZE)
|
fetchedHash := make([]byte, scha.HSIZE)
|
||||||
err = binary.Read(file, binary.BigEndian, s.hash)
|
err = binary.Read(file, binary.BigEndian, fetchedHash)
|
||||||
if err != nil { return err }
|
if err != nil { return err }
|
||||||
|
|
||||||
/* (4) Fail if wrong size */
|
/* (4) Fail if wrong size */
|
||||||
|
@ -58,7 +61,148 @@ func (s *T) fetch() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (5) Copy fetched hash into real one */
|
/* (5) Copy fetched hash into real one */
|
||||||
s.hash = fetchedHash
|
copy(s.hash, fetchedHash)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* (3) Request management
|
||||||
|
*
|
||||||
|
* @x1<[]byte> First request component
|
||||||
|
* @x2<[]byte> Second request component
|
||||||
|
*
|
||||||
|
* @return code<byte> Error code
|
||||||
|
* 0 -> authenticated
|
||||||
|
* 1 -> request error -> may resynchronise
|
||||||
|
* 2 -> Unknown error (check in returned 'err')
|
||||||
|
* @return err<error> Error or NIL if not
|
||||||
|
*
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
func (s *T) manageRequest(x1 []byte, x2 []byte) (byte, error) {
|
||||||
|
|
||||||
|
if DEBUG { fmt.Printf(" stored hash is H = %x\n", s.hash) }
|
||||||
|
|
||||||
|
/* (1) Extract meta data
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Get xor between x1 and x2 */
|
||||||
|
x := xor.ByteArray(x1, x2)
|
||||||
|
|
||||||
|
/* (2) Extract migration code */
|
||||||
|
mcode := uint8(x[1]) % 3;
|
||||||
|
|
||||||
|
/* (3) Fail if no migration but different hashes */
|
||||||
|
if mcode == 0 && string(x1[2:]) != string(x2[2:]) {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* (2) TimeID management
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Extract time mod */
|
||||||
|
timeMod := uint32(x[0]) % 2
|
||||||
|
|
||||||
|
/* (2) Try to guess time id */
|
||||||
|
timeId := timeid.Guess(s.ctx.Window(), timeMod)
|
||||||
|
timeIdBytes := make([]byte, 4)
|
||||||
|
fmt.Printf("Server.time[%d, %d]\n", timeId, timeMod);
|
||||||
|
binary.BigEndian.PutUint32(timeIdBytes, timeId)
|
||||||
|
|
||||||
|
/* (3) Hash guessed time id */
|
||||||
|
hashedTimeId, err := scha.Hash(timeIdBytes, 1);
|
||||||
|
if err != nil { return 2, err }
|
||||||
|
|
||||||
|
|
||||||
|
/* (3) Extract hashes
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Extract hash from x0 */
|
||||||
|
h0 := xor.ByteArray(x1, hashedTimeId)
|
||||||
|
if DEBUG { fmt.Printf(" supposing hash is h0 = %x\n", h0) }
|
||||||
|
|
||||||
|
/* (2) Extract next hash from x1 */
|
||||||
|
h1 := xor.ByteArray(x2, hashedTimeId)
|
||||||
|
if DEBUG { fmt.Printf(" supposing next is h1 = %x\n", h1) }
|
||||||
|
|
||||||
|
/* (3) Only remove timeMod if migration code = 0 */
|
||||||
|
if mcode == 0 {
|
||||||
|
h1[0] = xor.Byte(h1[0], byte(timeMod))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* (4) Hash h0 to compare with stored hash 's.hash'
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Hash 1 time to check if matches */
|
||||||
|
hashedH0, err := scha.Hash(h0, 1)
|
||||||
|
if err != nil { return 2, err }
|
||||||
|
if DEBUG { fmt.Printf(" hashed is h(h0) = %x\n", hashedH0) }
|
||||||
|
|
||||||
|
/* (2) If migration code = 2 (Rescue mode) -> hash MIN times */
|
||||||
|
if mcode == 2 {
|
||||||
|
hashedH0, err = scha.Hash(h0, s.ctx.MinDepth())
|
||||||
|
if err != nil { return 2, err }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (3) Fail if does not match */
|
||||||
|
if string(hashedH0) != string(s.hash) {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* (5) Store next hash
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
copy(s.hash, h1)
|
||||||
|
s.store();
|
||||||
|
|
||||||
|
return 0, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* (4) Generate resynchronisation response
|
||||||
|
*
|
||||||
|
* @y1<[]byte> First response component to write into
|
||||||
|
* @y2<[]byte> Second response component to write into
|
||||||
|
*
|
||||||
|
* @return err<error> Error or nil if not
|
||||||
|
*
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
func (s *T) generateResynchronisationResponse(y1 []byte, y2 []byte) error {
|
||||||
|
|
||||||
|
/* (1) Get current time id */
|
||||||
|
timeId, timeMod := timeid.Generate(s.ctx.Window())
|
||||||
|
|
||||||
|
/* (3) Hash the time id */
|
||||||
|
timeIdBytes := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(timeIdBytes, timeId)
|
||||||
|
|
||||||
|
hashedTimeId, err := scha.Hash(timeIdBytes, 1);
|
||||||
|
if err != nil { return err }
|
||||||
|
|
||||||
|
/* (4) Process y1 = H ^ h(timeId) */
|
||||||
|
copy(y1, xor.ByteArray(s.hash, hashedTimeId))
|
||||||
|
|
||||||
|
/* (5) Process y2 = H ^ h(timeId) ^ timeMod = y1 ^ timeMod */
|
||||||
|
copy(y2, xor.ByteArray(s.hash, hashedTimeId))
|
||||||
|
y2[0] = xor.Byte(y2[0], byte(timeMod))
|
||||||
|
|
||||||
|
if DEBUG {
|
||||||
|
|
||||||
|
fmt.Printf("=== y1 ===\n")
|
||||||
|
fmt.Printf(" hash is H = %x\n", s.hash)
|
||||||
|
fmt.Printf(" time id is t = %x[%d]\n", timeId, timeId)
|
||||||
|
fmt.Printf(" h(t) = %x\n", hashedTimeId)
|
||||||
|
fmt.Printf(" y1 is H+h(t) = %x\n", y1)
|
||||||
|
fmt.Printf(" check y1+h(t) eq H = %x\n", xor.ByteArray(y1, hashedTimeId))
|
||||||
|
fmt.Printf(" check y1+H eq h(t) = %x\n", xor.ByteArray(y1, s.hash))
|
||||||
|
|
||||||
|
fmt.Printf("=== y2 ===\n")
|
||||||
|
fmt.Printf(" time mod is m = %x[%d]\n", timeMod, timeMod)
|
||||||
|
fmt.Printf(" y2 is y1+m = %x\n", y2)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue