OpenCoverage

md5.c

Absolute File Name:/home/opencoverage/opencoverage/guest-scripts/coreutils/src/lib/md5.c
Source codeSwitch to Preprocessed file
LineSourceCount
1/* Functions to compute MD5 message digest of files or memory blocks.-
2 according to the definition of MD5 in RFC 1321 from April 1992.-
3 Copyright (C) 1995-1997, 1999-2001, 2005-2006, 2008-2018 Free Software-
4 Foundation, Inc.-
5 This file is part of the GNU C Library.-
6-
7 This program is free software; you can redistribute it and/or modify it-
8 under the terms of the GNU General Public License as published by the-
9 Free Software Foundation; either version 3, or (at your option) any-
10 later version.-
11-
12 This program is distributed in the hope that it will be useful,-
13 but WITHOUT ANY WARRANTY; without even the implied warranty of-
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the-
15 GNU General Public License for more details.-
16-
17 You should have received a copy of the GNU General Public License-
18 along with this program; if not, see <https://www.gnu.org/licenses/>. */-
19-
20/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */-
21-
22#include <config.h>-
23-
24#if HAVE_OPENSSL_MD5-
25# define GL_OPENSSL_INLINE _GL_EXTERN_INLINE-
26#endif-
27#include "md5.h"-
28-
29#include <stdalign.h>-
30#include <stdint.h>-
31#include <stdlib.h>-
32#include <string.h>-
33#include <sys/types.h>-
34-
35#if USE_UNLOCKED_IO-
36# include "unlocked-io.h"-
37#endif-
38-
39#ifdef _LIBC-
40# include <endian.h>-
41# if __BYTE_ORDER == __BIG_ENDIAN-
42# define WORDS_BIGENDIAN 1-
43# endif-
44/* We need to keep the namespace clean so define the MD5 function-
45 protected using leading __ . */-
46# define md5_init_ctx __md5_init_ctx-
47# define md5_process_block __md5_process_block-
48# define md5_process_bytes __md5_process_bytes-
49# define md5_finish_ctx __md5_finish_ctx-
50# define md5_read_ctx __md5_read_ctx-
51# define md5_stream __md5_stream-
52# define md5_buffer __md5_buffer-
53#endif-
54-
55#ifdef WORDS_BIGENDIAN-
56# define SWAP(n) \-
57 (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))-
58#else-
59# define SWAP(n) (n)-
60#endif-
61-
62#define BLOCKSIZE 32768-
63#if BLOCKSIZE % 64 != 0-
64# error "invalid BLOCKSIZE"-
65#endif-
66-
67#if ! HAVE_OPENSSL_MD5-
68/* This array contains the bytes used to pad the buffer to the next-
69 64-byte boundary. (RFC 1321, 3.1: Step 1) */-
70static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };-
71-
72-
73/* Initialize structure containing state of computation.-
74 (RFC 1321, 3.3: Step 3) */-
75void-
76md5_init_ctx (struct md5_ctx *ctx)-
77{-
78 ctx->A = 0x67452301;-
79 ctx->B = 0xefcdab89;-
80 ctx->C = 0x98badcfe;-
81 ctx->D = 0x10325476;-
82-
83 ctx->total[0] = ctx->total[1] = 0;-
84 ctx->buflen = 0;-
85}
executed 1586 times by 2 tests: end of block
Executed by:
  • md5sum
  • sort
1586
86-
87/* Copy the 4 byte value from v into the memory location pointed to by *cp,-
88 If your architecture allows unaligned access this is equivalent to-
89 * (uint32_t *) cp = v */-
90static void-
91set_uint32 (char *cp, uint32_t v)-
92{-
93 memcpy (cp, &v, sizeof v);-
94}
executed 5168964 times by 2 tests: end of block
Executed by:
  • md5sum
  • sort
