Description: Fix numerous issues on big-endian architectures
Author: Bill Blough <devel@blough.us>
Forwarded: https://github.com/pwsafe/pwsafe/pull/410
Last-Update: 2018-01-31
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
--- a/src/core/BlowFish.cpp
+++ b/src/core/BlowFish.cpp
@@ -20,10 +20,19 @@ union aword
   unsigned char byte [4];
   struct
   {
+#if defined PWS_LITTLE_ENDIAN
     unsigned int byte3:8;
     unsigned int byte2:8;
     unsigned int byte1:8;
     unsigned int byte0:8;
+#elif defined PWS_BIG_ENDIAN
+    unsigned int byte0:8;
+    unsigned int byte1:8;
+    unsigned int byte2:8;
+    unsigned int byte3:8;
+#else
+#error Is the target CPU big or little endian?
+#endif
   } w;
 };
 
@@ -428,16 +437,41 @@ void BlowFish::Encrypt(const unsigned ch
 {
   for (int x = 0; x < 8; x++)
     out[x] = in[x];
+
+#ifdef PWS_BIG_ENDIAN
+  byteswap(out, out + sizeof(int32) - 1);
+  byteswap(out + sizeof(int32), out + 2*sizeof(int32) - 1);
+#endif
+
   Blowfish_encipher(reinterpret_cast<uint32 *>(out),
     reinterpret_cast<uint32 *>(out + sizeof(uint32)));
+
+#ifdef PWS_BIG_ENDIAN
+  byteswap(out, out + sizeof(int32) - 1);
+  byteswap(out + sizeof(int32), out + 2*sizeof(int32) - 1);
+#endif
+
 }
 
 void BlowFish::Decrypt(const unsigned char *in, unsigned char *out) const
 {
   for (int x = 0; x < 8; x++)
     out[x] = in[x];
+
+#ifdef PWS_BIG_ENDIAN
+  byteswap(out, out + sizeof(int32) - 1);
+  byteswap(out + sizeof(int32), out + 2*sizeof(int32) - 1);
+#endif
+
   Blowfish_decipher(reinterpret_cast<uint32 *>(out),
     reinterpret_cast<uint32 *>(out + sizeof(uint32)));
+
+#ifdef PWS_BIG_ENDIAN
+  byteswap(out, out + sizeof(int32) - 1);
+  byteswap(out + sizeof(int32), out + 2*sizeof(int32) - 1);
+#endif
+
+
 }
 
 /*
--- a/src/core/ItemData.cpp
+++ b/src/core/ItemData.cpp
@@ -598,7 +598,7 @@ int32 CItemData::GetXTimeInt(int32 &xint
     CItem::GetField(fiter->second, in, tlen);
     if (tlen != 0) {
       ASSERT(tlen == sizeof(int32));
-      memcpy(&xint, in, sizeof(int32));
+      xint = getInt<int32>(in);
     } else {
       xint = 0;
     }
@@ -658,7 +658,7 @@ int16 CItemData::GetDCA(int16 &iDCA, con
 
     if (tlen != 0) {
       ASSERT(tlen == sizeof(int16));
-      memcpy(&iDCA, in, sizeof(int16));
+      iDCA = getInt<int16>(in);
     } else {
       iDCA = -1;
     }
@@ -686,7 +686,7 @@ int32 CItemData::GetKBShortcut(int32 &iK
 
     if (tlen != 0) {
       ASSERT(tlen == sizeof(int32));
-      memcpy(&iKBShortcut, in, sizeof(int32));
+      iKBShortcut = getInt<int32>(in);
     } else {
       iKBShortcut = 0;
     }
@@ -1926,8 +1926,37 @@ bool CItemData::DeSerializePlainText(con
       ASSERT(0);
       return false;
     }
+
+#ifdef PWS_BIG_ENDIAN
+    unsigned char buf[len] = {0};
+
+    switch(type) {
+      case CTIME:
+      case PMTIME:
+      case ATIME:
+      case XTIME:
+      case RMTIME:
+      case DCA:
+      case SHIFTDCA:
+      case KBSHORTCUT:
+      case XTIME_INT:
+
+        memcpy(buf, &(*iter), len);
+        byteswap(buf, buf + len - 1);
+
+        if (!SetField(type, buf, len))
+          return false;
+        break;
+
+      default:
+        if (!SetField(type, reinterpret_cast<const unsigned char *>(&(*iter)), len))
+          return false;
+	break;
+    }
+#else
     if (!SetField(type, reinterpret_cast<const unsigned char *>(&(*iter)), len))
       return false;
+#endif
     iter += len;
   }
   return false; // END tag not found!
--- a/src/core/PWSfileV3.cpp
+++ b/src/core/PWSfileV3.cpp
@@ -268,7 +268,7 @@ size_t PWSfileV3::WriteCBC(unsigned char
 size_t PWSfileV3::WriteCBC(unsigned char type, const unsigned char *data,
                            size_t length)
 {
-  m_hmac.Update(data, reinterpret_cast<int &>(length));
+  m_hmac.Update(data, static_cast<unsigned long>(length));
   return PWSfile::WriteCBC(type, data, length);
 }
 
--- a/src/core/PWSfileV4.cpp
+++ b/src/core/PWSfileV4.cpp
@@ -244,7 +244,7 @@ size_t PWSfileV4::WriteCBC(unsigned char
 size_t PWSfileV4::WriteCBC(unsigned char type, const unsigned char *data,
                            size_t length)
 {
-  int32 len32 = reinterpret_cast<int &>(length);
+  int32 len32 = static_cast<int>(length);
   unsigned char buf[4];
   putInt32(buf, len32);
 
@@ -291,7 +291,7 @@ size_t PWSfileV4::WriteContentFields(uns
   WriteField(CItemAtt::ATTAK, AK, sizeof(AK));
 
   // Write content length as the "value" of the content field
-  int32 len32 = reinterpret_cast<int &>(len);
+  int32 len32 = static_cast<int>(len);
   unsigned char buf[4];
   putInt32(buf, len32);
   WriteField(CItemAtt::CONTENT, buf, sizeof(buf));
@@ -337,7 +337,7 @@ size_t PWSfileV4::ReadCBC(unsigned char
   size_t numRead = PWSfile::ReadCBC(type, data, length);
 
   if (numRead > 0) {
-    int32 len32 = reinterpret_cast<int &>(length);
+    int32 len32 = static_cast<int>(length);
     unsigned char buf[4];
     putInt32(buf, len32);
 
--- a/src/core/Util.cpp
+++ b/src/core/Util.cpp
@@ -175,7 +175,7 @@ size_t _writecbc(FILE *fp, const unsigne
   // a dictionary attack harder
   PWSrand::GetInstance()->GetRandomData(curblock, BS);
   // block length overwrites 4 bytes of the above randomness.
-  putInt32(curblock, reinterpret_cast<int32 &>(length));
+  putInt32(curblock, static_cast<int32>(length));
 
   // following new for format 2.0 - lengthblock bytes 4-7 were unused before.
   curblock[sizeof(int32)] = type;
--- a/src/core/Util.h
+++ b/src/core/Util.h
@@ -102,7 +102,9 @@ inline int32 getInt32(const unsigned cha
 #if defined(_DEBUG)
   // Following code works for big or little endian architectures but we'll warn anyway
   // if CPU is really little endian
-  if ( *reinterpret_cast<const int32 *>(buf) == (buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)) )
+  if (
+      *reinterpret_cast<const int32 *>(buf) != 0 &&
+      *reinterpret_cast<const int32 *>(buf) == (buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)) )
   {
     pws_os::Trace0(_T("Warning: PWS_BIG_ENDIAN defined but architecture is little endian\n"));
   }
@@ -158,7 +160,9 @@ inline void putInt32(unsigned char buf[4
 #if defined(_DEBUG)
   // Above code works for big or little endian architectures but we'll warn anyway
   // if CPU is really little endian
-  if ( *(int32*) buf == (buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)) )
+  if (
+      *reinterpret_cast<const int32 *>(buf) != 0 &&
+      *(int32*) buf == (buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)) )
   {
     pws_os::Trace0(_T("Warning: PWS_BIG_ENDIAN defined but architecture is little endian\n"));
   }
@@ -231,6 +235,18 @@ void inline putInt(unsigned char *buf, c
 bool operator==(const std::string& str1, const stringT& str2);
 inline bool operator==(const stringT& str1, const std::string &str2) { return str2 == str1; }
 
+inline void byteswap(unsigned char * begin, unsigned char * end) {
+    unsigned char *a = begin;
+    unsigned char *b = end;
+    ASSERT(a < b);
+    unsigned char tmp;
+    while (a < b) {
+        tmp = *a;
+        *a++ = *b;
+        *b-- = tmp;
+    }
+}
+
 namespace PWSUtil {
   // namespace of common utility functions
 
--- a/src/os/unix/pws_time.cpp
+++ b/src/os/unix/pws_time.cpp
@@ -15,7 +15,19 @@
 
 int localtime64_r(const __time64_t *timep, struct tm *result)
 {
-  return localtime_r(reinterpret_cast<const time_t *>(timep), result) != 0;
+  const time_t *tp = reinterpret_cast<const time_t *>(timep);
+
+#ifdef PWS_BIG_ENDIAN
+  //handle downsizing cast on 32-bit big-endian systems
+  if (sizeof(time_t) < sizeof(__time64_t)) {
+    //assume alignment
+    assert(sizeof(__time64_t) % sizeof(time_t) == 0);
+    size_t offset = (sizeof(__time64_t)/sizeof(time_t)) - 1;
+    tp = reinterpret_cast<const time_t *>(((time_t*)timep) + offset);
+  }
+#endif
+
+  return localtime_r(tp, result) != 0;
 }
 
 int pws_os::asctime(TCHAR *s, size_t, tm const *t)
