cryptal  latest
Cryptography Abstraction Layer
CCM.php
1 <?php
2 
4 
7 
11 class CCM implements AsymmetricModeInterface
12 {
14  protected $cipher;
15 
17  protected $nonce;
18 
20  protected $L;
21 
23  protected $M;
24 
25  public function __construct(CryptoInterface $cipher, $iv, $tagLength)
26  {
27  if (16 !== $cipher->getBlockSize()) {
28  throw new \InvalidArgumentException('Incompatible cipher (block size != 16)');
29  }
30 
31  $nonceSize = strlen($iv);
32  if ($nonceSize < 7 || $nonceSize > 13) {
33  throw new \Exception("Invalid nonce (should be between 7 and 13 bytes long)");
34  }
35 
36  if (($tagLength & 0x1) || $tagLength < 4 || $tagLength > 16) {
37  throw new \Exception("Invalid tag length (valid values: 4, 6, 8, 10, 12, 14 & 16)");
38  }
39 
40  $this->cipher = $cipher;
41  $this->nonce = $iv;
42  $this->L = 15 - $nonceSize;
43  $this->M = ($tagLength - 2) >> 1;
44  }
45 
46  protected function checkum($M, $A)
47  {
48  $len = strlen($M);
49  for ($i = 0; $i < $this->L; $i++) {
50  $len >>= 8;
51  }
52  if ($len > 0) {
53  throw new \InvalidArgumentException('Invalid length for input data (greater than 2**8L)');
54  }
55 
56  // Build the first block.
57  // Note: l(m) is < 2**32 in this implementation
58  $lenA = strlen($A);
59  $lenM = str_pad(ltrim(pack('N', strlen($M)), "\x00"), $this->L, "\x00", STR_PAD_LEFT);
60  $b = chr((($lenA > 0) << 6) | ($this->M << 3) | ($this->L - 1)) . $this->nonce . $lenM;
61 
62  // Encode "l(a)"
63  if ($lenA < ((1 << 16) - (1 << 8))) {
64  // 0 < l(a) < 2**16 - 2**8
65  $b .= pack("n", $lenA);
66  } elseif (($lenA >> 32) === 0) {
67  // 2**16 - 2**8 <= l(a) < 2**32
68  $b .= pack("nN", 0xFEFF, $lenA);
69  } else {
70  // Messages with l(a) >= 2**32 are not supported yet
71  throw new \RuntimeException('Not implemented yet');
72  }
73 
74  // Encode "a" and add padding if necessary
75  $b .= $A;
76  $b .= str_repeat("\x00", (16 - (strlen($b) % 16)) % 16);
77 
78  // Encode "m" and add padding if necessary
79  $b .= $M;
80  $b .= str_repeat("\x00", (16 - (strlen($b) % 16)) % 16);
81 
82  // Compute the checksum "X"
83  $X = str_repeat("\x00", 16);
84  foreach (str_split($b, 16) as $block) {
85  $X = $this->cipher->encrypt('', $X ^ $block);
86  }
87  $T = substr($X, 0, ($this->M << 1) + 2);
88  return $T;
89  }
90 
92  protected function incrementCounter($c)
93  {
94  $carry = 1;
95  for ($i = $this->L - 1; $i >= 0; $i--) {
96  // chr() takes care of overflows automatically.
97  $c[$i] = chr(ord($c[$i]) + $carry);
98  $carry &= ("\x00" === $c[$i]);
99  }
100  return $c;
101  }
102 
103 
104  public function encrypt($data, $context)
105  {
106  $options = stream_context_get_options($context);
107  $Adata = isset($options['cryptal']['data']) ? (string) $options['cryptal']['data'] : '';
108  $counter = str_repeat("\x00", $this->L);
109  $a = chr($this->L - 1) . $this->nonce; // Flags & nonce
110  $S0 = $this->cipher->encrypt('', $a . $counter);
111 
112  $res = '';
113  foreach (str_split($data, 16) as $block) {
114  $counter = $this->incrementCounter($counter);
115  $res .= $block ^ $this->cipher->encrypt('', $a . $counter);
116  }
117 
118  stream_context_set_option($context, 'cryptal', 'tag', $this->checkum($data, $Adata) ^ $S0);
119  return $res;
120  }
121 
122  public function decrypt($data, $context)
123  {
124  $options = stream_context_get_options($context);
125  $Adata = isset($options['cryptal']['data']) ? (string) $options['cryptal']['data'] : '';
126  $T = isset($options['cryptal']['tag']) ? (string) $options['cryptal']['tag'] : '';
127  $counter = str_repeat("\x00", $this->L);
128  $a = chr($this->L - 1) . $this->nonce; // Flags & nonce
129  $S0 = $this->cipher->encrypt('', $a . $counter);
130 
131  $res = '';
132  foreach (str_split($data, 16) as $block) {
133  $counter = $this->incrementCounter($counter);
134  $res .= $block ^ $this->cipher->encrypt('', $a . $counter);
135  }
136 
137  $T2 = $this->checkum($res, $Adata) ^ $S0;
138  if ($T2 !== $T) {
139  throw new \InvalidArgumentException('Tag does not match expected value');
140  }
141  return $res;
142  }
143 }
encrypt($data, $context)
Definition: CCM.php:104
$L
Length parameter.
Definition: CCM.php:20
__construct(CryptoInterface $cipher, $iv, $tagLength)
Definition: CCM.php:25
$M
Output tag length.
Definition: CCM.php:23
incrementCounter($c)
Increment the value of the counter by one.
Definition: CCM.php:92
decrypt($data, $context)
Definition: CCM.php:122