Browse Source

more refactoring

Adam Ierymenko 5 years ago
parent
commit
5275a34b0b
5 changed files with 301 additions and 38 deletions
  1. 0 0
      attic/CoreTests.cpp
  2. 0 1
      go/native/CMakeLists.txt
  3. 10 8
      node/AES.hpp
  4. 250 9
      node/Tests.cpp
  5. 41 20
      node/Utils.cpp

+ 0 - 0
go/native/CoreTests.cpp → attic/CoreTests.cpp


+ 0 - 1
go/native/CMakeLists.txt

@@ -3,7 +3,6 @@ project(zt_go_native)
 
 set(src
 	GoGlue.cpp
-	CoreTests.cpp
 )
 
 set(headers

+ 10 - 8
node/AES.hpp

@@ -64,22 +64,24 @@ public:
 	 *
 	 * @param key 256-bit key
 	 */
-	explicit ZT_ALWAYS_INLINE AES(const uint8_t key[32]) noexcept { this->init(key); }
+	explicit ZT_ALWAYS_INLINE AES(const void *const key) noexcept { this->init(key); }
 
 	ZT_ALWAYS_INLINE ~AES() { Utils::burn(&_k,sizeof(_k)); }
 
 	/**
 	 * Set (or re-set) this AES256 cipher's key
+	 *
+	 * @param key 256-bit / 32-byte key
 	 */
-	ZT_ALWAYS_INLINE void init(const uint8_t key[32]) noexcept
+	ZT_ALWAYS_INLINE void init(const void *key) noexcept
 	{
 #ifdef ZT_AES_AESNI
 		if (likely(Utils::CPUID.aes)) {
-			_init_aesni(key);
+			_init_aesni(reinterpret_cast<const uint8_t *>(key));
 			return;
 		}
 #endif
-		_initSW(key);
+		_initSW(reinterpret_cast<const uint8_t *>(key));
 	}
 
 	/**
@@ -88,7 +90,7 @@ public:
 	 * @param in Input block
 	 * @param out Output block (can be same as input)
 	 */
-	ZT_ALWAYS_INLINE void encrypt(const uint8_t in[16],uint8_t out[16]) const noexcept
+	ZT_ALWAYS_INLINE void encrypt(const void *const in,void *const out) const noexcept
 	{
 #ifdef ZT_AES_AESNI
 		if (likely(Utils::CPUID.aes)) {
@@ -96,7 +98,7 @@ public:
 			return;
 		}
 #endif
-		_encryptSW(in,out);
+		_encryptSW(reinterpret_cast<const uint8_t *>(in),reinterpret_cast<uint8_t *>(out));
 	}
 
 	/**
@@ -105,7 +107,7 @@ public:
 	 * @param in Input block
 	 * @param out Output block (can be same as input)
 	 */
-	ZT_ALWAYS_INLINE void decrypt(const uint8_t in[16],uint8_t out[16]) const noexcept
+	ZT_ALWAYS_INLINE void decrypt(const void *const in,void *const out) const noexcept
 	{
 #ifdef ZT_AES_AESNI
 		if (likely(Utils::CPUID.aes)) {
@@ -113,7 +115,7 @@ public:
 			return;
 		}
 #endif
-		_decryptSW(in,out);
+		_decryptSW(reinterpret_cast<const uint8_t *>(in),reinterpret_cast<uint8_t *>(out));
 	}
 
 	/**

+ 250 - 9
node/Tests.cpp

@@ -37,9 +37,9 @@
 #include "LZ4.hpp"
 #include "Hashtable.hpp"
 #include "FCV.hpp"
+#include "SHA512.hpp"
 
 #include <cstdint>
-#include <cstdlib>
 #include <cstring>
 #include <cstdio>
 #include <set>
@@ -98,7 +98,7 @@ struct C25519TestVector
 	uint8_t priv1[64];
 	uint8_t pub2[64];
 	uint8_t priv2[64];
-	uint8_t agreement[64];
+	uint8_t agreementSha512[64];
 	uint8_t agreementSignedBy1[96];
 	uint8_t agreementSignedBy2[96];
 };
@@ -144,6 +144,13 @@ static const C25519TestVector C25519_TEST_VECTORS[ZT_NUM_C25519_TEST_VECTORS] =
 
 // --------------------------------------------------------------------------------------------------------------------
 
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define ZT_ENDIAN_S "little"
+#else
+#define ZT_ENDIAN_S "big"
+#endif
+
+// This is basically a unit test for compiler packed structure behavior.
 ZT_PACKED_STRUCT(struct StructPackingTestSample {
 	uint8_t foo;
 	uint16_t bar;
@@ -155,13 +162,41 @@ ZT_PACKED_STRUCT(struct StructPackingTestSample {
 	uint8_t lol;
 });
 
-#define ZT_T_ASSERT(e) if (!(e)) { ZT_T_PRINTF("FAILED (simple assert: " #e ")" ZT_EOL_S); return "simple assert: " #e; }
+// Increments and decrements a counter based on object create/destroy
+class LifeCycleTracker
+{
+public:
+	ZT_ALWAYS_INLINE LifeCycleTracker() :
+		cnt(nullptr)
+	{
+	}
+	ZT_ALWAYS_INLINE LifeCycleTracker(const LifeCycleTracker &ltc) :
+		cnt(ltc.cnt)
+	{
+		if (*cnt) ++*cnt;
+	}
+	explicit ZT_ALWAYS_INLINE LifeCycleTracker(long &c) :
+		cnt(&c)
+	{
+		++c;
+	}
+	ZT_ALWAYS_INLINE ~LifeCycleTracker()
+	{
+		if (cnt) --*cnt;
+	}
+	ZT_ALWAYS_INLINE LifeCycleTracker &operator=(const LifeCycleTracker &ltc)
+	{
+		if (&ltc != this) {
+			if (*cnt) --*cnt;
+			cnt = ltc.cnt;
+			if (*cnt) ++*cnt;
+		}
+		return *this;
+	}
+	long *cnt;
+};
 
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define ZT_ENDIAN_S "little"
-#else
-#define ZT_ENDIAN_S "big"
-#endif
+#define ZT_T_ASSERT(e) if (!(e)) { ZT_T_PRINTF("FAILED (simple assert: " #e ")" ZT_EOL_S); return "simple assert: " #e; }
 
 extern "C" const char *ZTT_general()
 {
@@ -206,6 +241,9 @@ extern "C" const char *ZTT_general()
 			ZT_T_ASSERT(sizeof(Protocol::Header) == ZT_PROTO_MIN_PACKET_LENGTH);
 			ZT_T_ASSERT(sizeof(Protocol::FragmentHeader) == ZT_PROTO_MIN_FRAGMENT_LENGTH);
 			ZT_T_ASSERT(sizeof(sockaddr_storage) == sizeof(InetAddress));
+			ZT_T_ASSERT(sizeof(sockaddr_in) <= sizeof(InetAddress));
+			ZT_T_ASSERT(sizeof(sockaddr_in6) <= sizeof(InetAddress));
+			ZT_T_ASSERT(sizeof(sockaddr) <= sizeof(InetAddress));
 			ZT_T_PRINTF("OK" ZT_EOL_S);
 		}
 
@@ -270,6 +308,92 @@ extern "C" const char *ZTT_general()
 			}
 			ZT_T_PRINTF("OK" ZT_EOL_S);
 		}
+
+		{
+			ZT_T_PRINTF("[general] Testing FCV (fixed capacity vector)... ");
+			long cnt = 0;
+			FCV<LifeCycleTracker,1024> test,test2;
+			for(unsigned int i=0;i<512;++i)
+				test.push_back(LifeCycleTracker(cnt));
+			if (cnt != 512) {
+				ZT_T_PRINTF("FAILED (expected 512 objects, got %lu (1))" ZT_EOL_S,cnt);
+				return "FCV object life cycle test failed (1)";
+			}
+			test2 = test;
+			if (cnt != 1024) {
+				ZT_T_PRINTF("FAILED (expected 1024 objects, got %lu (2))" ZT_EOL_S,cnt);
+				return "FCV object life cycle test failed (2)";
+			}
+			test.clear();
+			if (cnt != 512) {
+				ZT_T_PRINTF("FAILED (expected 512 objects, got %lu (3))" ZT_EOL_S,cnt);
+				return "FCV object life cycle test failed (3)";
+			}
+			for(unsigned int i=0;i<512;++i)
+				test.push_back(LifeCycleTracker(cnt));
+			if (cnt != 1024) {
+				ZT_T_PRINTF("FAILED (expected 1024 objects, got %lu (4))" ZT_EOL_S,cnt);
+				return "FCV object life cycle test failed (4)";
+			}
+			test.clear();
+			test2.clear();
+			if (cnt != 0) {
+				ZT_T_PRINTF("FAILED (expected 0 objects, got %lu (5))" ZT_EOL_S,cnt);
+				return "FCV object life cycle test failed (5)";
+			}
+			ZT_T_PRINTF("OK" ZT_EOL_S);
+		}
+
+		{
+			ZT_T_PRINTF("[general] Testing Hashtable... ");
+
+			long cnt = 0;
+			Hashtable< unsigned int,LifeCycleTracker > test,test2;
+			for(unsigned int i=0;i<512;++i)
+				test[i] = LifeCycleTracker(cnt);
+			if (cnt != 512) {
+				ZT_T_PRINTF("FAILED (expected 512 objects, got %lu (1))" ZT_EOL_S,cnt);
+				return "Hashtable test failed (1)";
+			}
+			test2 = test;
+			if (cnt != 1024) {
+				ZT_T_PRINTF("FAILED (expected 1024 objects, got %lu (2))" ZT_EOL_S,cnt);
+				return "Hashtable test failed (2)";
+			}
+			test.clear();
+			if (cnt != 512) {
+				ZT_T_PRINTF("FAILED (expected 512 objects, got %lu (3))" ZT_EOL_S,cnt);
+				return "Hashtable test failed (3)";
+			}
+			for(unsigned int i=0;i<512;++i)
+				test[i] = LifeCycleTracker(cnt);
+			if (cnt != 1024) {
+				ZT_T_PRINTF("FAILED (expected 1024 objects, got %lu (4))" ZT_EOL_S,cnt);
+				return "Hashtable test failed (4)";
+			}
+			test.clear();
+			test2.clear();
+			if (cnt != 0) {
+				ZT_T_PRINTF("FAILED (expected 0 objects, got %lu (5))" ZT_EOL_S,cnt);
+				return "Hashtable test failed (5)";
+			}
+
+			Hashtable< unsigned int,unsigned int > test3;
+			for(unsigned int i=0;i<1111;++i)
+				test3[i] = i;
+			if (test3.size() != 1111) {
+				ZT_T_PRINTF("FAILED (size() incorrect)" ZT_EOL_S);
+				return "Hashtable test failed (size() incorrect)";
+			}
+			for(unsigned int i=0;i<1111;++i) {
+				if (test3[i] != i) {
+					ZT_T_PRINTF("FAILED (lookup test)" ZT_EOL_S);
+					return "Hashtable test failed (lookup)";
+				}
+			}
+
+			ZT_T_PRINTF("OK" ZT_EOL_S);
+		}
 	} catch (std::exception &e) {
 		ZT_T_PRINTF(ZT_EOL_S "[general] Unexpected exception: %s" ZT_EOL_S,e.what());
 		return e.what();
@@ -277,12 +401,129 @@ extern "C" const char *ZTT_general()
 		ZT_T_PRINTF(ZT_EOL_S "[general] Unexpected exception: unknown exception" ZT_EOL_S);
 		return "an unknown exception occurred";
 	}
-
 	return nullptr;
 }
 
 extern "C" const char *ZTT_crypto()
 {
+	try {
+		{
+			ZT_T_PRINTF("[crypto] Testing SHA384 and SHA512... ");
+			uint8_t h[64];
+			SHA512(h,SHA512_TV0_INPUT,strlen(SHA512_TV0_INPUT));
+			if (memcmp(h,SHA512_TV0_DIGEST,64) != 0) {
+				ZT_T_PRINTF("FAILED (SHA512)" ZT_EOL_S);
+				return "SHA512 test vector failed";
+			}
+			ZT_T_PRINTF("OK" ZT_EOL_S);
+			SHA384(h,SHA512_TV0_INPUT,strlen(SHA512_TV0_INPUT));
+			if (memcmp(h,SHA512_TV0_SHA384_DIGEST,48) != 0) {
+				ZT_T_PRINTF("FAILED (SHA384)" ZT_EOL_S);
+				return "SHA384 test vector failed";
+			}
+			ZT_T_PRINTF("OK" ZT_EOL_S);
+		}
+
+		{
+			uint8_t agree0[32],agree1[32],kh[64],sig[96];
+			ZT_T_PRINTF("[crypto] Testing C25519/Ed25519... ");
+			for(int t=0;t<ZT_NUM_C25519_TEST_VECTORS;++t) {
+				C25519::agree(C25519_TEST_VECTORS[t].priv1,C25519_TEST_VECTORS[t].pub2,agree0);
+				C25519::agree(C25519_TEST_VECTORS[t].priv2,C25519_TEST_VECTORS[t].pub1,agree1);
+				if (memcmp(agree0,agree1,32) != 0) {
+					ZT_T_PRINTF("FAILED (keys do not agree, vector %d)" ZT_EOL_S,t);
+					return "Curve25519 key agreement test failed (a/b does not agree with b/a)";
+				}
+				SHA512(kh,agree0,32);
+				if (memcmp(kh,C25519_TEST_VECTORS[t].agreementSha512,64) != 0) {
+					ZT_T_PRINTF("FAILED (hash of agreement does not match test vector %d)" ZT_EOL_S,t);
+					return "Curve25519 key agreement test failed (does not match expected value)";
+				}
+				C25519::sign(C25519_TEST_VECTORS[t].priv1,C25519_TEST_VECTORS[t].pub1,kh,64,sig);
+				if (memcmp(sig,C25519_TEST_VECTORS[t].agreementSignedBy1,96) != 0) {
+					ZT_T_PRINTF("FAILED (signature of agreement by key 1 does not match test vector %d)" ZT_EOL_S,t);
+					return "Curve25519 signature test failed (signature by key 1 does not match expected value)";
+				}
+				C25519::sign(C25519_TEST_VECTORS[t].priv2,C25519_TEST_VECTORS[t].pub2,kh,64,sig);
+				if (memcmp(sig,C25519_TEST_VECTORS[t].agreementSignedBy1,96) != 0) {
+					ZT_T_PRINTF("FAILED (signature of agreement by key 2 does not match test vector %d)" ZT_EOL_S,t);
+					return "Curve25519 signature test failed (signature by key 2 does not match expected value)";
+				}
+			}
+			ZT_T_PRINTF("OK" ZT_EOL_S);
+		}
+
+		{
+			uint8_t key[48];
+			ZT_T_PRINTF("[crypto] Testing ECC384 (NIST P-384)... ");
+			ECC384ECDH(ECC384_TV0_PUBLIC,ECC384_TV0_PRIVATE,key);
+			if (memcmp(key,ECC384_TV0_DH_SELF_AGREE,48) != 0) {
+				ZT_T_PRINTF("FAILED (test vector 0, self-agree)" ZT_EOL_S);
+				return "ECC384 test vector 0 self-agree failed";
+			}
+			if (!ECC384ECDSAVerify(ECC384_TV0_PUBLIC,ECC384_TV0_PUBLIC,ECC384_TV0_SIG)) {
+				ZT_T_PRINTF("FAILED (test vector 0, signature check)" ZT_EOL_S);
+				return "ECC384 test vector 0 signature check failed";
+			}
+			ZT_T_PRINTF("OK" ZT_EOL_S);
+		}
+
+		{
+			uint8_t ks[64];
+			ZT_T_PRINTF("[crypto] Testing Salsa20... ");
+			Salsa20 s20;
+			s20.init(SALSA20_TV0_KEY,SALSA20_TV0_IV);
+			memset(ks,0,sizeof(ks));
+			s20.crypt20(ks,ks,sizeof(ks));
+			if (memcmp(ks,SALSA20_TV0_KS,64) != 0) {
+				ZT_T_PRINTF("FAILED (Salsa20 test vector)" ZT_EOL_S);
+				return "Salsa20 test vector failed";
+			}
+			s20.init(SALSA12_TV0_KEY,SALSA12_TV0_IV);
+			memset(ks,0,sizeof(ks));
+			s20.crypt12(ks,ks,sizeof(ks));
+			if (memcmp(ks,SALSA12_TV0_KS,64) != 0) {
+				ZT_T_PRINTF("FAILED (Salsa12 test vector)" ZT_EOL_S);
+				return "Salsa12 test vector failed";
+			}
+			ZT_T_PRINTF("OK" ZT_EOL_S);
+		}
+
+		{
+			uint8_t tag[16];
+			ZT_T_PRINTF("[crypto] Testing Poly1305... ");
+			poly1305(tag,POLY1305_TV0_INPUT,sizeof(POLY1305_TV0_INPUT),POLY1305_TV0_KEY);
+			if (memcmp(tag,POLY1305_TV0_TAG,16) != 0) {
+				ZT_T_PRINTF("FAILED (test vector 0)");
+				return "poly1305 test vector 0 failed";
+			}
+			poly1305(tag,POLY1305_TV1_INPUT,sizeof(POLY1305_TV1_INPUT),POLY1305_TV1_KEY);
+			if (memcmp(tag,POLY1305_TV1_TAG,16) != 0) {
+				ZT_T_PRINTF("FAILED (test vector 1)");
+				return "poly1305 test vector 1 failed";
+			}
+			ZT_T_PRINTF("OK" ZT_EOL_S);
+		}
+
+		{
+			uint8_t out[16];
+			ZT_T_PRINTF("[crypto] Testing AES (hardware acceleration: %s)... ",AES::accelerated() ? "enabled" : "disabled");
+			AES aes(AES_TEST_VECTOR_0_KEY);
+			aes.encrypt(AES_TEST_VECTOR_0_IN,out);
+			if (memcmp(AES_TEST_VECTOR_0_OUT,out,16) != 0) {
+			}
+			aes.decrypt(out,out);
+			if (memcmp(AES_TEST_VECTOR_0_IN,out,16) != 0) {
+			}
+			ZT_T_PRINTF("OK" ZT_EOL_S);
+		}
+	} catch (std::exception &e) {
+		ZT_T_PRINTF(ZT_EOL_S "[crypto] Unexpected exception: %s" ZT_EOL_S,e.what());
+		return e.what();
+	} catch ( ... ) {
+		ZT_T_PRINTF(ZT_EOL_S "[crypto] Unexpected exception: unknown exception" ZT_EOL_S);
+		return "an unknown exception occurred";
+	}
 	return nullptr;
 }
 

+ 41 - 20
node/Utils.cpp

@@ -259,18 +259,28 @@ void getSecureRandom(void *buf,unsigned int bytes) noexcept
 {
 	static Mutex globalLock;
 	static bool initialized = false;
-	static uint64_t randomState[8];
-	static uint64_t randomBuf[8192];
-	static unsigned int randomPtr = 65536;
+	static uint64_t randomState[16]; // secret state
+	static uint64_t randomBuf[8192]; // next batch of random bytes
+	static unsigned long randomPtr = sizeof(randomBuf); // refresh on first iteration
+
+	// This secure random function gets entropy from the system random source (e.g. /dev/urandom),
+	// CPU random instructions if present, and other sources and uses them to initialize a SHA/AES
+	// based CSPRNG with a large state. System random sources are not used directly to mitigate
+	// against cases where the system random source is broken in some way, which does happen from
+	// time to time.
 
 	Mutex::Lock gl(globalLock);
 
 	for(unsigned int i=0;i<bytes;++i) {
-		if (randomPtr >= 65536) {
+		if (randomPtr >= sizeof(randomBuf)) {
 			randomPtr = 0;
 
 			if (!initialized) {
 				initialized = true;
+
+				// Fill both randomState and randomBuf from system random source. Failure here
+				// is fatal to the running application and indicates a serious system problem.
+				// This is some of the only OS-specific code in the core.
 #ifdef __WINDOWS__
 				HCRYPTPROV cryptProvider = NULL;
 				if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
@@ -292,12 +302,12 @@ void getSecureRandom(void *buf,unsigned int bytes) noexcept
 					fprintf(stderr,"FATAL: Utils::getSecureRandom() unable to open /dev/urandom\n");
 					exit(1);
 				}
-				if ((int)::read(devURandomFd,randomState,sizeof(randomState)) != (int)sizeof(randomState)) {
+				if ((long)::read(devURandomFd,randomState,sizeof(randomState)) != (long)sizeof(randomState)) {
 					::close(devURandomFd);
 					fprintf(stderr,"FATAL: Utils::getSecureRandom() unable to read from /dev/urandom\n");
 					exit(1);
 				}
-				if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
+				if ((long)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (long)sizeof(randomBuf)) {
 					::close(devURandomFd);
 					fprintf(stderr,"FATAL: Utils::getSecureRandom() unable to read from /dev/urandom\n");
 					exit(1);
@@ -305,29 +315,40 @@ void getSecureRandom(void *buf,unsigned int bytes) noexcept
 				close(devURandomFd);
 #endif
 
-				// Mix in additional entropy just in case the standard random source is wonky somehow
+				// Mix in additional entropy from time, the address of 'buf', rdrand if present, etc.
 				randomState[0] ^= (uint64_t)time(nullptr);
 				randomState[1] ^= (uint64_t)((uintptr_t)buf);
+#ifdef __UNIX_LIKE__
+				randomState[2] ^= (uint64_t)getpid();
+				randomState[3] ^= (uint64_t)getppid();
+#endif
 #if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
 				if (CPUID.rdrand) {
 					uint64_t tmp = 0;
-					_rdrand64_step((unsigned long long *)&tmp);
-					randomState[2] ^= tmp;
-					_rdrand64_step((unsigned long long *)&tmp);
-					randomState[3] ^= tmp;
+					for(int i=0;i<16;++i) {
+						_rdrand64_step((unsigned long long *)&tmp);
+						randomState[i] ^= tmp;
+					}
 				}
 #endif
 			}
 
-			++randomState[0];
-			SHA512(randomState,randomState,sizeof(randomState));
-			AES aes(reinterpret_cast<const uint8_t *>(randomState));
+			// Generate a new randomBuf:
+			//
+			// (1) Generate next randomState by perturbing, hashing, and replacing the first 384 bits with the hash.
+			// (2) Initialize AES using the first 256 bits of the new randomState as its key.
+			// (3) Initialize a 128-bit counter field using the following 128 bits of randomState.
+			// (4) Encrypt randomBuf with AES-CTR (machine-endian counter since spec conformance doesn't matter).
+
+			++randomState[15];
+			SHA384(randomState,randomState,sizeof(randomState));
+			AES aes(randomState);
 			uint64_t ctr[2],tmp[2];
-			ctr[0] = randomState[6];
-			ctr[1] = randomState[7];
+			ctr[0] = randomState[4];
+			ctr[1] = randomState[5]; // AES key + CTR/nonce = part replaced each time by SHA384
 			for(int k=0;k<8192;) {
 				++ctr[0];
-				aes.encrypt(reinterpret_cast<const uint8_t *>(ctr),reinterpret_cast<uint8_t *>(tmp));
+				aes.encrypt(ctr,tmp);
 				randomBuf[k] ^= tmp[0];
 				randomBuf[k+1] ^= tmp[1];
 				k += 2;
@@ -421,7 +442,6 @@ int b32d(const char *encoded,uint8_t *result,int bufSize) noexcept
   return count;
 }
 
-#define ROL64(x,k) (((x) << (k)) | ((x) >> (64 - (k))))
 uint64_t random() noexcept
 {
 	// https://en.wikipedia.org/wiki/Xorshift#xoshiro256**
@@ -434,14 +454,15 @@ uint64_t random() noexcept
 	uint64_t s1 = s_s1;
 	uint64_t s2 = s_s2;
 	uint64_t s3 = s_s3;
-	const uint64_t result = ROL64(s1 * 5,7U) * 9;
+	const uint64_t s1x5 = s1 * 5;
+	const uint64_t result = ((s1x5 << 7U)|(s1x5 >> 57U)) * 9;
 	const uint64_t t = s1 << 17U;
 	s2 ^= s0;
 	s3 ^= s1;
 	s1 ^= s2;
 	s0 ^= s3;
 	s2 ^= t;
-	s3 = ROL64(s3,45U);
+	s3 = ((s3 << 45U)|(s3 >> 19U));
 	s_s0 = s0;
 	s_s1 = s1;
 	s_s2 = s2;