diff --git a/cmd/simple/simple.go b/cmd/simple/simple.go index f77f16f..dd4c1cb 100644 --- a/cmd/simple/simple.go +++ b/cmd/simple/simple.go @@ -7,8 +7,8 @@ import ( "flag" "fmt" "git.xdrm.io/schastsp/context" - "git.xdrm.io/schastsp/client" - "git.xdrm.io/schastsp/server" + "git.xdrm.io/schastsp/internal/client" + "git.xdrm.io/schastsp/internal/server" ) /* Store target config paths */ diff --git a/client/client.go b/internal/client/client.go similarity index 99% rename from client/client.go rename to internal/client/client.go index 51593bc..39c6f4a 100644 --- a/client/client.go +++ b/internal/client/client.go @@ -5,7 +5,7 @@ import ( "fmt" "git.xdrm.io/schastsp/client/keyset" "git.xdrm.io/schastsp/context" - "git.xdrm.io/schastsp/pkg/scha" + "git.xdrm.io/schastsp/util/scha" "io" ) diff --git a/client/client.internal.go b/internal/client/client.internal.go similarity index 98% rename from client/client.internal.go rename to internal/client/client.internal.go index db6af4f..322d2b1 100644 --- a/client/client.internal.go +++ b/internal/client/client.internal.go @@ -4,9 +4,9 @@ import ( "encoding/binary" "fmt" "git.xdrm.io/schastsp/client/keyset" - "git.xdrm.io/schastsp/pkg/scha" - "git.xdrm.io/schastsp/pkg/timeid" - "git.xdrm.io/schastsp/pkg/xor" + "git.xdrm.io/schastsp/util/scha" + "git.xdrm.io/schastsp/util/timeid" + "git.xdrm.io/schastsp/util/xor" ) /* (1) Updates 'key' and 'sync' with files diff --git a/client/config.go b/internal/client/config.go similarity index 98% rename from client/config.go rename to internal/client/config.go index 5398895..fdc1e9a 100644 --- a/client/config.go +++ b/internal/client/config.go @@ -1,7 +1,7 @@ package client; import ( - "git.xdrm.io/schastsp/client/keyset" + "git.xdrm.io/schastsp/internal/client/keyset" "fmt" "path/filepath" "errors" diff --git a/client/keyset/keyset.go b/internal/client/keyset/keyset.go similarity index 99% rename from client/keyset/keyset.go rename to internal/client/keyset/keyset.go index 4e2daa0..2d87007 100644 --- a/client/keyset/keyset.go +++ b/internal/client/keyset/keyset.go @@ -4,7 +4,7 @@ import ( "encoding/binary" "errors" "git.xdrm.io/schastsp/context" - "git.xdrm.io/schastsp/pkg/scha" + "git.xdrm.io/schastsp/util/scha" "io" ) diff --git a/client/keyset/keyset.internal.go b/internal/client/keyset/keyset.internal.go similarity index 100% rename from client/keyset/keyset.internal.go rename to internal/client/keyset/keyset.internal.go diff --git a/client/keyset/keyset_test.go b/internal/client/keyset/keyset_test.go similarity index 100% rename from client/keyset/keyset_test.go rename to internal/client/keyset/keyset_test.go diff --git a/server/server.go b/internal/server/server.go similarity index 99% rename from server/server.go rename to internal/server/server.go index 7b68c3e..495bd76 100644 --- a/server/server.go +++ b/internal/server/server.go @@ -2,7 +2,7 @@ package server import ( "fmt" - "git.xdrm.io/schastsp/pkg/scha" + "git.xdrm.io/schastsp/util/scha" "io" "os" "path/filepath" diff --git a/server/server.internal.go b/internal/server/server.internal.go similarity index 98% rename from server/server.internal.go rename to internal/server/server.internal.go index 41bcf94..aa0be2f 100644 --- a/server/server.internal.go +++ b/internal/server/server.internal.go @@ -4,9 +4,9 @@ import ( "encoding/binary" "errors" "fmt" - "git.xdrm.io/schastsp/pkg/scha" - "git.xdrm.io/schastsp/pkg/timeid" - "git.xdrm.io/schastsp/pkg/xor" + "git.xdrm.io/schastsp/util/scha" + "git.xdrm.io/schastsp/util/timeid" + "git.xdrm.io/schastsp/util/xor" "os" ) diff --git a/util/scha/hash.go b/util/scha/hash.go new file mode 100644 index 0000000..50ba779 --- /dev/null +++ b/util/scha/hash.go @@ -0,0 +1,101 @@ +package scha + +import ( + "errors" + "crypto/sha512" + "git.xdrm.io/schastsp/pkg/xor" +) + +/* (0) Static +---------------------------------------------------------*/ +/* (1) Constants */ +const HSIZE uint16 = sha512.Size; +const HBITSIZE uint32 = uint32(HSIZE) * 8; + + + + +/* (1) Basic hash function +* +* @input<[]byte> Byte array input +* +* @return digest<[]byte]> Byte array digest +* +---------------------------------------------------------*/ +func hash(input []byte) []byte{ + + /* (1) Create sha512 hasher */ + hasher := sha512.New(); + + /* (2) Defer memory cleanup */ + defer hasher.Reset(); + + /* (3) Set input to be hashed */ + hasher.Write(input); + + /* (4) Extract digest */ + return hasher.Sum(nil); + +} + + +/* (2) Public hashing interface +* +* @input<[]byte> Byte array input +* @depth Number of time to hash recursively (must be > 1) +* @salt<[]byte> [OPT] Optional salt +* @pepper<[]byte> [OPT] Optional pepper +* +* @return digest<[]byte]> Byte array digest +* @return err If consistence error +* +---------------------------------------------------------*/ +func Hash(input []byte, depth uint16, options... []byte) ([]byte, error) { + + + /* (1) Manage errors errors + ---------------------------------------------------------*/ + /* (1) Avoid no depth (no hash at all) */ + if( depth < 1 ){ + return nil, errors.New("Cannot use a 'depth' of zero. This is inconsistent and means that no hash will be processed"); + } + + /* (2) Avoir empty input */ + if( len(input) < 1 ){ + return nil, errors.New("Cannot use an empty 'input'. This is inconsistent"); + } + + + /* (2) Extract optional arguments + ---------------------------------------------------------*/ + /* (1) Optional salt */ + salt := make([]byte, 0) + if len(options) > 0 { salt = options[0] } + + /* (2) Optional pepper */ + pepper := make([]byte, 0) + if len(options) > 0 { pepper = options[0] } + + + /* (3) Process cyclic hash + ---------------------------------------------------------*/ + /* (1) Initialise digest */ + digest := make([]byte, 0, HSIZE) + + /* (2) Process first hash + salt */ + digest = hash( xor.ByteArray(input, salt) ) + + /* (3) Iterate @depth times */ + for depth--; depth > 0; depth-- { + + // Add Pepper only for last time + if( depth == 1 ){ + digest = hash( xor.ByteArray(digest, pepper) ) + } else { + digest = hash(digest) + } + + } + + return digest, nil +} \ No newline at end of file diff --git a/util/scha/hash_test.go b/util/scha/hash_test.go new file mode 100644 index 0000000..65c50b2 --- /dev/null +++ b/util/scha/hash_test.go @@ -0,0 +1,156 @@ +package scha + +import "testing" + +func TestSimpleHash(t *testing.T){ + + input := []byte("somePlainText"); + expected := []byte{ + 0x4c, 0xcb, 0x0e, 0xf6, 0x81, 0x99, 0x2e, 0xd6, 0xb8, 0x17, 0x52, 0x1d, 0x09, + 0x6e, 0x99, 0x19, 0xe7, 0xda, 0x50, 0xc8, 0xbf, 0x64, 0xae, 0xc1, 0x4f, 0xaa, + 0x47, 0x06, 0xf3, 0x49, 0x30, 0x8a, 0x90, 0x8e, 0xd2, 0xff, 0xc2, 0x6d, 0xee, + 0xaa, 0xd6, 0x45, 0xd8, 0xb3, 0x17, 0xe3, 0xb9, 0x45, 0x29, 0x26, 0xe2, 0x8e, + 0x99, 0x50, 0x94, 0x49, 0x90, 0x02, 0xa5, 0x61, 0x4a, 0x3f, 0x5e, 0xfa}; + got, err := Hash(input, 1); + digestLength := uint(len(got)); + + /* (2) Fail on errors */ + if err != nil { + t.Errorf("Expected no errors, got: %s", err); + } + + /* (2) Fail on wrong size */ + if uint16(digestLength) != HSIZE { + t.Errorf("Expected hash digest of %d bytes ; %d bytes received", HSIZE, digestLength); + } + + /* (3) Check each byte */ + for k, v := range got{ + + if v != expected[k] { + t.Errorf("Expected sha[%d] of '%x' to be '%x' ; received '%x'", HSIZE, input, expected, got); + return; + } + + } + +} + + + +func TestDepth1Plus1Equals2(t *testing.T){ + + input := []byte("someOtherPlainText"); + + /* (1) Calculate H1 */ + h1, err := Hash(input, 1) + + if err != nil { + t.Errorf("Expected no error"); + return; + } + + /* (2) Calculate H1(H1) */ + h11, err := Hash(h1, 1); + + if err != nil { + t.Errorf("Expected no error"); + return; + } + + /* (3) Calculate H2 */ + h2, err := Hash(input, 1+1); + + if err != nil { + t.Errorf("Expected no error"); + return; + } + + /* (4) Manage different length */ + if len(h11) != len(h2) || len(h11) != int(HSIZE) { + t.Errorf("Expected digest lengths to be %d, got %d and %d", HSIZE, len(h11), len(h2)); + return; + } + + /* (5) Compare the 2 strings */ + for k, v := range h11 { + if v != h2[k] { + t.Errorf("Expected h2() to be equal to h1(h1())\n got '%x'\n expected '%x'", h2, h11) + return; + } + } + +} + + + +func TestDepth52Plus64Equals116(t *testing.T){ + + input := []byte("someOtherPlainText"); + + /* (1) Calculate H52 */ + h52, err := Hash(input, 52) + + if err != nil { + t.Errorf("Expected no error"); + return; + } + + /* (2) Calculate H52(H64) */ + h5264, err := Hash(h52, 64); + + if err != nil { + t.Errorf("Expected no error"); + return; + } + + /* (3) Calculate H116 */ + h116, err := Hash(input, 52+64); + + if err != nil { + t.Errorf("Expected no error"); + return; + } + + /* (4) Manage different length */ + if len(h5264) != len(h116) || len(h5264) != int(HSIZE) { + t.Errorf("Expected digest lengths to be %d, got %d and %d", HSIZE, len(h5264), len(h116)); + return; + } + + /* (5) Compare the 2 strings */ + for k, v := range h5264 { + if v != h116[k] { + t.Errorf("Expected h116() to be equal to h52(h64())\n got '%x'\n expected '%x'", h116, h5264) + return; + } + } + +} + + + +func TestDepthError(t *testing.T){ + + input := []byte("somePlainText"); + _, err := Hash(input, 0); + + /* (2) Fail on errors */ + if err == nil { + t.Errorf("Expected an error for depth of 0"); + } + +} + + + +func TestEmptyInputError(t *testing.T){ + + _, err := Hash(nil, 1); + + /* (2) Fail on errors */ + if err == nil { + t.Errorf("Expected an error for empty input"); + } + +} \ No newline at end of file diff --git a/util/timeid/timeid.go b/util/timeid/timeid.go new file mode 100644 index 0000000..d82d506 --- /dev/null +++ b/util/timeid/timeid.go @@ -0,0 +1,51 @@ +package timeid + +import ( + "math" + "time" +) + + +/* (1) Generates the current time id +* +* @wsize Window Size in seconds +* +* @return id Current time id +* @return parity Current time parity +* +---------------------------------------------------------*/ +func Generate(wsize float64) (uint32, uint32){ + + /* (1) If wsize is 0 (div by zero possible error) */ + if wsize == 0 { return 0, 0 } + + /* (2) Get current timestamp */ + timestamp := float64( time.Now().Unix() ); + + /* (3) Calculate the time id */ + var id = uint32( timestamp / wsize ); + + /* (4) Calculate parity */ + var parity = id % 2; + + return id, parity; +} + + +/* (2) Try to guess a previous time id from its parity +* +* @wsize Window Size in seconds +* @parity Received parity +* +* @return id The guessed time id +* +---------------------------------------------------------*/ +func Guess(wsize float64, parity uint32) uint32{ + + /* (1) Get current time id */ + var idNow, parityNow = Generate(wsize); + + /* (2) Update ID with tidNow parity difference */ + return idNow - uint32(math.Abs( float64(parityNow) - float64(parity) )); + +} \ No newline at end of file diff --git a/util/timeid/timeid_test.go b/util/timeid/timeid_test.go new file mode 100644 index 0000000..651b14f --- /dev/null +++ b/util/timeid/timeid_test.go @@ -0,0 +1,23 @@ +package timeid + +import ( + "time" + "testing" +) + + +func TestGuessing(t *testing.T){ + + var windowSize float64 = .5; + + id, parity := Generate(windowSize); + + time.Sleep( time.Duration(windowSize) * time.Second); + + var guessedId = Guess(windowSize, parity); + + if id != guessedId { + t.Errorf("Wrong guessed id, expected '%d' ; got '%d'", id, guessedId); + } + +} \ No newline at end of file diff --git a/util/xor/xor.go b/util/xor/xor.go new file mode 100644 index 0000000..cc7f1ab --- /dev/null +++ b/util/xor/xor.go @@ -0,0 +1,67 @@ +package xor + +import "math" + +/* (1) bitwise XOR between bytes +* +* @_left Left operand +* @_right Right operand +* +* @return out Bitwise XOR result between _left and _right +* +---------------------------------------------------------*/ +func Byte(_left byte, _right byte) byte { + + return _left ^ _right; + +} + + +/* (2) bitwise XOR between Byte arrays +* +* @_left Left operand +* @_right Right operand +* +* @return out Bitwise XOR result between _left and _right +* +* @@ NOTE @@ +* If an argument is smaller, it will be right-padded with null bytes (0x00) +* +---------------------------------------------------------*/ +func ByteArray(_left []byte, _right []byte) []byte { + + /* (1) Process + ---------------------------------------------------------*/ + /* (1) Extract lengths */ + ll := len(_left) + lr := len(_right) + + /* (2) Get max length */ + l := int(math.Max(float64(ll), float64(lr))) + + /* (3) Initialise 'out' */ + out := make([]byte, l, l) + + /* (2) Process bitwise XOR */ + for i := 0 ; i < l ; i++ { + + // 1. Out of range for _left + if i >= ll { + + out[i] = _right[i]; + + } else if i >= lr { + + out[i] = _left[i]; + + } else { + + out[i] = Byte(_left[i], _right[i]) + + } + + } + + return out + +}