Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(896)

Side by Side Diff: fusl/src/crypt/crypt_md5.c

Issue 1714623002: [fusl] clang-format fusl (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: headers too Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * md5 crypt implementation 2 * md5 crypt implementation
3 * 3 *
4 * original md5 crypt design is from Poul-Henning Kamp 4 * original md5 crypt design is from Poul-Henning Kamp
5 * this implementation was created based on the code in freebsd 5 * this implementation was created based on the code in freebsd
6 * at least 32bit int is assumed, key is limited and $1$ prefix is mandatory, 6 * at least 32bit int is assumed, key is limited and $1$ prefix is mandatory,
7 * on error "*" is returned 7 * on error "*" is returned
8 */ 8 */
9 #include <string.h> 9 #include <string.h>
10 #include <stdint.h> 10 #include <stdint.h>
11 11
12 /* public domain md5 implementation based on rfc1321 and libtomcrypt */ 12 /* public domain md5 implementation based on rfc1321 and libtomcrypt */
13 13
14 struct md5 { 14 struct md5 {
15 » uint64_t len; /* processed message length */ 15 uint64_t len; /* processed message length */
16 » uint32_t h[4]; /* hash state */ 16 uint32_t h[4]; /* hash state */
17 » uint8_t buf[64]; /* message block buffer */ 17 uint8_t buf[64]; /* message block buffer */
18 }; 18 };
19 19
20 static uint32_t rol(uint32_t n, int k) { return (n << k) | (n >> (32-k)); } 20 static uint32_t rol(uint32_t n, int k) {
21 #define F(x,y,z) (z ^ (x & (y ^ z))) 21 return (n << k) | (n >> (32 - k));
22 #define G(x,y,z) (y ^ (z & (y ^ x))) 22 }
23 #define H(x,y,z) (x ^ y ^ z) 23 #define F(x, y, z) (z ^ (x & (y ^ z)))
24 #define I(x,y,z) (y ^ (x | ~z)) 24 #define G(x, y, z) (y ^ (z & (y ^ x)))
25 #define FF(a,b,c,d,w,s,t) a += F(b,c,d) + w + t; a = rol(a,s) + b 25 #define H(x, y, z) (x ^ y ^ z)
26 #define GG(a,b,c,d,w,s,t) a += G(b,c,d) + w + t; a = rol(a,s) + b 26 #define I(x, y, z) (y ^ (x | ~z))
27 #define HH(a,b,c,d,w,s,t) a += H(b,c,d) + w + t; a = rol(a,s) + b 27 #define FF(a, b, c, d, w, s, t) \
28 #define II(a,b,c,d,w,s,t) a += I(b,c,d) + w + t; a = rol(a,s) + b 28 a += F(b, c, d) + w + t; \
29 a = rol(a, s) + b
30 #define GG(a, b, c, d, w, s, t) \
31 a += G(b, c, d) + w + t; \
32 a = rol(a, s) + b
33 #define HH(a, b, c, d, w, s, t) \
34 a += H(b, c, d) + w + t; \
35 a = rol(a, s) + b
36 #define II(a, b, c, d, w, s, t) \
37 a += I(b, c, d) + w + t; \
38 a = rol(a, s) + b
29 39
30 static const uint32_t tab[64] = { 40 static const uint32_t tab[64] = {
31 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa83046 13, 0xfd469501, 41 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a,
32 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa67943 8e, 0x49b40821, 42 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
33 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e6 81, 0xe7d3fbc8, 43 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340,
34 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02 d9, 0x8d2a4c8a, 44 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
35 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b 60, 0xbebfbc70, 45 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
36 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27c f8, 0xc4ac5665, 46 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
37 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff4 7d, 0x85845dd1, 47 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa,
38 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2 bb, 0xeb86d391 48 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
39 }; 49 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
50 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
51 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
40 52
41 static void processblock(struct md5 *s, const uint8_t *buf) 53 static void processblock(struct md5* s, const uint8_t* buf) {
42 { 54 uint32_t i, W[16], a, b, c, d;
43 » uint32_t i, W[16], a, b, c, d;
44 55
45 » for (i = 0; i < 16; i++) { 56 for (i = 0; i < 16; i++) {
46 » » W[i] = buf[4*i]; 57 W[i] = buf[4 * i];
47 » » W[i] |= (uint32_t)buf[4*i+1]<<8; 58 W[i] |= (uint32_t)buf[4 * i + 1] << 8;
48 » » W[i] |= (uint32_t)buf[4*i+2]<<16; 59 W[i] |= (uint32_t)buf[4 * i + 2] << 16;
49 » » W[i] |= (uint32_t)buf[4*i+3]<<24; 60 W[i] |= (uint32_t)buf[4 * i + 3] << 24;
50 » } 61 }
51 62
52 » a = s->h[0]; 63 a = s->h[0];
53 » b = s->h[1]; 64 b = s->h[1];
54 » c = s->h[2]; 65 c = s->h[2];
55 » d = s->h[3]; 66 d = s->h[3];
56 67
57 » i = 0; 68 i = 0;
58 » while (i < 16) { 69 while (i < 16) {
59 » » FF(a,b,c,d, W[i], 7, tab[i]); i++; 70 FF(a, b, c, d, W[i], 7, tab[i]);
60 » » FF(d,a,b,c, W[i], 12, tab[i]); i++; 71 i++;
61 » » FF(c,d,a,b, W[i], 17, tab[i]); i++; 72 FF(d, a, b, c, W[i], 12, tab[i]);
62 » » FF(b,c,d,a, W[i], 22, tab[i]); i++; 73 i++;
63 » } 74 FF(c, d, a, b, W[i], 17, tab[i]);
64 » while (i < 32) { 75 i++;
65 » » GG(a,b,c,d, W[(5*i+1)%16], 5, tab[i]); i++; 76 FF(b, c, d, a, W[i], 22, tab[i]);
66 » » GG(d,a,b,c, W[(5*i+1)%16], 9, tab[i]); i++; 77 i++;
67 » » GG(c,d,a,b, W[(5*i+1)%16], 14, tab[i]); i++; 78 }
68 » » GG(b,c,d,a, W[(5*i+1)%16], 20, tab[i]); i++; 79 while (i < 32) {
69 » } 80 GG(a, b, c, d, W[(5 * i + 1) % 16], 5, tab[i]);
70 » while (i < 48) { 81 i++;
71 » » HH(a,b,c,d, W[(3*i+5)%16], 4, tab[i]); i++; 82 GG(d, a, b, c, W[(5 * i + 1) % 16], 9, tab[i]);
72 » » HH(d,a,b,c, W[(3*i+5)%16], 11, tab[i]); i++; 83 i++;
73 » » HH(c,d,a,b, W[(3*i+5)%16], 16, tab[i]); i++; 84 GG(c, d, a, b, W[(5 * i + 1) % 16], 14, tab[i]);
74 » » HH(b,c,d,a, W[(3*i+5)%16], 23, tab[i]); i++; 85 i++;
75 » } 86 GG(b, c, d, a, W[(5 * i + 1) % 16], 20, tab[i]);
76 » while (i < 64) { 87 i++;
77 » » II(a,b,c,d, W[7*i%16], 6, tab[i]); i++; 88 }
78 » » II(d,a,b,c, W[7*i%16], 10, tab[i]); i++; 89 while (i < 48) {
79 » » II(c,d,a,b, W[7*i%16], 15, tab[i]); i++; 90 HH(a, b, c, d, W[(3 * i + 5) % 16], 4, tab[i]);
80 » » II(b,c,d,a, W[7*i%16], 21, tab[i]); i++; 91 i++;
81 » } 92 HH(d, a, b, c, W[(3 * i + 5) % 16], 11, tab[i]);
93 i++;
94 HH(c, d, a, b, W[(3 * i + 5) % 16], 16, tab[i]);
95 i++;
96 HH(b, c, d, a, W[(3 * i + 5) % 16], 23, tab[i]);
97 i++;
98 }
99 while (i < 64) {
100 II(a, b, c, d, W[7 * i % 16], 6, tab[i]);
101 i++;
102 II(d, a, b, c, W[7 * i % 16], 10, tab[i]);
103 i++;
104 II(c, d, a, b, W[7 * i % 16], 15, tab[i]);
105 i++;
106 II(b, c, d, a, W[7 * i % 16], 21, tab[i]);
107 i++;
108 }
82 109
83 » s->h[0] += a; 110 s->h[0] += a;
84 » s->h[1] += b; 111 s->h[1] += b;
85 » s->h[2] += c; 112 s->h[2] += c;
86 » s->h[3] += d; 113 s->h[3] += d;
87 } 114 }
88 115
89 static void pad(struct md5 *s) 116 static void pad(struct md5* s) {
90 { 117 unsigned r = s->len % 64;
91 » unsigned r = s->len % 64;
92 118
93 » s->buf[r++] = 0x80; 119 s->buf[r++] = 0x80;
94 » if (r > 56) { 120 if (r > 56) {
95 » » memset(s->buf + r, 0, 64 - r); 121 memset(s->buf + r, 0, 64 - r);
96 » » r = 0; 122 r = 0;
97 » » processblock(s, s->buf); 123 processblock(s, s->buf);
98 » } 124 }
99 » memset(s->buf + r, 0, 56 - r); 125 memset(s->buf + r, 0, 56 - r);
100 » s->len *= 8; 126 s->len *= 8;
101 » s->buf[56] = s->len; 127 s->buf[56] = s->len;
102 » s->buf[57] = s->len >> 8; 128 s->buf[57] = s->len >> 8;
103 » s->buf[58] = s->len >> 16; 129 s->buf[58] = s->len >> 16;
104 » s->buf[59] = s->len >> 24; 130 s->buf[59] = s->len >> 24;
105 » s->buf[60] = s->len >> 32; 131 s->buf[60] = s->len >> 32;
106 » s->buf[61] = s->len >> 40; 132 s->buf[61] = s->len >> 40;
107 » s->buf[62] = s->len >> 48; 133 s->buf[62] = s->len >> 48;
108 » s->buf[63] = s->len >> 56; 134 s->buf[63] = s->len >> 56;
109 » processblock(s, s->buf); 135 processblock(s, s->buf);
110 } 136 }
111 137
112 static void md5_init(struct md5 *s) 138 static void md5_init(struct md5* s) {
113 { 139 s->len = 0;
114 » s->len = 0; 140 s->h[0] = 0x67452301;
115 » s->h[0] = 0x67452301; 141 s->h[1] = 0xefcdab89;
116 » s->h[1] = 0xefcdab89; 142 s->h[2] = 0x98badcfe;
117 » s->h[2] = 0x98badcfe; 143 s->h[3] = 0x10325476;
118 » s->h[3] = 0x10325476;
119 } 144 }
120 145
121 static void md5_sum(struct md5 *s, uint8_t *md) 146 static void md5_sum(struct md5* s, uint8_t* md) {
122 { 147 int i;
123 » int i;
124 148
125 » pad(s); 149 pad(s);
126 » for (i = 0; i < 4; i++) { 150 for (i = 0; i < 4; i++) {
127 » » md[4*i] = s->h[i]; 151 md[4 * i] = s->h[i];
128 » » md[4*i+1] = s->h[i] >> 8; 152 md[4 * i + 1] = s->h[i] >> 8;
129 » » md[4*i+2] = s->h[i] >> 16; 153 md[4 * i + 2] = s->h[i] >> 16;
130 » » md[4*i+3] = s->h[i] >> 24; 154 md[4 * i + 3] = s->h[i] >> 24;
131 » } 155 }
132 } 156 }
133 157
134 static void md5_update(struct md5 *s, const void *m, unsigned long len) 158 static void md5_update(struct md5* s, const void* m, unsigned long len) {
135 { 159 const uint8_t* p = m;
136 » const uint8_t *p = m; 160 unsigned r = s->len % 64;
137 » unsigned r = s->len % 64;
138 161
139 » s->len += len; 162 s->len += len;
140 » if (r) { 163 if (r) {
141 » » if (len < 64 - r) { 164 if (len < 64 - r) {
142 » » » memcpy(s->buf + r, p, len); 165 memcpy(s->buf + r, p, len);
143 » » » return; 166 return;
144 » » } 167 }
145 » » memcpy(s->buf + r, p, 64 - r); 168 memcpy(s->buf + r, p, 64 - r);
146 » » len -= 64 - r; 169 len -= 64 - r;
147 » » p += 64 - r; 170 p += 64 - r;
148 » » processblock(s, s->buf); 171 processblock(s, s->buf);
149 » } 172 }
150 » for (; len >= 64; len -= 64, p += 64) 173 for (; len >= 64; len -= 64, p += 64)
151 » » processblock(s, p); 174 processblock(s, p);
152 » memcpy(s->buf, p, len); 175 memcpy(s->buf, p, len);
153 } 176 }
154 177
155 /*- 178 /*-
156 * Copyright (c) 2003 Poul-Henning Kamp 179 * Copyright (c) 2003 Poul-Henning Kamp
157 * All rights reserved. 180 * All rights reserved.
158 * 181 *
159 * Redistribution and use in source and binary forms, with or without 182 * Redistribution and use in source and binary forms, with or without
160 * modification, are permitted provided that the following conditions 183 * modification, are permitted provided that the following conditions
161 * are met: 184 * are met:
162 * 1. Redistributions of source code must retain the above copyright 185 * 1. Redistributions of source code must retain the above copyright
(...skipping 13 matching lines...) Expand all
176 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 199 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
177 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 200 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
178 * SUCH DAMAGE. 201 * SUCH DAMAGE.
179 */ 202 */
180 203
181 /* key limit is not part of the original design, added for DoS protection */ 204 /* key limit is not part of the original design, added for DoS protection */
182 #define KEY_MAX 30000 205 #define KEY_MAX 30000
183 #define SALT_MAX 8 206 #define SALT_MAX 8
184 207
185 static const unsigned char b64[] = 208 static const unsigned char b64[] =
186 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 209 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
187 210
188 static char *to64(char *s, unsigned int u, int n) 211 static char* to64(char* s, unsigned int u, int n) {
189 { 212 while (--n >= 0) {
190 » while (--n >= 0) { 213 *s++ = b64[u % 64];
191 » » *s++ = b64[u % 64]; 214 u /= 64;
192 » » u /= 64; 215 }
193 » } 216 return s;
194 » return s;
195 } 217 }
196 218
197 static char *md5crypt(const char *key, const char *setting, char *output) 219 static char* md5crypt(const char* key, const char* setting, char* output) {
198 { 220 struct md5 ctx;
199 » struct md5 ctx; 221 unsigned char md[16];
200 » unsigned char md[16]; 222 unsigned int i, klen, slen;
201 » unsigned int i, klen, slen; 223 const char* salt;
202 » const char *salt; 224 char* p;
203 » char *p;
204 225
205 » /* reject large keys */ 226 /* reject large keys */
206 » klen = strnlen(key, KEY_MAX+1); 227 klen = strnlen(key, KEY_MAX + 1);
207 » if (klen > KEY_MAX) 228 if (klen > KEY_MAX)
208 » » return 0; 229 return 0;
209 230
210 » /* setting: $1$salt$ (closing $ is optional) */ 231 /* setting: $1$salt$ (closing $ is optional) */
211 » if (strncmp(setting, "$1$", 3) != 0) 232 if (strncmp(setting, "$1$", 3) != 0)
212 » » return 0; 233 return 0;
213 » salt = setting + 3; 234 salt = setting + 3;
214 » for (i = 0; i < SALT_MAX && salt[i] && salt[i] != '$'; i++); 235 for (i = 0; i < SALT_MAX && salt[i] && salt[i] != '$'; i++)
215 » slen = i; 236 ;
237 slen = i;
216 238
217 » /* md5(key salt key) */ 239 /* md5(key salt key) */
218 » md5_init(&ctx); 240 md5_init(&ctx);
219 » md5_update(&ctx, key, klen); 241 md5_update(&ctx, key, klen);
220 » md5_update(&ctx, salt, slen); 242 md5_update(&ctx, salt, slen);
221 » md5_update(&ctx, key, klen); 243 md5_update(&ctx, key, klen);
222 » md5_sum(&ctx, md); 244 md5_sum(&ctx, md);
223 245
224 » /* md5(key $1$ salt repeated-md weird-key[0]-0) */ 246 /* md5(key $1$ salt repeated-md weird-key[0]-0) */
225 » md5_init(&ctx); 247 md5_init(&ctx);
226 » md5_update(&ctx, key, klen); 248 md5_update(&ctx, key, klen);
227 » md5_update(&ctx, setting, 3 + slen); 249 md5_update(&ctx, setting, 3 + slen);
228 » for (i = klen; i > sizeof md; i -= sizeof md) 250 for (i = klen; i > sizeof md; i -= sizeof md)
229 » » md5_update(&ctx, md, sizeof md); 251 md5_update(&ctx, md, sizeof md);
230 » md5_update(&ctx, md, i); 252 md5_update(&ctx, md, i);
231 » md[0] = 0; 253 md[0] = 0;
232 » for (i = klen; i; i >>= 1) 254 for (i = klen; i; i >>= 1)
233 » » if (i & 1) 255 if (i & 1)
234 » » » md5_update(&ctx, md, 1); 256 md5_update(&ctx, md, 1);
235 » » else 257 else
236 » » » md5_update(&ctx, key, 1); 258 md5_update(&ctx, key, 1);
237 » md5_sum(&ctx, md); 259 md5_sum(&ctx, md);
238 260
239 » /* md = f(md, key, salt) iteration */ 261 /* md = f(md, key, salt) iteration */
240 » for (i = 0; i < 1000; i++) { 262 for (i = 0; i < 1000; i++) {
241 » » md5_init(&ctx); 263 md5_init(&ctx);
242 » » if (i % 2) 264 if (i % 2)
243 » » » md5_update(&ctx, key, klen); 265 md5_update(&ctx, key, klen);
244 » » else 266 else
245 » » » md5_update(&ctx, md, sizeof md); 267 md5_update(&ctx, md, sizeof md);
246 » » if (i % 3) 268 if (i % 3)
247 » » » md5_update(&ctx, salt, slen); 269 md5_update(&ctx, salt, slen);
248 » » if (i % 7) 270 if (i % 7)
249 » » » md5_update(&ctx, key, klen); 271 md5_update(&ctx, key, klen);
250 » » if (i % 2) 272 if (i % 2)
251 » » » md5_update(&ctx, md, sizeof md); 273 md5_update(&ctx, md, sizeof md);
252 » » else 274 else
253 » » » md5_update(&ctx, key, klen); 275 md5_update(&ctx, key, klen);
254 » » md5_sum(&ctx, md); 276 md5_sum(&ctx, md);
255 » } 277 }
256 278
257 » /* output is $1$salt$hash */ 279 /* output is $1$salt$hash */
258 » memcpy(output, setting, 3 + slen); 280 memcpy(output, setting, 3 + slen);
259 » p = output + 3 + slen; 281 p = output + 3 + slen;
260 » *p++ = '$'; 282 *p++ = '$';
261 » static const unsigned char perm[][3] = { 283 static const unsigned char perm[][3] = {0, 6, 12, 1, 7, 13, 2, 8,
262 » » 0,6,12,1,7,13,2,8,14,3,9,15,4,10,5 }; 284 14, 3, 9, 15, 4, 10, 5};
263 » for (i=0; i<5; i++) p = to64(p, 285 for (i = 0; i < 5; i++)
264 » » (md[perm[i][0]]<<16)|(md[perm[i][1]]<<8)|md[perm[i][2]], 4); 286 p = to64(p, (md[perm[i][0]] << 16) | (md[perm[i][1]] << 8) | md[perm[i][2]],
265 » p = to64(p, md[11], 2); 287 4);
266 » *p = 0; 288 p = to64(p, md[11], 2);
289 *p = 0;
267 290
268 » return output; 291 return output;
269 } 292 }
270 293
271 char *__crypt_md5(const char *key, const char *setting, char *output) 294 char* __crypt_md5(const char* key, const char* setting, char* output) {
272 { 295 static const char testkey[] = "Xy01@#\x01\x02\x80\x7f\xff\r\n\x81\t !";
273 » static const char testkey[] = "Xy01@#\x01\x02\x80\x7f\xff\r\n\x81\t !"; 296 static const char testsetting[] = "$1$abcd0123$";
274 » static const char testsetting[] = "$1$abcd0123$"; 297 static const char testhash[] = "$1$abcd0123$9Qcg8DyviekV3tDGMZynJ1";
275 » static const char testhash[] = "$1$abcd0123$9Qcg8DyviekV3tDGMZynJ1"; 298 char testbuf[64];
276 » char testbuf[64]; 299 char *p, *q;
277 » char *p, *q;
278 300
279 » p = md5crypt(key, setting, output); 301 p = md5crypt(key, setting, output);
280 » /* self test and stack cleanup */ 302 /* self test and stack cleanup */
281 » q = md5crypt(testkey, testsetting, testbuf); 303 q = md5crypt(testkey, testsetting, testbuf);
282 » if (!p || q != testbuf || memcmp(testbuf, testhash, sizeof testhash)) 304 if (!p || q != testbuf || memcmp(testbuf, testhash, sizeof testhash))
283 » » return "*"; 305 return "*";
284 » return p; 306 return p;
285 } 307 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698