diff options
author | EuAndreh <eu@euandre.org> | 2024-08-06 10:25:30 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2024-08-06 10:25:30 -0300 |
commit | 27f238f52c79835fb924e9ca738c12344630cac7 (patch) | |
tree | de805031261d47712c446964adee3970bd699db0 | |
parent | Start using TestStart() and Testing() (diff) | |
download | gobang-27f238f52c79835fb924e9ca738c12344630cac7.tar.gz gobang-27f238f52c79835fb924e9ca738c12344630cac7.tar.xz |
Add tests for UUID functions
-rw-r--r-- | src/gobang.go | 143 | ||||
-rw-r--r-- | tests/gobang.go | 126 |
2 files changed, 220 insertions, 49 deletions
diff --git a/src/gobang.go b/src/gobang.go index 4775dfc..dc0d9bc 100644 --- a/src/gobang.go +++ b/src/gobang.go @@ -33,9 +33,14 @@ const ( LevelDebug logLevel = 4 ) -const lengthUUID = 16 + +const ( + uuidDashCount = 4 + uuidByteCount = 16 + uuidEncodedLength = (uuidByteCount * 2) + uuidDashCount +) type UUID struct { - bytes [lengthUUID]byte + bytes [uuidByteCount]byte } type Gauge struct { @@ -54,7 +59,7 @@ type CopyResult struct { const maxInt = int((^uint(0)) >> 1) const ( - scrypt_N = 32768 + scrypt_N = 1 << 15 scrypt_r = 8 scrypt_p = 1 scryptSaltMinLength = 32 @@ -62,7 +67,6 @@ const ( ) - // Private variables // lastV7time is the last time we returned stored as: @@ -125,7 +129,7 @@ func _PBKDF2Key( hashLen := prf.Size() numBlocks := (keyLen + hashLen - 1) / hashLen - var buf [4]byte + var buffer [4]byte dk := make([]byte, 0, numBlocks*hashLen) U := make([]byte, hashLen) for block := 1; block <= numBlocks; block++ { @@ -134,11 +138,11 @@ func _PBKDF2Key( // U_1 = PRF(password, salt || uint(i)) prf.Reset() prf.Write(salt) - buf[0] = byte(block >> 24) - buf[1] = byte(block >> 16) - buf[2] = byte(block >> 8) - buf[3] = byte(block >> 0) - prf.Write(buf[:4]) + buffer[0] = byte(block >> 24) + buffer[1] = byte(block >> 16) + buffer[2] = byte(block >> 8) + buffer[3] = byte(block >> 0) + prf.Write(buffer[:4]) dk = prf.Sum(dk) T := dk[len(dk) - hashLen:] copy(U, T) @@ -380,16 +384,27 @@ func scrypt( return _PBKDF2Key(password, b, 1, keyLen, sha256.New), nil } -func Salt() []byte { - var buf [scryptSaltMinLength]byte - _, err := io.ReadFull(rand.Reader, buf[:]) +func Random(length int) []byte { + buffer := make([]byte, length) + _, err := io.ReadFull(rand.Reader, buffer) FatalIf(err) - return buf[:] + return buffer +} + +func Salt() []byte { + return Random(scryptSaltMinLength) } func Hash(password []byte, salt []byte) []byte{ Assert(len(salt) >= scryptSaltMinLength, "salt is too small") - hash, err := scrypt(password, salt, scrypt_N, scrypt_r, scrypt_p, scryptDesiredLength) + hash, err := scrypt( + password, + salt, + scrypt_N, + scrypt_r, + scrypt_p, + scryptDesiredLength, + ) FatalIf(err) return hash } @@ -410,42 +425,43 @@ func getV7Time(nano int64) (int64, int64) { timeMutex.Lock() defer timeMutex.Unlock() if now <= lastV7Time { - now = lastV7Time + 1 + now = lastV7Time + 1 milli = now >> 12 - seq = now & 0xfff + seq = now & 0xfff } lastV7Time = now return milli, seq } -func NewUUID() UUID { - var buf [lengthUUID]byte - _, err := io.ReadFull(rand.Reader, buf[7:]) - if err != nil { - panic(err) - } +func newUUIDFrom(randomBuffer [uuidByteCount]byte, now int64) UUID { + randomBuffer[6] = (randomBuffer[6] & 0x0f) | 0x40 // Version 4 + randomBuffer[8] = (randomBuffer[8] & 0x3f) | 0x80 // Variant is 10 - buf[6] = (buf[6] & 0x0f) | 0x40 // Version 4 - buf[8] = (buf[8] & 0x3f) | 0x80 // Variant is 10 + t, s := getV7Time(now) - t, s := getV7Time(time.Now().UnixNano()) + randomBuffer[0] = byte(t >> 40) + randomBuffer[1] = byte(t >> 32) + randomBuffer[2] = byte(t >> 24) + randomBuffer[3] = byte(t >> 16) + randomBuffer[4] = byte(t >> 8) + randomBuffer[5] = byte(t >> 0) - buf[0] = byte(t >> 40) - buf[1] = byte(t >> 32) - buf[2] = byte(t >> 24) - buf[3] = byte(t >> 16) - buf[4] = byte(t >> 8) - buf[5] = byte(t >> 0) + randomBuffer[6] = 0x70 | (0x0f & byte(s >> 8)) + randomBuffer[7] = byte(s) + return UUID { bytes: randomBuffer } +} + +func NewUUID() UUID { + var buffer [uuidByteCount]byte + _, err := io.ReadFull(rand.Reader, buffer[7:]) + FatalIf(err) - buf[6] = 0x70 | (0x0f & byte(s >> 8)) - buf[7] = byte(s) - return UUID { bytes: buf } + now := time.Now().UnixNano() + return newUUIDFrom(buffer, now) } -func (uuid UUID) ToString() string { - const dashCount = 4 - const encodedLength = (lengthUUID * 2) + dashCount - dst := [encodedLength]byte { +func (uuid UUID) String() string { + dst := [uuidEncodedLength]byte { 0, 0, 0, 0, 0, 0, 0, 0, '-', @@ -469,6 +485,37 @@ func (uuid UUID) ToString() string { return string(dst[:]) } +var ( + dashIndexes = []int { 8, 13, 18, 23 } + emptyUUID = UUID {} + badUUIDLengthError = errors.New("str isn't of the correct length") + badUUIDDashCountError = errors.New("Bad count of dashes in string") + badUUIDDashPositionError = errors.New("Bad char in string") +) +func ParseUUID(str string) (UUID, error) { + if len(str) != uuidEncodedLength { + return emptyUUID, badUUIDLengthError + } + + if strings.Count(str, "-") != uuidDashCount { + return emptyUUID, badUUIDDashCountError + } + + for _, idx := range dashIndexes { + if str[idx] != '-' { + return emptyUUID, badUUIDDashPositionError + } + } + + hexstr := strings.Join(strings.Split(str, "-"), "") + data, err := hex.DecodeString(hexstr) + if err != nil { + return emptyUUID, err + } + + return UUID { bytes: [uuidByteCount]byte(data) }, nil +} + func Debug(message string, type_ string, args ...any) { if level < LevelDebug { return @@ -478,7 +525,7 @@ func Debug(message string, type_ string, args ...any) { message, append( []any { - "id", NewUUID().ToString(), + "id", NewUUID(), "kind", "log", "type", type_, }, @@ -496,7 +543,7 @@ func Info(message string, type_ string, args ...any) { message, append( []any { - "id", NewUUID().ToString(), + "id", NewUUID(), "kind", "log", "type", type_, }, @@ -514,7 +561,7 @@ func Warning(message string, type_ string, args ...any) { message, append( []any { - "id", NewUUID().ToString(), + "id", NewUUID(), "kind", "log", "type", type_, }, @@ -532,7 +579,7 @@ func Error(message string, type_ string, args ...any) { message, append( []any { - "id", NewUUID().ToString(), + "id", NewUUID(), "kind", "log", "type", type_, }, @@ -550,7 +597,7 @@ func Metric(type_ string, label string, args ...any) { "_", append( []any { - "id", NewUUID().ToString(), + "id", NewUUID(), "kind", "metric", "type", type_, "label", label, @@ -636,7 +683,11 @@ func TestStart(name string) { func Testing(message string, body func()) { if showColour() { - fmt.Fprintf(os.Stderr, "\033[0;33mtesting\033[0m: %s... ", message) + fmt.Fprintf( + os.Stderr, + "\033[0;33mtesting\033[0m: %s... ", + message, + ) body() fmt.Fprint(os.Stderr, "\033[0;32mOK\033[0m.\n") } else { @@ -682,7 +733,7 @@ func setLoggerOutput(w io.Writer) { "info", "pid", os.Getpid(), "ppid", os.Getppid(), - "puuid", NewUUID().ToString(), + "puuid", NewUUID(), ), )) } diff --git a/tests/gobang.go b/tests/gobang.go index 61168d6..46cc9c5 100644 --- a/tests/gobang.go +++ b/tests/gobang.go @@ -5,10 +5,12 @@ import ( "crypto/sha1" "crypto/sha256" "encoding/base64" + "encoding/hex" "encoding/json" "fmt" "hash" "log/slog" + "time" ) @@ -324,11 +326,18 @@ func test_scrypt() { Testing("example value", func() { const expected = "lGnMz8io0AUkfzn6Pls1qX20Vs7PGN6sbYQ2TQgY12M=" - // DO NOT use this salt value; generate your own random salt. 8 bytes is - // a good length. + // DO NOT use this salt value; generate your own random salt. + // 8 bytes is a good length. salt := []byte{0xc8, 0x28, 0xf2, 0x58, 0xa7, 0x6a, 0xad, 0x7b} - dk, err := scrypt([]byte("some password"), salt, 1<<15, 8, 1, 32) + dk, err := scrypt( + []byte("some password"), + salt, + 1<<15, + 8, + 1, + 32, + ) ErrorIf(err) given := base64.StdEncoding.EncodeToString(dk) @@ -336,6 +345,113 @@ func test_scrypt() { }) } +func test_newUUIDFrom() { + TestStart("newUUIDFrom()") + + Testing("same UUID from same input", func() { + for i := 0; i < 100; i++ { + var b [uuidByteCount]byte + buffer := Random(uuidByteCount) + now := time.Now().UnixNano() + + lastV7Time = now + 1 + copy(b[:], buffer) + uuid1 := newUUIDFrom(b, now) + + lastV7Time = now + 1 + copy(b[:], buffer) + uuid2 := newUUIDFrom(b, now) + + AssertEqual(uuid1, uuid2) + } + }) +} + +func test_NewUUID() { + TestStart("NewUUID()") + + Testing("we can generate UUID values", func() { + var uuid UUID = NewUUID() + AssertEqual(len(uuid.bytes), uuidByteCount) + }) +} + +func test_UUIDString() { + TestStart("UUID.String()") + + Testing("simple example values", func() { + uuids := []UUID { + UUID { + bytes: [uuidByteCount]byte { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, + UUID { + bytes: [uuidByteCount]byte { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, + }, + }, + UUID { + bytes: [uuidByteCount]byte { + 222, 222, 222, 222, 222, 222, 222, 222, + 222, 222, 222, 222, 222, 222, 222, 222, + }, + }, + UUID { + bytes: [uuidByteCount]byte { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + }, + }, + } + + strs := []string { + "00000000-0000-0000-0000-000000000000", + "00010203-0405-0607-0809-0a0b0c0d0e0f", + "dededede-dede-dede-dede-dededededede", + "ffffffff-ffff-ffff-ffff-ffffffffffff", + } + + for i := range uuids { + AssertEqual(uuids[i].String(), strs[i]) + } + }) +} + +func test_ParseUUID() { + TestStart("ParseUUID()") + + Testing("UUID -> string -> UUID round trip", func() { + for i := 0; i < 100; i++ { + uuid0 := NewUUID() + uuid1, err := ParseUUID(uuid0.String()) + ErrorIf(err) + AssertEqual(uuid1, uuid0) + } + }) + + Testing("errors we detect", func() { + var err error + + _, err = ParseUUID("") + AssertEqual(err, badUUIDLengthError) + + _, err = ParseUUID("---000000000000000000000000000000000") + AssertEqual(err, badUUIDDashCountError) + + _, err = ParseUUID("----00000000000000000000000000000000") + AssertEqual(err, badUUIDDashPositionError) + + _, err = ParseUUID("00000000-0000-0000-0000-00000000000g") + AssertEqual(err, hex.InvalidByteError('g')) + + _, err = ParseUUID("00000000-0000-0000-0000-000000000000") + ErrorIf(err) + }) +} + func TestSetLoggerOutput() { return type entry struct { @@ -364,4 +480,8 @@ func TestSetLoggerOutput() { func MainTest() { test__PBKDF() test_scrypt() + test_newUUIDFrom() + test_NewUUID() + test_UUIDString() + test_ParseUUID() } |