-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
<?php | ||
|
||
namespace fpoirotte\Cryptal\Modes; | ||
|
||
use fpoirotte\Cryptal\Implementers\CryptoInterface; | ||
use fpoirotte\Cryptal\AsymmetricModeInterface; | ||
|
||
/** | ||
* Counter with CBC-MAC (CCM) | ||
*/ | ||
class CCM implements AsymmetricModeInterface | ||
{ | ||
/// Cipher | ||
protected $cipher; | ||
|
||
/// Nonce | ||
protected $nonce; | ||
|
||
/// Length parameter | ||
protected $L; | ||
|
||
/// Output tag length | ||
protected $M; | ||
|
||
public function __construct(CryptoInterface $cipher, $iv, $tagLength) | ||
{ | ||
if (16 !== $cipher->getBlockSize()) { | ||
throw new \InvalidArgumentException('Incompatible cipher (block size != 16)'); | ||
} | ||
|
||
$nonceSize = strlen($iv); | ||
if ($nonceSize < 7 || $nonceSize > 13) { | ||
throw new \Exception("Invalid nonce (should be between 7 and 13 bytes long)"); | ||
} | ||
|
||
if (($tagLength & 0x1) || $tagLength < 4 || $tagLength > 16) { | ||
throw new \Exception("Invalid tag length (valid values: 4, 6, 8, 10, 12, 14 & 16)"); | ||
} | ||
|
||
$this->cipher = $cipher; | ||
$this->nonce = $iv; | ||
$this->L = 15 - $nonceSize; | ||
$this->M = ($tagLength - 2) >> 1; | ||
} | ||
|
||
protected function checkum($M, $A) | ||
{ | ||
$len = strlen($M); | ||
for ($i = 0; $i < $this->L; $i++) { | ||
$len >>= 8; | ||
} | ||
if ($len > 0) { | ||
throw new \InvalidArgumentException('Invalid length for input data (greater than 2**8L)'); | ||
} | ||
|
||
// Build the first block. | ||
// Note: l(m) is < 2**32 in this implementation | ||
$lenA = strlen($A); | ||
$lenM = str_pad(ltrim(pack('N', strlen($M)), "\x00"), $this->L, "\x00", STR_PAD_LEFT); | ||
$b = chr((($lenA > 0) << 6) | ($this->M << 3) | ($this->L - 1)) . $this->nonce . $lenM; | ||
|
||
// Encode "l(a)" | ||
if ($lenA < ((1 << 16) - (1 << 8))) { | ||
// 0 < l(a) < 2**16 - 2**8 | ||
$b .= pack("n", $lenA); | ||
} elseif (($lenA >> 32) === 0) { | ||
// 2**16 - 2**8 <= l(a) < 2**32 | ||
$b .= pack("nN", 0xFEFF, $lenA); | ||
} else { | ||
// Messages with l(a) >= 2**32 are not supported yet | ||
throw new \RuntimeException('Not implemented yet'); | ||
} | ||
|
||
// Encode "a" and add padding if necessary | ||
$b .= $A; | ||
$b .= str_repeat("\x00", (16 - (strlen($b) % 16)) % 16); | ||
|
||
// Encode "m" and add padding if necessary | ||
$b .= $M; | ||
$b .= str_repeat("\x00", (16 - (strlen($b) % 16)) % 16); | ||
|
||
// Compute the checksum "X" | ||
$X = str_repeat("\x00", 16); | ||
foreach (str_split($b, 16) as $block) { | ||
$X = $this->cipher->encrypt('', $X ^ $block); | ||
} | ||
$T = substr($X, 0, ($this->M << 1) + 2); | ||
return $T; | ||
} | ||
|
||
/// Increment the value of the counter by one. | ||
protected function incrementCounter($c) | ||
{ | ||
for ($i = $this->L - 1; $i >= 0; $i--) { | ||
// chr() takes care of overflows automatically. | ||
$c[$i] = chr(ord($c[$i]) + 1); | ||
|
||
// Stop, unless the incremented generated an overflow. | ||
// In that case, we continue to propagate the carry. | ||
if ("\x00" !== $c[$i]) { | ||
break; | ||
} | ||
} | ||
return $c; | ||
} | ||
|
||
|
||
public function encrypt($data, $context) | ||
{ | ||
$options = stream_context_get_options($context); | ||
$Adata = isset($options['cryptal']['data']) ? (string) $options['cryptal']['data'] : ''; | ||
$counter = str_repeat("\x00", $this->L); | ||
$a = chr($this->L - 1) . $this->nonce; // Flags & nonce | ||
$S0 = $this->cipher->encrypt('', $a . $counter); | ||
|
||
$res = ''; | ||
foreach (str_split($data, 16) as $block) { | ||
$counter = $this->incrementCounter($counter); | ||
$res .= $block ^ $this->cipher->encrypt('', $a . $counter); | ||
} | ||
|
||
stream_context_set_option($context, 'cryptal', 'tag', $this->checkum($data, $Adata) ^ $S0); | ||
return $res; | ||
} | ||
|
||
public function decrypt($data, $context) | ||
{ | ||
$options = stream_context_get_options($context); | ||
$Adata = isset($options['cryptal']['data']) ? (string) $options['cryptal']['data'] : ''; | ||
$T = isset($options['cryptal']['tag']) ? (string) $options['cryptal']['tag'] : ''; | ||
$counter = str_repeat("\x00", $this->L); | ||
$a = chr($this->L - 1) . $this->nonce; // Flags & nonce | ||
$S0 = $this->cipher->encrypt('', $a . $counter); | ||
|
||
$res = ''; | ||
foreach (str_split($data, 16) as $block) { | ||
$counter = $this->incrementCounter($counter); | ||
$res .= $block ^ $this->cipher->encrypt('', $a . $counter); | ||
} | ||
|
||
$T2 = $this->checkum($res, $Adata) ^ $S0; | ||
if ($T2 !== $T) { | ||
throw new \InvalidArgumentException('Tag does not match expected value'); | ||
} | ||
return $res; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
<?php | ||
|
||
use fpoirotte\Cryptal\Padding\None; | ||
use fpoirotte\Cryptal\CipherEnum; | ||
use fpoirotte\Cryptal\ModeEnum; | ||
|
||
/** | ||
* Test point addition for various NIST curves | ||
* using the test vectors at http://point-at-infinity.org/ecc/nisttv | ||
*/ | ||
class CCMTest extends \PHPUnit\Framework\TestCase | ||
{ | ||
static $cache; | ||
|
||
public static function setUpBeforeClass() | ||
{ | ||
self::$cache = array(); | ||
} | ||
|
||
public function getCipher($key) | ||
{ | ||
if (!isset(self::$cache[$key])) { | ||
$map = array( | ||
16 => CipherEnum::CIPHER_AES_128(), | ||
); | ||
$cipher = new AesEcbStub($map[strlen($key)], ModeEnum::MODE_ECB(), new None, $key); | ||
self::$cache[$key] = $cipher; | ||
} | ||
return self::$cache[$key]; | ||
} | ||
|
||
public function vectors() | ||
{ | ||
// K, N, A, P, C, T | ||
return array( | ||
// Test vectors from RFC 3610 | ||
array( | ||
'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF', | ||
'00000003020100A0A1A2A3A4A5', | ||
'0001020304050607', | ||
'08090A0B0C0D0E0F101112131415161718191A1B1C1D1E', | ||
'588C979A61C663D2F066D0C2C0F989806D5F6B61DAC384', | ||
'17E8D12CFDF926E0', | ||
), | ||
array( | ||
'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF', | ||
'00000004030201A0A1A2A3A4A5', | ||
'0001020304050607', | ||
'08090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F', | ||
'72C91A36E135F8CF291CA894085C87E3CC15C439C9E43A3B', | ||
'A091D56E10400916', | ||
), | ||
array( | ||
'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF', | ||
'00000005040302A0A1A2A3A4A5', | ||
'0001020304050607', | ||
'08090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20', | ||
'51B1E5F44A197D1DA46B0F8E2D282AE871E838BB64DA859657', | ||
'4ADAA76FBD9FB0C5', | ||
), | ||
array( | ||
'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF', | ||
'00000006050403A0A1A2A3A4A5', | ||
'000102030405060708090A0B', | ||
'0C0D0E0F101112131415161718191A1B1C1D1E', | ||
'A28C6865939A9A79FAAA5C4C2A9D4A91CDAC8C', | ||
'96C861B9C9E61EF1', | ||
), | ||
array( | ||
'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF', | ||
'00000007060504A0A1A2A3A4A5', | ||
'000102030405060708090A0B', | ||
'0C0D0E0F101112131415161718191A1B1C1D1E1F', | ||
'DCF1FB7B5D9E23FB9D4E131253658AD86EBDCA3E', | ||
'51E83F077D9C2D93', | ||
), | ||
array( | ||
'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF', | ||
'00000008070605A0A1A2A3A4A5', | ||
'000102030405060708090A0B', | ||
'0C0D0E0F101112131415161718191A1B1C1D1E1F20', | ||
'6FC1B011F006568B5171A42D953D469B2570A4BD87', | ||
'405A0443AC91CB94', | ||
), | ||
array( | ||
'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF', | ||
'00000009080706A0A1A2A3A4A5', | ||
'0001020304050607', | ||
'08090A0B0C0D0E0F101112131415161718191A1B1C1D1E', | ||
'0135D1B2C95F41D5D1D4FEC185D166B8094E999DFED96C', | ||
'048C56602C97ACBB7490', | ||
), | ||
); | ||
} | ||
|
||
/** | ||
* @dataProvider vectors | ||
* @group medium | ||
*/ | ||
public function testCCM($K, $N, $A, $P, $C, $T) | ||
{ | ||
$K = pack('H*', $K); | ||
$P = pack('H*', $P); | ||
$A = pack('H*', $A); | ||
$N = pack('H*', $N); | ||
$C = strtolower($C); | ||
$T = strtolower($T); | ||
|
||
$cipher = $this->getCipher($K); | ||
$ccm = new fpoirotte\Cryptal\Modes\CCM($cipher, $N, strlen($T) >> 1); | ||
$ctx = stream_context_create(array('cryptal' => array('data' => $A))); | ||
|
||
$res = $ccm->encrypt($P, $ctx); | ||
$options = stream_context_get_options($ctx); | ||
$this->assertSame($C, bin2hex($res)); | ||
$this->assertSame($T, bin2hex($options['cryptal']['tag'])); | ||
|
||
$res = $ccm->decrypt($res, $ctx); | ||
$this->assertSame(bin2hex($P), bin2hex($res)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
0100000003020100a0a1a2a3a4a50000 3a2e46c8ec33a5485620542c022cc07d | ||
0100000003020100a0a1a2a3a4a50001 50859d916dcb6ddde077c2d1d4ec9f97 | ||
0100000003020100a0a1a2a3a4a50002 7546717ac6de9aff640c9c06de6d0d8f | ||
5900000003020100a0a1a2a3a4a50017 eb9d5547730955ab231e0a2dfe4b90d6 | ||
eb955546710a51ae25190a2dfe4b90d6 cdb6411e3cdc9b4f5d9258b69ee7f091 | ||
c5bf4b1530d195404d834aa58af2e686 9c38405ea03c1bc904b58b40c76ca2eb | ||
84215a45bc2105c904b58b40c76ca2eb 2dc697e411ca83a860c2c406ccaa542f | ||
0100000009080706a0a1a2a3a4a50000 8d07802562b08c00a6eead29752b0add | ||
0100000009080706a0a1a2a3a4a50001 093cdbb9c5524fdac1c5ecd291c470af | ||
0100000009080706a0a1a2a3a4a50002 11578386e2c472b48ecc8aadab776fcb | ||
0100000004030201a0a1a2a3a4a50000 5728d00496d265e556a857c1025f93af | ||
0100000004030201a0a1a2a3a4a50001 7ac0103ded38f6c0390dba871c4991f4 | ||
0100000004030201a0a1a2a3a4a50002 d40cde22d5f92424f7be9a569da79f51 | ||
5900000004030201a0a1a2a3a4a50018 f0c254d3ca03e23970bd24a84c399e77 | ||
0100000005040302a0a1a2a3a4a50000 396ec01a7db96e6fa12aa238c0295bf5 | ||
0100000005040302a0a1a2a3a4a50001 59b8efff46147312b47a1d9d393d3cff | ||
0100000005040302a0a1a2a3a4a50002 69f122a078c79b8977894c99975c2378 | ||
6100000009080706a0a1a2a3a4a50017 6006c572da239cbfa05b0aded2cda81e | ||
600ec573d82098baa65c0aded2cda81e 417de2ae94e2ead900fc44fcd0695227 | ||
4974e8a598efe4d610ed56efc47c4430 2a6c42ca49d7c701c57d59ff8716490e | ||
327558d155cad901c57d59ff8716490e 898bd6454e2720bbd27ef3157a7c90b2 | ||
0100000008070605a0a1a2a3a4a50000 e57ddc56c652922b6a1ab87baa923b99 | ||
0100000008070605a0a1a2a3a4a50001 63ccbe1ee01744984564b23a8d245c80 | ||
0100000008070605a0a1a2a3a4a50002 396dbaa2a7d2cbd4b5e17c107945bbc0 | ||
5900000008070605a0a1a2a3a4a50015 0472da4c6ff60a6306521a060480cde5 | ||
047eda4d6df50e660055120f0e8bcde5 644c36a5a22737620b89f1d7bff273d4 | ||
684138aab23625711f9ce7c0a7eb69cf 41e119cd1924ce77f12fa660c16ebb4e | ||
5dfc07d23924ce77f12fa660c16ebb4e a527d8156ac359bf1cb886e62f299129 | ||
0100000007060504a0a1a2a3a4a50000 1951d78528996726b001fa7ceca6dcad | ||
0100000007060504a0a1a2a3a4a50001 d0fcf5744d8f31e8895b05054b7c90c3 | ||
0100000007060504a0a1a2a3a4a50002 72a0d4219f0de1d40483bc2d3d0cfc2a | ||
5900000007060504a0a1a2a3a4a50014 004c509545803c4851cde13b56c89a85 | ||
004050944783384d57cae9325cc39a85 e2b8f7ce49b2217284a8ea84faad675c | ||
eeb5f9c159a3336190bdfc93e2b47d47 3efb367225db1101d3c22f0ecaff44f3 | ||
22e6286d25db1101d3c22f0ecaff44f3 48b9e88255054ab5490a95f9349b4b5e | ||
f0ca54d2c800e63c76ba24a84c399e77 48de8b8628ea4a4000aa42c295bf4a8c | ||
40d7818d24e7444f10bb50d181aa5c9b 0f89ffbca62bc24f13215f168796aa33 | ||
1790e5a7ba36dc5013215f168796aa33 f7b9056a86926cf3fb163dc499efaa11 | ||
5900000005040302a0a1a2a3a4a50019 6f8a12f7bf8d4dc5a1196e95dff0b427 | ||
6f8212f6bd8e49c0a71e6e95dff0b427 37e9b78cc22017e73380430cbef42824 | ||
3fe0bd87ce2d19e82391511faae13e33 90ca05139f4d4ecf226fe981c59e2d40 | ||
88d31f08835050d0026fe981c59e2d40 73b46775c026deaa410397d670fe5fb0 | ||
0100000006050403a0a1a2a3a4a50000 dd872a807c75f84e296bb883a30ce22f | ||
0100000006050403a0a1a2a3a4a50001 ae81666a838b886aeebf4a5b3284508a | ||
0100000006050403a0a1a2a3a4a50002 d1b19206ac939e2fb6ddce10a774fd8d | ||
5900000006050403a0a1a2a3a4a50013 06652c600ef58963cac325a9cd3e2be1 | ||
06692c610cf68d66ccc42da0c7352be1 a07509ac15c25886042f806054fea686 | ||
ac7807a305d34a95103a96774ce7bc9d 644c0990d91b83e9ab4b8eed066ff5bf | ||
78511790d91b83e9ab4b8eed066ff5bf 4b4f4b39b593e6bfb0b2c2b70f29cd7a |