new: Support for BECH32 (P2WPKH) BTC address

pull/7780/head
Jakub Onderka 2021-09-27 23:36:35 +02:00
parent d0f9fa8ea0
commit 871ca92c48
7 changed files with 117 additions and 120 deletions

View File

@ -217,9 +217,7 @@ jobs:
- name: Run PHP tests
run: |
./app/Vendor/bin/parallel-lint --exclude app/Lib/cakephp/ --exclude app/Vendor/ --exclude app/Lib/random_compat/ -e php,ctp app/
./app/Vendor/bin/phpunit app/Test/ComplexTypeToolTest.php
./app/Vendor/bin/phpunit app/Test/JSONConverterToolTest.php
./app/Vendor/bin/phpunit app/Test/CidrToolTest.php
./app/Vendor/bin/phpunit app/Test/
- name: Run tests
run: |

View File

@ -274,8 +274,13 @@ class ComplexTypeTool
private function __checkForBTC($input)
{
if (preg_match("#^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$#i", $input['raw'])) {
return array('types' => array('btc'), 'categories' => array('Financial fraud'), 'to_ids' => true, 'default_type' => 'btc', 'value' => $input['raw']);
if (preg_match("#^([13][a-km-zA-HJ-NP-Z1-9]{25,34})|(bc|tb)1([023456789acdefghjklmnpqrstuvwxyz]{11,71})$#i", $input['raw'])) {
return [
'types' => ['btc'],
'to_ids' => true,
'default_type' => 'btc',
'value' => $input['raw'],
];
}
return false;
}
@ -305,7 +310,7 @@ class ComplexTypeTool
// handle prepared composite values with the filename|hash format
if (strpos($input['raw'], '|')) {
$compositeParts = explode('|', $input['raw']);
if (count($compositeParts) == 2) {
if (count($compositeParts) === 2) {
if ($this->__resolveFilename($compositeParts[0])) {
$hash = $this->__resolveHash($compositeParts[1]);
if ($hash) {
@ -372,7 +377,6 @@ class ComplexTypeTool
if (preg_match("#^cve-[0-9]{4}-[0-9]{4,9}$#i", $input['raw'])) {
return [
'types' => ['vulnerability'],
'categories' => ['External analysis'],
'to_ids' => false,
'default_type' => 'vulnerability',
'value' => strtoupper($input['raw']), // 'CVE' must be uppercase
@ -381,7 +385,7 @@ class ComplexTypeTool
// Phone numbers - for automatic recognition, needs to start with + or include dashes
if ($input['raw'][0] === '+' || strpos($input['raw'], '-')) {
if (!preg_match('#^[0-9]{4}-[0-9]{2}-[0-9]{2}$#i', $input['raw']) && preg_match("#^(\+)?([0-9]{1,3}(\(0\))?)?[0-9\/\-]{5,}[0-9]$#i", $input['raw'])) {
return array('types' => array('phone-number', 'prtn', 'whois-registrant-phone'), 'categories' => array('Other'), 'to_ids' => false, 'default_type' => 'phone-number', 'value' => $input['raw']);
return array('types' => array('phone-number', 'prtn', 'whois-registrant-phone'), 'to_ids' => false, 'default_type' => 'phone-number', 'value' => $input['raw']);
}
}
return false;
@ -471,7 +475,7 @@ class ComplexTypeTool
$temp = explode('\\', $input['raw']);
if (strpos(end($temp), '.') || preg_match('/^.:/i', $temp[0])) {
if ($this->__resolveFilename(end($temp))) {
return array('types' => array('filename'), 'categories' => array('Payload installation'), 'to_ids' => true, 'default_type' => 'filename', 'value' => $input['raw']);
return array('types' => array('filename'), 'to_ids' => true, 'default_type' => 'filename', 'value' => $input['raw']);
}
} else if (!empty($temp[0])) {
return array('types' => array('regkey'), 'to_ids' => false, 'default_type' => 'regkey', 'value' => $input['raw']);

View File

@ -89,6 +89,22 @@ class FinancialTool
'XK' => '20'
);
const BTC_BASE32_CONVERT_TABLE = [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
];
/**
* @param string $type
* @param string $value
* @return bool
*/
public function validateRouter($type, $value)
{
$validationRoutes = array(
@ -99,8 +115,8 @@ class FinancialTool
'btc' => 'BTC',
'xmr' => 'XMR'
);
if (in_array($type, array_keys($validationRoutes))) {
return $this->{'validate' . strtoupper($validationRoutes[$type])}($value);
if (isset($validationRoutes[$type])) {
return $this->{'validate' . $validationRoutes[$type]}($value);
}
return true;
}
@ -129,7 +145,6 @@ class FinancialTool
return (int)$mod;
}
// validating using method described on wikipedia @ https://en.wikipedia.org/wiki/International_Bank_Account_Number#Algorithms
public function validateIBAN($iban)
{
@ -197,11 +212,20 @@ class FinancialTool
return false;
}
// based on the php implementation of the BTC address validation example from
// http://rosettacode.org/wiki/Bitcoin/address_validation
/**
* Based on the php implementation of the BTC address validation example from
* http://rosettacode.org/wiki/Bitcoin/address_validation
* @param string $address
* @return bool
*/
public function validateBTC($address)
{
if (strlen($address) < 26 || strlen($address) > 35) {
if ($this->validateBtcBech32($address)) {
return true;
}
$strlen = strlen($address);
if ($strlen < 26 || $strlen > 35) {
return false;
}
$decoded = $this->__decodeBase58($address);
@ -226,12 +250,48 @@ class FinancialTool
return true;
}
private function validateBtcBech32($address)
{
if (!preg_match('/^(bc|tb)1([023456789acdefghjklmnpqrstuvwxyz]{11,71})$/i', $address, $match)) {
return false;
}
$hrp = strtolower($match[1]);
$expand1 = [];
$expand2 = [];
for ($i = 0; $i < strlen($hrp); $i++) {
$o = ord($hrp[$i]);
$expand1[] = $o >> 5;
$expand2[] = $o & 31;
}
$data = array_merge($expand1, [0], $expand2);
$chars = strtolower($match[2]);
$chars = array_map('ord', str_split($chars));
foreach ($chars as $char) {
$data[] = ($char & 0x80) ? -1 : self::BTC_BASE32_CONVERT_TABLE[$char];
}
$generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
$chk = 1;
foreach ($data as $char) {
$top = $chk >> 25;
$chk = ($chk & 0x1ffffff) << 5 ^ $char;
for ($j = 0; $j < 5; $j++) {
$value = (($top >> $j) & 1) ? $generator[$j] : 0;
$chk ^= $value;
}
}
return $chk === 1;
}
private function __decodeBase58($input)
{
$alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
$out = array_fill(0, 25, 0);
for ($i=0;$i<strlen($input);$i++) {
for ($i = 0; $i < strlen($input); $i++) {
$p = strpos($alphabet, $input[$i]);
if ($p === false) {
return false;
@ -239,7 +299,7 @@ class FinancialTool
$c = $p;
for ($j = 25; $j--;) {
$c += (int)(58 * $out[$j]);
$out[$j] = (int)($c % 256);
$out[$j] = $c % 256;
$c /= 256;
$c = (int)$c;
}

View File

@ -1,47 +0,0 @@
<?php
App::uses('ThreatLevel', 'Model');
/**
* ThreatLevel Test Case
*
*/
class ThreatLevelTest extends CakeTestCase {
/**
* Fixtures
*
* @var array
*/
public $fixtures = array(
'app.threat_level',
'app.event',
'app.user',
'app.role',
'app.post',
'app.thread',
'app.attribute',
'app.shadow_attribute'
);
/**
* setUp method
*
* @return void
*/
public function setUp() {
parent::setUp();
$this->ThreatLevel = ClassRegistry::init('ThreatLevel');
}
/**
* tearDown method
*
* @return void
*/
public function tearDown() {
unset($this->ThreatLevel);
parent::tearDown();
}
}

View File

@ -411,6 +411,27 @@ EOT;
$this->assertEquals('btc', $results[0]['default_type']);
}
public function testCheckFreeTextBtcBech32(): void
{
$complexTypeTool = new ComplexTypeTool();
$validAddresses = [
'BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4',
'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7',
'bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx',
'BC1SW50QA3JX3S',
'bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj',
'tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy',
];
foreach ($validAddresses as $validAddress) {
$results = $complexTypeTool->checkFreeText($validAddress);
$this->assertCount(1, $results);
$this->assertEquals($validAddress, $results[0]['value']);
$this->assertEquals('btc', $results[0]['default_type']);
}
}
public function testCheckFreeTextSsdeep(): void
{
$complexTypeTool = new ComplexTypeTool();

View File

@ -0,0 +1,17 @@
<?php
require_once __DIR__ . '/../Lib/Tools/FinancialTool.php';
use PHPUnit\Framework\TestCase;
class FinancialToolTest extends TestCase
{
public function testValidateBtc(): void
{
$financialTool = new FinancialTool();
$this->assertTrue($financialTool->validateBTC('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'), 'P2PKH');
$this->assertTrue($financialTool->validateBTC('3GRdnTq18LyNveWa1gQJcgp8qEnzijv5vR'), 'P2SH');
$this->assertTrue($financialTool->validateBTC('bc1qnkyhslv83yyp0q0suxw0uj3lg9drgqq9c0auzc'), 'P2WPKH');
$this->assertTrue($financialTool->validateBTC('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq'), 'P2WPKH');
$this->assertTrue($financialTool->validateBTC('bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9'), 'P2WPKH');
}
}

View File

@ -1,56 +0,0 @@
<?php
/**
* ThreatLevelFixture
*
*/
class ThreatLevelFixture extends CakeTestFixture {
/**
* Fields
*
* @var array
*/
public $fields = array(
'id' => array('type' => 'boolean', 'null' => false, 'default' => null, 'key' => 'primary'),
'name' => array('type' => 'string', 'null' => false, 'length' => 50, 'collate' => 'utf8_bin', 'charset' => 'utf8'),
'description' => array('type' => 'string', 'null' => true, 'default' => null, 'collate' => 'utf8_bin', 'charset' => 'utf8'),
'form_description' => array('type' => 'string', 'null' => false, 'collate' => 'utf8_bin', 'charset' => 'utf8'),
'indexes' => array(
'PRIMARY' => array('column' => 'id', 'unique' => 1)
),
'tableParameters' => array('charset' => 'latin1', 'collate' => 'latin1_swedish_ci', 'engine' => 'InnoDB')
);
/**
* Records
*
* @var array
*/
public $records = array(
array(
'id' => 1,
'name' => 'High',
'description' => '*high* means sophisticated APT malware or 0-day attack',
'form_description' => 'Sophisticated APT malware or 0-day attack'
),
array(
'id' => 2,
'name' => 'Medium',
'description' => '*medium* means APT malware',
'form_description' => 'APT malware'
),
array(
'id' => 3,
'name' => 'Low',
'description' => '*low* means mass-malware',
'form_description' => 'Mass-malware'
),
array(
'id' => 4,
'name' => 'Undefined',
'description' => '*undefined* no risk',
'form_description' => 'No risk'
),
);
}