Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
ripemd.c
00001 /* ripemd.c 00002 * 00003 * Copyright (C) 2006-2013 wolfSSL Inc. 00004 * 00005 * This file is part of CyaSSL. 00006 * 00007 * CyaSSL is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation; either version 2 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * CyaSSL is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with this program; if not, write to the Free Software 00019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 00020 */ 00021 00022 00023 #ifdef HAVE_CONFIG_H 00024 #include <config.h> 00025 #endif 00026 00027 #include <cyassl/ctaocrypt/settings.h> 00028 00029 #ifdef CYASSL_RIPEMD 00030 00031 #include <cyassl/ctaocrypt/ripemd.h> 00032 #ifdef NO_INLINE 00033 #include <cyassl/ctaocrypt/misc.h> 00034 #else 00035 #include <ctaocrypt/src/misc.c> 00036 #endif 00037 00038 00039 #ifndef min 00040 00041 static INLINE word32 min(word32 a, word32 b) 00042 { 00043 return a > b ? b : a; 00044 } 00045 00046 #endif /* min */ 00047 00048 00049 void InitRipeMd(RipeMd* ripemd) 00050 { 00051 ripemd->digest[0] = 0x67452301L; 00052 ripemd->digest[1] = 0xEFCDAB89L; 00053 ripemd->digest[2] = 0x98BADCFEL; 00054 ripemd->digest[3] = 0x10325476L; 00055 ripemd->digest[4] = 0xC3D2E1F0L; 00056 00057 ripemd->buffLen = 0; 00058 ripemd->loLen = 0; 00059 ripemd->hiLen = 0; 00060 } 00061 00062 00063 /* for all */ 00064 #define F(x, y, z) (x ^ y ^ z) 00065 #define G(x, y, z) (z ^ (x & (y^z))) 00066 #define H(x, y, z) (z ^ (x | ~y)) 00067 #define I(x, y, z) (y ^ (z & (x^y))) 00068 #define J(x, y, z) (x ^ (y | ~z)) 00069 00070 #define k0 0 00071 #define k1 0x5a827999 00072 #define k2 0x6ed9eba1 00073 #define k3 0x8f1bbcdc 00074 #define k4 0xa953fd4e 00075 #define k5 0x50a28be6 00076 #define k6 0x5c4dd124 00077 #define k7 0x6d703ef3 00078 #define k8 0x7a6d76e9 00079 #define k9 0 00080 00081 /* for 160 and 320 */ 00082 #define Subround(f, a, b, c, d, e, x, s, k) \ 00083 a += f(b, c, d) + x + k;\ 00084 a = rotlFixed((word32)a, s) + e;\ 00085 c = rotlFixed((word32)c, 10U) 00086 00087 static void Transform(RipeMd* ripemd) 00088 { 00089 word32 a1, b1, c1, d1, e1, a2, b2, c2, d2, e2; 00090 a1 = a2 = ripemd->digest[0]; 00091 b1 = b2 = ripemd->digest[1]; 00092 c1 = c2 = ripemd->digest[2]; 00093 d1 = d2 = ripemd->digest[3]; 00094 e1 = e2 = ripemd->digest[4]; 00095 00096 Subround(F, a1, b1, c1, d1, e1, ripemd->buffer[ 0], 11, k0); 00097 Subround(F, e1, a1, b1, c1, d1, ripemd->buffer[ 1], 14, k0); 00098 Subround(F, d1, e1, a1, b1, c1, ripemd->buffer[ 2], 15, k0); 00099 Subround(F, c1, d1, e1, a1, b1, ripemd->buffer[ 3], 12, k0); 00100 Subround(F, b1, c1, d1, e1, a1, ripemd->buffer[ 4], 5, k0); 00101 Subround(F, a1, b1, c1, d1, e1, ripemd->buffer[ 5], 8, k0); 00102 Subround(F, e1, a1, b1, c1, d1, ripemd->buffer[ 6], 7, k0); 00103 Subround(F, d1, e1, a1, b1, c1, ripemd->buffer[ 7], 9, k0); 00104 Subround(F, c1, d1, e1, a1, b1, ripemd->buffer[ 8], 11, k0); 00105 Subround(F, b1, c1, d1, e1, a1, ripemd->buffer[ 9], 13, k0); 00106 Subround(F, a1, b1, c1, d1, e1, ripemd->buffer[10], 14, k0); 00107 Subround(F, e1, a1, b1, c1, d1, ripemd->buffer[11], 15, k0); 00108 Subround(F, d1, e1, a1, b1, c1, ripemd->buffer[12], 6, k0); 00109 Subround(F, c1, d1, e1, a1, b1, ripemd->buffer[13], 7, k0); 00110 Subround(F, b1, c1, d1, e1, a1, ripemd->buffer[14], 9, k0); 00111 Subround(F, a1, b1, c1, d1, e1, ripemd->buffer[15], 8, k0); 00112 00113 Subround(G, e1, a1, b1, c1, d1, ripemd->buffer[ 7], 7, k1); 00114 Subround(G, d1, e1, a1, b1, c1, ripemd->buffer[ 4], 6, k1); 00115 Subround(G, c1, d1, e1, a1, b1, ripemd->buffer[13], 8, k1); 00116 Subround(G, b1, c1, d1, e1, a1, ripemd->buffer[ 1], 13, k1); 00117 Subround(G, a1, b1, c1, d1, e1, ripemd->buffer[10], 11, k1); 00118 Subround(G, e1, a1, b1, c1, d1, ripemd->buffer[ 6], 9, k1); 00119 Subround(G, d1, e1, a1, b1, c1, ripemd->buffer[15], 7, k1); 00120 Subround(G, c1, d1, e1, a1, b1, ripemd->buffer[ 3], 15, k1); 00121 Subround(G, b1, c1, d1, e1, a1, ripemd->buffer[12], 7, k1); 00122 Subround(G, a1, b1, c1, d1, e1, ripemd->buffer[ 0], 12, k1); 00123 Subround(G, e1, a1, b1, c1, d1, ripemd->buffer[ 9], 15, k1); 00124 Subround(G, d1, e1, a1, b1, c1, ripemd->buffer[ 5], 9, k1); 00125 Subround(G, c1, d1, e1, a1, b1, ripemd->buffer[ 2], 11, k1); 00126 Subround(G, b1, c1, d1, e1, a1, ripemd->buffer[14], 7, k1); 00127 Subround(G, a1, b1, c1, d1, e1, ripemd->buffer[11], 13, k1); 00128 Subround(G, e1, a1, b1, c1, d1, ripemd->buffer[ 8], 12, k1); 00129 00130 Subround(H, d1, e1, a1, b1, c1, ripemd->buffer[ 3], 11, k2); 00131 Subround(H, c1, d1, e1, a1, b1, ripemd->buffer[10], 13, k2); 00132 Subround(H, b1, c1, d1, e1, a1, ripemd->buffer[14], 6, k2); 00133 Subround(H, a1, b1, c1, d1, e1, ripemd->buffer[ 4], 7, k2); 00134 Subround(H, e1, a1, b1, c1, d1, ripemd->buffer[ 9], 14, k2); 00135 Subround(H, d1, e1, a1, b1, c1, ripemd->buffer[15], 9, k2); 00136 Subround(H, c1, d1, e1, a1, b1, ripemd->buffer[ 8], 13, k2); 00137 Subround(H, b1, c1, d1, e1, a1, ripemd->buffer[ 1], 15, k2); 00138 Subround(H, a1, b1, c1, d1, e1, ripemd->buffer[ 2], 14, k2); 00139 Subround(H, e1, a1, b1, c1, d1, ripemd->buffer[ 7], 8, k2); 00140 Subround(H, d1, e1, a1, b1, c1, ripemd->buffer[ 0], 13, k2); 00141 Subround(H, c1, d1, e1, a1, b1, ripemd->buffer[ 6], 6, k2); 00142 Subround(H, b1, c1, d1, e1, a1, ripemd->buffer[13], 5, k2); 00143 Subround(H, a1, b1, c1, d1, e1, ripemd->buffer[11], 12, k2); 00144 Subround(H, e1, a1, b1, c1, d1, ripemd->buffer[ 5], 7, k2); 00145 Subround(H, d1, e1, a1, b1, c1, ripemd->buffer[12], 5, k2); 00146 00147 Subround(I, c1, d1, e1, a1, b1, ripemd->buffer[ 1], 11, k3); 00148 Subround(I, b1, c1, d1, e1, a1, ripemd->buffer[ 9], 12, k3); 00149 Subround(I, a1, b1, c1, d1, e1, ripemd->buffer[11], 14, k3); 00150 Subround(I, e1, a1, b1, c1, d1, ripemd->buffer[10], 15, k3); 00151 Subround(I, d1, e1, a1, b1, c1, ripemd->buffer[ 0], 14, k3); 00152 Subround(I, c1, d1, e1, a1, b1, ripemd->buffer[ 8], 15, k3); 00153 Subround(I, b1, c1, d1, e1, a1, ripemd->buffer[12], 9, k3); 00154 Subround(I, a1, b1, c1, d1, e1, ripemd->buffer[ 4], 8, k3); 00155 Subround(I, e1, a1, b1, c1, d1, ripemd->buffer[13], 9, k3); 00156 Subround(I, d1, e1, a1, b1, c1, ripemd->buffer[ 3], 14, k3); 00157 Subround(I, c1, d1, e1, a1, b1, ripemd->buffer[ 7], 5, k3); 00158 Subround(I, b1, c1, d1, e1, a1, ripemd->buffer[15], 6, k3); 00159 Subround(I, a1, b1, c1, d1, e1, ripemd->buffer[14], 8, k3); 00160 Subround(I, e1, a1, b1, c1, d1, ripemd->buffer[ 5], 6, k3); 00161 Subround(I, d1, e1, a1, b1, c1, ripemd->buffer[ 6], 5, k3); 00162 Subround(I, c1, d1, e1, a1, b1, ripemd->buffer[ 2], 12, k3); 00163 00164 Subround(J, b1, c1, d1, e1, a1, ripemd->buffer[ 4], 9, k4); 00165 Subround(J, a1, b1, c1, d1, e1, ripemd->buffer[ 0], 15, k4); 00166 Subround(J, e1, a1, b1, c1, d1, ripemd->buffer[ 5], 5, k4); 00167 Subround(J, d1, e1, a1, b1, c1, ripemd->buffer[ 9], 11, k4); 00168 Subround(J, c1, d1, e1, a1, b1, ripemd->buffer[ 7], 6, k4); 00169 Subround(J, b1, c1, d1, e1, a1, ripemd->buffer[12], 8, k4); 00170 Subround(J, a1, b1, c1, d1, e1, ripemd->buffer[ 2], 13, k4); 00171 Subround(J, e1, a1, b1, c1, d1, ripemd->buffer[10], 12, k4); 00172 Subround(J, d1, e1, a1, b1, c1, ripemd->buffer[14], 5, k4); 00173 Subround(J, c1, d1, e1, a1, b1, ripemd->buffer[ 1], 12, k4); 00174 Subround(J, b1, c1, d1, e1, a1, ripemd->buffer[ 3], 13, k4); 00175 Subround(J, a1, b1, c1, d1, e1, ripemd->buffer[ 8], 14, k4); 00176 Subround(J, e1, a1, b1, c1, d1, ripemd->buffer[11], 11, k4); 00177 Subround(J, d1, e1, a1, b1, c1, ripemd->buffer[ 6], 8, k4); 00178 Subround(J, c1, d1, e1, a1, b1, ripemd->buffer[15], 5, k4); 00179 Subround(J, b1, c1, d1, e1, a1, ripemd->buffer[13], 6, k4); 00180 00181 Subround(J, a2, b2, c2, d2, e2, ripemd->buffer[ 5], 8, k5); 00182 Subround(J, e2, a2, b2, c2, d2, ripemd->buffer[14], 9, k5); 00183 Subround(J, d2, e2, a2, b2, c2, ripemd->buffer[ 7], 9, k5); 00184 Subround(J, c2, d2, e2, a2, b2, ripemd->buffer[ 0], 11, k5); 00185 Subround(J, b2, c2, d2, e2, a2, ripemd->buffer[ 9], 13, k5); 00186 Subround(J, a2, b2, c2, d2, e2, ripemd->buffer[ 2], 15, k5); 00187 Subround(J, e2, a2, b2, c2, d2, ripemd->buffer[11], 15, k5); 00188 Subround(J, d2, e2, a2, b2, c2, ripemd->buffer[ 4], 5, k5); 00189 Subround(J, c2, d2, e2, a2, b2, ripemd->buffer[13], 7, k5); 00190 Subround(J, b2, c2, d2, e2, a2, ripemd->buffer[ 6], 7, k5); 00191 Subround(J, a2, b2, c2, d2, e2, ripemd->buffer[15], 8, k5); 00192 Subround(J, e2, a2, b2, c2, d2, ripemd->buffer[ 8], 11, k5); 00193 Subround(J, d2, e2, a2, b2, c2, ripemd->buffer[ 1], 14, k5); 00194 Subround(J, c2, d2, e2, a2, b2, ripemd->buffer[10], 14, k5); 00195 Subround(J, b2, c2, d2, e2, a2, ripemd->buffer[ 3], 12, k5); 00196 Subround(J, a2, b2, c2, d2, e2, ripemd->buffer[12], 6, k5); 00197 00198 Subround(I, e2, a2, b2, c2, d2, ripemd->buffer[ 6], 9, k6); 00199 Subround(I, d2, e2, a2, b2, c2, ripemd->buffer[11], 13, k6); 00200 Subround(I, c2, d2, e2, a2, b2, ripemd->buffer[ 3], 15, k6); 00201 Subround(I, b2, c2, d2, e2, a2, ripemd->buffer[ 7], 7, k6); 00202 Subround(I, a2, b2, c2, d2, e2, ripemd->buffer[ 0], 12, k6); 00203 Subround(I, e2, a2, b2, c2, d2, ripemd->buffer[13], 8, k6); 00204 Subround(I, d2, e2, a2, b2, c2, ripemd->buffer[ 5], 9, k6); 00205 Subround(I, c2, d2, e2, a2, b2, ripemd->buffer[10], 11, k6); 00206 Subround(I, b2, c2, d2, e2, a2, ripemd->buffer[14], 7, k6); 00207 Subround(I, a2, b2, c2, d2, e2, ripemd->buffer[15], 7, k6); 00208 Subround(I, e2, a2, b2, c2, d2, ripemd->buffer[ 8], 12, k6); 00209 Subround(I, d2, e2, a2, b2, c2, ripemd->buffer[12], 7, k6); 00210 Subround(I, c2, d2, e2, a2, b2, ripemd->buffer[ 4], 6, k6); 00211 Subround(I, b2, c2, d2, e2, a2, ripemd->buffer[ 9], 15, k6); 00212 Subround(I, a2, b2, c2, d2, e2, ripemd->buffer[ 1], 13, k6); 00213 Subround(I, e2, a2, b2, c2, d2, ripemd->buffer[ 2], 11, k6); 00214 00215 Subround(H, d2, e2, a2, b2, c2, ripemd->buffer[15], 9, k7); 00216 Subround(H, c2, d2, e2, a2, b2, ripemd->buffer[ 5], 7, k7); 00217 Subround(H, b2, c2, d2, e2, a2, ripemd->buffer[ 1], 15, k7); 00218 Subround(H, a2, b2, c2, d2, e2, ripemd->buffer[ 3], 11, k7); 00219 Subround(H, e2, a2, b2, c2, d2, ripemd->buffer[ 7], 8, k7); 00220 Subround(H, d2, e2, a2, b2, c2, ripemd->buffer[14], 6, k7); 00221 Subround(H, c2, d2, e2, a2, b2, ripemd->buffer[ 6], 6, k7); 00222 Subround(H, b2, c2, d2, e2, a2, ripemd->buffer[ 9], 14, k7); 00223 Subround(H, a2, b2, c2, d2, e2, ripemd->buffer[11], 12, k7); 00224 Subround(H, e2, a2, b2, c2, d2, ripemd->buffer[ 8], 13, k7); 00225 Subround(H, d2, e2, a2, b2, c2, ripemd->buffer[12], 5, k7); 00226 Subround(H, c2, d2, e2, a2, b2, ripemd->buffer[ 2], 14, k7); 00227 Subround(H, b2, c2, d2, e2, a2, ripemd->buffer[10], 13, k7); 00228 Subround(H, a2, b2, c2, d2, e2, ripemd->buffer[ 0], 13, k7); 00229 Subround(H, e2, a2, b2, c2, d2, ripemd->buffer[ 4], 7, k7); 00230 Subround(H, d2, e2, a2, b2, c2, ripemd->buffer[13], 5, k7); 00231 00232 Subround(G, c2, d2, e2, a2, b2, ripemd->buffer[ 8], 15, k8); 00233 Subround(G, b2, c2, d2, e2, a2, ripemd->buffer[ 6], 5, k8); 00234 Subround(G, a2, b2, c2, d2, e2, ripemd->buffer[ 4], 8, k8); 00235 Subround(G, e2, a2, b2, c2, d2, ripemd->buffer[ 1], 11, k8); 00236 Subround(G, d2, e2, a2, b2, c2, ripemd->buffer[ 3], 14, k8); 00237 Subround(G, c2, d2, e2, a2, b2, ripemd->buffer[11], 14, k8); 00238 Subround(G, b2, c2, d2, e2, a2, ripemd->buffer[15], 6, k8); 00239 Subround(G, a2, b2, c2, d2, e2, ripemd->buffer[ 0], 14, k8); 00240 Subround(G, e2, a2, b2, c2, d2, ripemd->buffer[ 5], 6, k8); 00241 Subround(G, d2, e2, a2, b2, c2, ripemd->buffer[12], 9, k8); 00242 Subround(G, c2, d2, e2, a2, b2, ripemd->buffer[ 2], 12, k8); 00243 Subround(G, b2, c2, d2, e2, a2, ripemd->buffer[13], 9, k8); 00244 Subround(G, a2, b2, c2, d2, e2, ripemd->buffer[ 9], 12, k8); 00245 Subround(G, e2, a2, b2, c2, d2, ripemd->buffer[ 7], 5, k8); 00246 Subround(G, d2, e2, a2, b2, c2, ripemd->buffer[10], 15, k8); 00247 Subround(G, c2, d2, e2, a2, b2, ripemd->buffer[14], 8, k8); 00248 00249 Subround(F, b2, c2, d2, e2, a2, ripemd->buffer[12], 8, k9); 00250 Subround(F, a2, b2, c2, d2, e2, ripemd->buffer[15], 5, k9); 00251 Subround(F, e2, a2, b2, c2, d2, ripemd->buffer[10], 12, k9); 00252 Subround(F, d2, e2, a2, b2, c2, ripemd->buffer[ 4], 9, k9); 00253 Subround(F, c2, d2, e2, a2, b2, ripemd->buffer[ 1], 12, k9); 00254 Subround(F, b2, c2, d2, e2, a2, ripemd->buffer[ 5], 5, k9); 00255 Subround(F, a2, b2, c2, d2, e2, ripemd->buffer[ 8], 14, k9); 00256 Subround(F, e2, a2, b2, c2, d2, ripemd->buffer[ 7], 6, k9); 00257 Subround(F, d2, e2, a2, b2, c2, ripemd->buffer[ 6], 8, k9); 00258 Subround(F, c2, d2, e2, a2, b2, ripemd->buffer[ 2], 13, k9); 00259 Subround(F, b2, c2, d2, e2, a2, ripemd->buffer[13], 6, k9); 00260 Subround(F, a2, b2, c2, d2, e2, ripemd->buffer[14], 5, k9); 00261 Subround(F, e2, a2, b2, c2, d2, ripemd->buffer[ 0], 15, k9); 00262 Subround(F, d2, e2, a2, b2, c2, ripemd->buffer[ 3], 13, k9); 00263 Subround(F, c2, d2, e2, a2, b2, ripemd->buffer[ 9], 11, k9); 00264 Subround(F, b2, c2, d2, e2, a2, ripemd->buffer[11], 11, k9); 00265 00266 c1 = ripemd->digest[1] + c1 + d2; 00267 ripemd->digest[1] = ripemd->digest[2] + d1 + e2; 00268 ripemd->digest[2] = ripemd->digest[3] + e1 + a2; 00269 ripemd->digest[3] = ripemd->digest[4] + a1 + b2; 00270 ripemd->digest[4] = ripemd->digest[0] + b1 + c2; 00271 ripemd->digest[0] = c1; 00272 } 00273 00274 00275 static INLINE void AddLength(RipeMd* ripemd, word32 len) 00276 { 00277 word32 tmp = ripemd->loLen; 00278 if ( (ripemd->loLen += len) < tmp) 00279 ripemd->hiLen++; /* carry low to high */ 00280 } 00281 00282 00283 void RipeMdUpdate(RipeMd* ripemd, const byte* data, word32 len) 00284 { 00285 /* do block size increments */ 00286 byte* local = (byte*)ripemd->buffer; 00287 00288 while (len) { 00289 word32 add = min(len, RIPEMD_BLOCK_SIZE - ripemd->buffLen); 00290 XMEMCPY(&local[ripemd->buffLen], data, add); 00291 00292 ripemd->buffLen += add; 00293 data += add; 00294 len -= add; 00295 00296 if (ripemd->buffLen == RIPEMD_BLOCK_SIZE) { 00297 #ifdef BIG_ENDIAN_ORDER 00298 ByteReverseWords(ripemd->buffer, ripemd->buffer, 00299 RIPEMD_BLOCK_SIZE); 00300 #endif 00301 Transform(ripemd); 00302 AddLength(ripemd, RIPEMD_BLOCK_SIZE); 00303 ripemd->buffLen = 0; 00304 } 00305 } 00306 } 00307 00308 00309 void RipeMdFinal(RipeMd* ripemd, byte* hash) 00310 { 00311 byte* local = (byte*)ripemd->buffer; 00312 00313 AddLength(ripemd, ripemd->buffLen); /* before adding pads */ 00314 00315 local[ripemd->buffLen++] = 0x80; /* add 1 */ 00316 00317 /* pad with zeros */ 00318 if (ripemd->buffLen > RIPEMD_PAD_SIZE) { 00319 XMEMSET(&local[ripemd->buffLen], 0, RIPEMD_BLOCK_SIZE - ripemd->buffLen); 00320 ripemd->buffLen += RIPEMD_BLOCK_SIZE - ripemd->buffLen; 00321 00322 #ifdef BIG_ENDIAN_ORDER 00323 ByteReverseWords(ripemd->buffer, ripemd->buffer, RIPEMD_BLOCK_SIZE); 00324 #endif 00325 Transform(ripemd); 00326 ripemd->buffLen = 0; 00327 } 00328 XMEMSET(&local[ripemd->buffLen], 0, RIPEMD_PAD_SIZE - ripemd->buffLen); 00329 00330 /* put lengths in bits */ 00331 ripemd->loLen = ripemd->loLen << 3; 00332 ripemd->hiLen = (ripemd->loLen >> (8*sizeof(ripemd->loLen) - 3)) + 00333 (ripemd->hiLen << 3); 00334 00335 /* store lengths */ 00336 #ifdef BIG_ENDIAN_ORDER 00337 ByteReverseWords(ripemd->buffer, ripemd->buffer, RIPEMD_BLOCK_SIZE); 00338 #endif 00339 /* ! length ordering dependent on digest endian type ! */ 00340 XMEMCPY(&local[RIPEMD_PAD_SIZE], &ripemd->loLen, sizeof(word32)); 00341 XMEMCPY(&local[RIPEMD_PAD_SIZE + sizeof(word32)], &ripemd->hiLen, 00342 sizeof(word32)); 00343 00344 Transform(ripemd); 00345 #ifdef BIG_ENDIAN_ORDER 00346 ByteReverseWords(ripemd->digest, ripemd->digest, RIPEMD_DIGEST_SIZE); 00347 #endif 00348 XMEMCPY(hash, ripemd->digest, RIPEMD_DIGEST_SIZE); 00349 00350 InitRipeMd(ripemd); /* reset state */ 00351 } 00352 00353 00354 #endif /* CYASSL_RIPEMD */
Generated on Tue Jul 12 2022 20:12:51 by