5168964
95-
96/* Put result from CTX in first 16 bytes following RESBUF. The result-
97 must be in little endian byte order. */-
98void *-
99md5_read_ctx (const struct md5_ctx *ctx, void *resbuf)-
100{-
101 char *r = resbuf;-
102 set_uint32 (r + 0 * sizeof ctx->A, SWAP (ctx->A));-
103 set_uint32 (r + 1 * sizeof ctx->B, SWAP (ctx->B));-
104 set_uint32 (r + 2 * sizeof ctx->C, SWAP (ctx->C));-
105 set_uint32 (r + 3 * sizeof ctx->D, SWAP (ctx->D));-
106-
107 return resbuf;
executed 1292241 times by 2 tests: return resbuf;
Executed by:
  • md5sum
  • sort
1292241
108}-
109-
110/* Process the remaining bytes in the internal buffer and the usual-
111 prolog according to the standard and write the result to RESBUF. */-
112void *-
113md5_finish_ctx (struct md5_ctx *ctx, void *resbuf)-
114{-
115 /* Take yet unprocessed bytes into account. */-
116 uint32_t bytes = ctx->buflen;-
117 size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4;
(bytes < 56)Description
TRUEevaluated 1292240 times by 2 tests
Evaluated by:
  • md5sum
  • sort
FALSEevaluated 1 time by 1 test
Evaluated by:
  • md5sum
1-1292240
118-
119 /* Now count remaining bytes. */-
120 ctx->total[0] += bytes;-
121 if (ctx->total[0] < bytes)
ctx->total[0] < bytesDescription
TRUEnever evaluated
FALSEevaluated 1292241 times by 2 tests
Evaluated by:
  • md5sum
  • sort
0-1292241
122 ++ctx->total[1];
never executed: ++ctx->total[1];
0
123-
124 /* Put the 64-bit file length in *bits* at the end of the buffer. */-
125 ctx->buffer[size - 2] = SWAP (ctx->total[0] << 3);-
126 ctx->buffer[size - 1] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29));-
127-
128 memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes);-
129-
130 /* Process last bytes. */-
131 md5_process_block (ctx->buffer, size * 4, ctx);-
132-
133 return md5_read_ctx (ctx, resbuf);
executed 1292241 times by 2 tests: return md5_read_ctx (ctx, resbuf);
Executed by:
  • md5sum
  • sort
1292241
134}-
135#endif-
136-
137/* Compute MD5 message digest for bytes read from STREAM. The-
138 resulting message digest number will be written into the 16 bytes-
139 beginning at RESBLOCK. */-
140int-
141md5_stream (FILE *stream, void *resblock)-
142{-
143 struct md5_ctx ctx;-
144 size_t sum;-
145-
146 char *buffer = malloc (BLOCKSIZE + 72);-
147 if (!buffer)
!bufferDescription
TRUEnever evaluated
FALSEevaluated 1571 times by 1 test
Evaluated by:
  • md5sum
0-1571
148 return 1;
never executed: return 1;
0
149-
150 /* Initialize the computation context. */-
151 md5_init_ctx (&ctx);-
152-
153 /* Iterate over full file contents. */-
154 while (1)-
155 {-
156 /* We read the file in blocks of BLOCKSIZE bytes. One call of the-
157 computation function processes the whole buffer so that with the-
158 next round of the loop another block can be read. */-
159 size_t n;-
160 sum = 0;-
161-
162 /* Read block. Take care for partial reads. */-
163 while (1)-
164 {-
165 n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);-
166-
167 sum += n;-
168-
169 if (sum == BLOCKSIZE)
sum == 32768Description
TRUEevaluated 250 times by 1 test
Evaluated by:
  • md5sum
FALSEevaluated 1571 times by 1 test
Evaluated by:
  • md5sum
250-1571
170 break;
executed 250 times by 1 test: break;
Executed by:
  • md5sum
250
171-
172 if (n == 0)
n == 0Description
TRUEevaluated 1522 times by 1 test
Evaluated by:
  • md5sum
FALSEevaluated 49 times by 1 test
Evaluated by:
  • md5sum
49-1522
173 {-
174 /* Check for the error flag IFF N == 0, so that we don't-
175 exit the loop after a partial read due to e.g., EAGAIN-
176 or EWOULDBLOCK. */-
177 if (ferror (stream))
ferror_unlocked (stream)Description
TRUEnever evaluated
FALSEevaluated 1522 times by 1 test
Evaluated by:
  • md5sum
0-1522
178 {-
179 free (buffer);-
180 return 1;
never executed: return 1;
0
181 }-
182 goto process_partial_block;
executed 1522 times by 1 test: goto process_partial_block;
Executed by:
  • md5sum
1522
183 }-
184-
185 /* We've read at least one byte, so ignore errors. But always-
186 check for EOF, since feof may be true even though N > 0.-
187 Otherwise, we could end up calling fread after EOF. */-
188 if (feof (stream))
feof_unlocked (stream)Description
TRUEevaluated 49 times by 1 test
Evaluated by:
  • md5sum
FALSEnever evaluated
0-49
189 goto process_partial_block;
executed 49 times by 1 test: goto process_partial_block;
Executed by:
  • md5sum
49
190 }
never executed: end of block
0
191-
192 /* Process buffer with BLOCKSIZE bytes. Note that-
193 BLOCKSIZE % 64 == 0-
194 */-
195 md5_process_block (buffer, BLOCKSIZE, &ctx);-
196 }
executed 250 times by 1 test: end of block
Executed by:
  • md5sum
250
197-
198process_partial_block:
code before this statement never executed: process_partial_block:
0
199-
200 /* Process any remaining bytes. */-
201 if (sum > 0)
sum > 0Description
TRUEevaluated 49 times by 1 test
Evaluated by:
  • md5sum
FALSEevaluated 1522 times by 1 test
Evaluated by:
  • md5sum
49-1522
202 md5_process_bytes (buffer, sum, &ctx);
executed 49 times by 1 test: md5_process_bytes (buffer, sum, &ctx);
Executed by:
  • md5sum
49
203-
204 /* Construct result in desired memory. */-
205 md5_finish_ctx (&ctx, resblock);-
206 free (buffer);-
207 return 0;
executed 1571 times by 1 test: return 0;
Executed by:
  • md5sum
1571
208}-
209-
210#if ! HAVE_OPENSSL_MD5-
211/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The-
212 result is always in little endian byte order, so that a byte-wise-
213 output yields to the wanted ASCII representation of the message-
214 digest. */-
215void *-
216md5_buffer (const char *buffer, size_t len, void *resblock)-
217{-
218 struct md5_ctx ctx;-
219-
220 /* Initialize the computation context. */-
221 md5_init_ctx (&ctx);-
222-
223 /* Process whole buffer but last len % 64 bytes. */-
224 md5_process_bytes (buffer, len, &ctx);-
225-
226 /* Put result in desired memory area. */-
227 return md5_finish_ctx (&ctx, resblock);
never executed: return md5_finish_ctx (&ctx, resblock);
0
228}-
229-
230-
231void-
232md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx)-
233{-
234 /* When we already have some bits in our internal buffer concatenate-
235 both inputs first. */-
236 if (ctx->buflen != 0)
ctx->buflen != 0Description
TRUEevaluated 1290670 times by 1 test
Evaluated by:
  • sort
FALSEevaluated 64 times by 2 tests
Evaluated by:
  • md5sum
  • sort
64-1290670
237 {-
238 size_t left_over = ctx->buflen;-
239 size_t add = 128 - left_over > len ? len : 128 - left_over;
128 - left_over > lenDescription
TRUEevaluated 1290670 times by 1 test
Evaluated by:
  • sort
FALSEnever evaluated
0-1290670
240-
241 memcpy (&((char *) ctx->buffer)[left_over], buffer, add);-
242 ctx->buflen += add;-
243-
244 if (ctx->buflen > 64)
ctx->buflen > 64Description
TRUEevaluated 240828 times by 1 test
Evaluated by:
  • sort
FALSEevaluated 1049842 times by 1 test
Evaluated by:
  • sort
240828-1049842
245 {-
246 md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx);-
247-
248 ctx->buflen &= 63;-
249 /* The regions in the following copy operation cannot overlap,-
250 because ctx->buflen < 64 ≤ (left_over + add) & ~63. */-
251 memcpy (ctx->buffer,-
252 &((char *) ctx->buffer)[(left_over + add) & ~63],-
253 ctx->buflen);-
254 }
executed 240828 times by 1 test: end of block
Executed by:
  • sort
240828
255-
256 buffer = (const char *) buffer + add;-
257 len -= add;-
258 }
executed 1290670 times by 1 test: end of block
Executed by:
  • sort
1290670
259-
260 /* Process available complete blocks. */-
261 if (len >= 64)
len >= 64Description
TRUEevaluated 3 times by 1 test
Evaluated by:
  • md5sum
FALSEevaluated 1290731 times by 2 tests
Evaluated by:
  • md5sum
  • sort
3-1290731
262 {-
263#if !(_STRING_ARCH_unaligned || _STRING_INLINE_unaligned)-
264# define UNALIGNED_P(p) ((uintptr_t) (p) % alignof (uint32_t) != 0)-
265 if (UNALIGNED_P (buffer))-
266 while (len > 64)-
267 {-
268 md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx);-
269 buffer = (const char *) buffer + 64;-
270 len -= 64;-
271 }-
272 else-
273#endif-
274 {-
275 md5_process_block (buffer, len & ~63, ctx);-
276 buffer = (const char *) buffer + (len & ~63);-
277 len &= 63;-
278 }-
279 }
executed 3 times by 1 test: end of block
Executed by:
  • md5sum
3
280-
281 /* Move remaining bytes in internal buffer. */-
282 if (len > 0)
len > 0Description
TRUEevaluated 64 times by 2 tests
Evaluated by:
  • md5sum
  • sort
FALSEevaluated 1290670 times by 1 test
Evaluated by:
  • sort
64-1290670
283 {-
284 size_t left_over = ctx->buflen;-
285-
286 memcpy (&((char *) ctx->buffer)[left_over], buffer, len);-
287 left_over += len;-
288 if (left_over >= 64)
left_over >= 64Description
TRUEnever evaluated
FALSEevaluated 64 times by 2 tests
Evaluated by:
  • md5sum
  • sort
0-64
289 {-
290 md5_process_block (ctx->buffer, 64, ctx);-
291 left_over -= 64;-
292 /* The regions in the following copy operation cannot overlap,-
293 because left_over ≤ 64. */-
294 memcpy (ctx->buffer, &ctx->buffer[16], left_over);-
295 }
never executed: end of block
0
296 ctx->buflen = left_over;-
297 }
executed 64 times by 2 tests: end of block
Executed by:
  • md5sum
  • sort
64
298}
executed 1290734 times by 2 tests: end of block
Executed by:
  • md5sum
  • sort
1290734
299-
300-
301/* These are the four functions used in the four steps of the MD5 algorithm-
302 and defined in the RFC 1321. The first function is a little bit optimized-
303 (as found in Colin Plumbs public domain implementation). */-
304/* #define FF(b, c, d) ((b & c) | (~b & d)) */-
305#define FF(b, c, d) (d ^ (b & (c ^ d)))-
306#define FG(b, c, d) FF (d, b, c)-
307#define FH(b, c, d) (b ^ c ^ d)-
308#define FI(b, c, d) (c ^ (b | ~d))-
309-
310/* Process LEN bytes of BUFFER, accumulating context into CTX.-
311 It is assumed that LEN % 64 == 0. */-
312-
313void-
314md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx)-
315{-
316 uint32_t correct_words[16];-
317 const uint32_t *words = buffer;-
318 size_t nwords = len / sizeof (uint32_t);-
319 const uint32_t *endp = words + nwords;-
320 uint32_t A = ctx->A;-
321 uint32_t B = ctx->B;-
322 uint32_t C = ctx->C;-
323 uint32_t D = ctx->D;-
324 uint32_t lolen = len;-
325-
326 /* First increment the byte count. RFC 1321 specifies the possible-
327 length of the file up to 2^64 bits. Here we only compute the-
328 number of bytes. Do a double word increment. */-
329 ctx->total[0] += lolen;-
330 ctx->total[1] += (len >> 31 >> 1) + (ctx->total[0] < lolen);-
331-
332 /* Process all bytes in the buffer with 64 bytes in each round of-
333 the loop. */-
334 while (words < endp)
words < endpDescription
TRUEevaluated 1661253 times by 2 tests
Evaluated by:
  • md5sum
  • sort
FALSEevaluated 1533322 times by 2 tests
Evaluated by:
  • md5sum
  • sort
1533322-1661253
335 {-
336 uint32_t *cwp = correct_words;-
337 uint32_t A_save = A;-
338 uint32_t B_save = B;-
339 uint32_t C_save = C;-
340 uint32_t D_save = D;-
341-
342 /* First round: using the given function, the context and a constant-
343 the next context is computed. Because the algorithms processing-
344 unit is a 32-bit word and it is determined to work on words in-
345 little endian byte order we perhaps have to change the byte order-
346 before the computation. To reduce the work for the next steps-
347 we store the swapped words in the array CORRECT_WORDS. */-
348-
349#define OP(a, b, c, d, s, T) \-
350 do \-
351 { \-
352 a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \-
353 ++words; \-
354 CYCLIC (a, s); \-
355 a += b; \-
356 } \-
357 while (0)-
358-
359 /* It is unfortunate that C does not provide an operator for-
360 cyclic rotation. Hope the C compiler is smart enough. */-
361#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))-
362-
363 /* Before we start, one word to the strange constants.-
364 They are defined in RFC 1321 as-
365-
366 T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64-
367-
368 Here is an equivalent invocation using Perl:-
369-
370 perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}'-
371 */-
372-
373 /* Round 1. */-
374 OP (A, B, C, D, 7, 0xd76aa478);-
375 OP (D, A, B, C, 12, 0xe8c7b756);-
376 OP (C, D, A, B, 17, 0x242070db);-
377 OP (B, C, D, A, 22, 0xc1bdceee);-
378 OP (A, B, C, D, 7, 0xf57c0faf);-
379 OP (D, A, B, C, 12, 0x4787c62a);-
380 OP (C, D, A, B, 17, 0xa8304613);-
381 OP (B, C, D, A, 22, 0xfd469501);-
382 OP (A, B, C, D, 7, 0x698098d8);-
383 OP (D, A, B, C, 12, 0x8b44f7af);-
384 OP (C, D, A, B, 17, 0xffff5bb1);-
385 OP (B, C, D, A, 22, 0x895cd7be);-
386 OP (A, B, C, D, 7, 0x6b901122);-
387 OP (D, A, B, C, 12, 0xfd987193);-
388 OP (C, D, A, B, 17, 0xa679438e);-
389 OP (B, C, D, A, 22, 0x49b40821);-
390-
391 /* For the second to fourth round we have the possibly swapped words-
392 in CORRECT_WORDS. Redefine the macro to take an additional first-
393 argument specifying the function to use. */-
394#undef OP-
395#define OP(f, a, b, c, d, k, s, T) \-
396 do \-
397 { \-
398 a += f (b, c, d) + correct_words[k] + T; \-
399 CYCLIC (a, s); \-
400 a += b; \-
401 } \-
402 while (0)-
403-
404 /* Round 2. */-
405 OP (FG, A, B, C, D, 1, 5, 0xf61e2562);-
406 OP (FG, D, A, B, C, 6, 9, 0xc040b340);-
407 OP (FG, C, D, A, B, 11, 14, 0x265e5a51);-
408 OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);-
409 OP (FG, A, B, C, D, 5, 5, 0xd62f105d);-
410 OP (FG, D, A, B, C, 10, 9, 0x02441453);-
411 OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);-
412 OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);-
413 OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);-
414 OP (FG, D, A, B, C, 14, 9, 0xc33707d6);-
415 OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);-
416 OP (FG, B, C, D, A, 8, 20, 0x455a14ed);-
417 OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);-
418 OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);-
419 OP (FG, C, D, A, B, 7, 14, 0x676f02d9);-
420 OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);-
421-
422 /* Round 3. */-
423 OP (FH, A, B, C, D, 5, 4, 0xfffa3942);-
424 OP (FH, D, A, B, C, 8, 11, 0x8771f681);-
425 OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);-
426 OP (FH, B, C, D, A, 14, 23, 0xfde5380c);-
427 OP (FH, A, B, C, D, 1, 4, 0xa4beea44);-
428 OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);-
429 OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);-
430 OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);-
431 OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);-
432 OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);-
433 OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);-
434 OP (FH, B, C, D, A, 6, 23, 0x04881d05);-
435 OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);-
436 OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);-
437 OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);-
438 OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);-
439-
440 /* Round 4. */-
441 OP (FI, A, B, C, D, 0, 6, 0xf4292244);-
442 OP (FI, D, A, B, C, 7, 10, 0x432aff97);-
443 OP (FI, C, D, A, B, 14, 15, 0xab9423a7);-
444 OP (FI, B, C, D, A, 5, 21, 0xfc93a039);-
445 OP (FI, A, B, C, D, 12, 6, 0x655b59c3);-
446 OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);-
447 OP (FI, C, D, A, B, 10, 15, 0xffeff47d);-
448 OP (FI, B, C, D, A, 1, 21, 0x85845dd1);-
449 OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);-
450 OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);-
451 OP (FI, C, D, A, B, 6, 15, 0xa3014314);-
452 OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);-
453 OP (FI, A, B, C, D, 4, 6, 0xf7537e82);-
454 OP (FI, D, A, B, C, 11, 10, 0xbd3af235);-
455 OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);-
456 OP (FI, B, C, D, A, 9, 21, 0xeb86d391);-
457-
458 /* Add the starting values of the context. */-
459 A += A_save;-
460 B += B_save;-
461 C += C_save;-
462 D += D_save;-
463 }
executed 1661253 times by 2 tests: end of block
Executed by:
  • md5sum
  • sort
1661253
464-
465 /* Put checksum in context given as argument. */-
466 ctx->A = A;-
467 ctx->B = B;-
468 ctx->C = C;-
469 ctx->D = D;-
470}
executed 1533322 times by 2 tests: end of block
Executed by:
  • md5sum
  • sort
1533322
471#endif-
472-
473/*-
474 * Hey Emacs!-
475 * Local Variables:-
476 * coding: utf-8-
477 * End:-
478 */-
Source codeSwitch to Preprocessed file

Generated by Squish Coco 4.1.2