phpseclib asn1 parser issue with MS cert SAN - x509
I am attempting to parse some M$ generated certs and finding that the phpseclib ASN1 decodeBER function is choking on some OID's. I would really like to understand this function better and its behavior in this case.
Here is an example cert for discussion:
-----BEGIN CERTIFICATE-----
MIIG1jCCBL6gAwIBAgITUAAAAA0qg8bE6DhrLAAAAAAADTANBgkqhkiG9w0BAQsF
ADAiMSAwHgYDVQQDExcuU2VjdXJlIEVudGVycHJpc2UgQ0EgMTAeFw0xNTAyMjMx
NTE1MDdaFw0xNjAyMjMxNTE1MDdaMD8xFjAUBgoJkiaJk/IsZAEZFgZzZWN1cmUx
DjAMBgNVBAMTBVVzZXJzMRUwEwYDVQQDEwxtZXRhY2xhc3NpbmcwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMdG1CzR/gTalbLN9J+2cvMGeD7wsR7S78
HU5hdwE+kECROjRAcjFBOR57ezSDrkmhkTzo28tj0oAHjOh8N9vuXtASfZSCXugx
H+ImJ+E7PA4aXBp+0H2hohW9sXNNCFiVNmJLX66O4bxIeKtVRq/+eSNijV4OOEkC
zMyTHAUbOFP0t6KoJtM1syNoQ1+fKdfcjz5XtiEzSVcp2zf0MwNFSeZSgGQ0jh8A
Kd6YVKA8ZnrqOWZxKETT+bBNTjIT0ggjQfzcE4zW2RzrN7zWabUowoU92+DAp4s3
sAEywX9ISSge62DEzTnZZSf9bpoScAfT8raRFA3BkoJ/s4c4CgfPAgMBAAGjggLm
MIIC4jAdBgNVHQ4EFgQULlIyJL9+ZwAI/SkVdsJMxFOVp+EwHwYDVR0jBBgwFoAU
5nEIMEUT5mMd1WepmviwgK7dIzwwggEKBgNVHR8EggEBMIH+MIH7oIH4oIH1hoG5
bGRhcDovLy9DTj0uU2VjdXJlJTIwRW50ZXJwcmlzZSUyMENBJTIwMSxDTj1hdXRo
LENOPUNEUCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxD
Tj1Db25maWd1cmF0aW9uLERDPXNlY3VyZT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25M
aXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnSGN2h0dHA6
Ly9jcmwuc2VjdXJlb2JzY3VyZS5jb20vP2FjdGlvbj1jcmwmY2E9ZW50ZXJwcmlz
ZTEwgccGCCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049
LlNlY3VyZSUyMEVudGVycHJpc2UlMjBDQSUyMDEsQ049QUlBLENOPVB1YmxpYyUy
MEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9
c2VjdXJlP2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0
aW9uQXV0aG9yaXR5MBcGCSsGAQQBgjcUAgQKHggAVQBzAGUAcjAOBgNVHQ8BAf8E
BAMCBaAwKQYDVR0lBCIwIAYKKwYBBAGCNwoDBAYIKwYBBQUHAwQGCCsGAQUFBwMC
MC4GA1UdEQQnMCWgIwYKKwYBBAGCNxQCA6AVDBNtZXRhY2xhc3NpbmdAc2VjdXJl
MEQGCSqGSIb3DQEJDwQ3MDUwDgYIKoZIhvcNAwICAgCAMA4GCCqGSIb3DQMEAgIA
gDAHBgUrDgMCBzAKBggqhkiG9w0DBzANBgkqhkiG9w0BAQsFAAOCAgEAKNmjYh+h
cObJEM0CWgz50jOYKZ4M5iIxoAWgrYY9Pv+0O9aPjvPLzjd5bY322L8lxh5wy5my
DKmip+irzjdVdxzQfoyy+ceODmCbX9L6MfEDn0RBzdwjLe1/eOxE1na0sZztrVCc
yt5nI91NNGZJUcVqVQsIA/25FWlkvo/FTfuqTuXdQiEVM5MCKJI915anmTdugy+G
0CmBJALIxtyz5P7sZhaHZFNdpKnx82QsauErqjP9H0RXc6VXX5qt+tEDvYfSlFcc
0lv3aQnV/eIdfm7APJkQ3lmNWWQwdkVf7adXJ7KAAPHSt1yvSbVxThJR/jmIkyeQ
XW/TOP5m7JI/GrmvdlzI1AgwJ+zO8fOmCDuif99pDb1CvkzQ65RZ8p5J1ZV6hzlb
VvOhn4LDnT1jnTcEqigmx1gxM/5ifvMorXn/ItMjKPlb72vHpeF7OeKE8GHsvZAm
osHcKyJXbTIcXchmpZX1efbmCMJBqHgJ/qBTBMl9BX0+YqbTZyabRJSs9ezbTRn0
oRYl21Q8EnvS71CemxEUkSsKJmfJKkQNCsOjc8AbX/V/X9R7LJkH3UEx6K2zQQKK
k6m17mi63YW/+iPCGOWZ2qXmY5HPEyyF2L4L4IDryFJ+8xLyw3pH9/yp5aHZDtp6
833K6qyjgHJT+fUzSEYpiwF5rSBJIGClOCY=
-----END CERTIFICATE-----
For simple testing purposes this is my driver:
$X509 = new File_X509();
$BER = $X509->_extractBER($BER);
$ASN1 = new File_ASN1();
$ASN1->loadOIDs($X509->oids);
$DECODED = $ASN1->decodeBER( $BER );
Utility::dumper($DECODED);
I think we are running into trouble ~line 423:
case FILE_ASN1_TYPE_OCTET_STRING:
if (!$constructed) {
$current['content'] = $content;
} else {
$current['content'] = '';
$length = 0;
while (substr($content, 0, 2) != "\0\0") {
$temp = $this->_decode_ber($content, $length + $start);
$this->_string_shift($content, $temp['length']);
// all subtags should be octet strings
//if ($temp['type'] != FILE_ASN1_TYPE_OCTET_STRING) {
// return false;
//}
$current['content'].= $temp['content'];
$length+= $temp['length'];
}
if (substr($content, 0, 2) == "\0\0") {
$length+= 2; // +2 for the EOC
}
}
break;
Specifically in dumping the output of array element 7 (subjectAltName)
[7] => Array
(
[start] => 1104
[headerlength] => 2
[type] => 16
[content] => Array
(
[0] => Array
(
[start] => 1106
[headerlength] => 2
[type] => 6
[content] => 2.5.29.17
[length] => 5
)
[1] => Array
(
[start] => 1111
[headerlength] => 2
[type] => 4
[content] => 0% #^F+^F^A^D^A^Â7^T^B^C ^U^L^Smetaclassing#secure
[length] => 41
)
)
[length] => 48
)
It appears that the content should be parsed into an array instead of a string of binary junk. I believe it should parse into something more like this:
SEQUENCE(2 elem)
OBJECT IDENTIFIER2.5.29.17
OCTET STRING(1 elem)
SEQUENCE(1 elem)
Offset: 1113
Length: 2+37
(constructed)
Value:
(1 elem)
[0](2 elem)
OBJECT IDENTIFIER1.3.6.1.4.1.311.20.2.3
[0](1 elem)
UTF8String metaclassing#secure
I believe this is a "constructed" field, however up at line ~298 the bitwise operation happening is a little beyond my understanding:
$constructed = ($type >> 5) & 1;
I have written a corner-case matching hex values to force $constructed to 1 for this specific array element but didnt see much of an improvement in parsing. Wondering what the next best step is to remedy this? I really appreciate your help and thoughts. Thanks!
you should attempt to recurse OCTET_STRING contents and attempt to decode the nested record to see if it decodes as valid ASN.1. All certificate extension values are placed as a nested to OCTET_STRING structure:
From what I know, the following primitive tags are used in primitive form only: BOOLEAN, INTEGER, NULL, OBJECT_IDENTIFIER, REAL, ENUMERATED and RELATIVE_OID. The rest primitive tags may have embedded content. Although, this list is not definitive, OCTET_STRING definitely may be a container even if its tag is encoded in a primitive form. This applies to BIT_STRING as well.
Yes indeed, the code should recur. Here are my findings thus far:
$class = ($type >> 6) & 3;
The issue does appear to be the bitwise operation being used, it is not correctly identifying class (is returning 0 for application when it should be 2 for context specific and 0 for constructed which should be 1.
To test I inserted this code below (really hackish, but im an ASN.1n00b:
// Manual testing for incorrect class/constructed detection!
if ( !$constructed && $class == 0 && isset($content[0]) && isset($content[1]) )
{
//print "\nSTART: $start\tTYPE IS: {$type}\t CLASS IS: {$class}\tCONTENT IS {$content}\n";
if ( ($content[0] == "0" && $content[1] == "%" ) ||
($content[0] == hex2bin("30") && $content[1] == hex2bin("81") ) ||
($content[0] == hex2bin("82") && $content[1] == hex2bin("01") ) ||
($content[0] == hex2bin("30") && $content[1] == hex2bin("20") ) ||
($content[0] == hex2bin("1e") && $content[1] == hex2bin("08") ) ||
($content[0] == hex2bin("00") && $content[1] == hex2bin("55") ) ||
($content[0] == hex2bin("04") && $content[1] == hex2bin("14") ) ||
($content[0] == hex2bin("30") && $content[1] == hex2bin("16") ) ||
($content[0] == hex2bin("00") && $content[1] == hex2bin("30") ) )
{
$class = 2; $constructed = 1; //print "SET CLASS AND CONSTRUCTED!\n";
}else{
//print "C0: {$content[0]} = (0x" . bin2hex($content[0]) . ") C1: {$content[1]} = (0x" . bin2hex($content[1]) . ") did not match!\n";
}
}
And indeed, manually matching the bytes not correctly detected does resolve my issue:
[7] => Array
(
[start] => 1104
[headerlength] => 2
[type] => 16
[content] => Array
(
[0] => Array
(
[start] => 1106
[headerlength] => 2
[type] => 6
[content] => 2.5.29.17
[length] => 5
)
[1] => Array
(
[type] => 2
[constant] => 4
[content] => Array
(
[0] => Array
(
[start] => 1113
[headerlength] => 2
[type] => 16
[content] => Array
(
[0] => Array
(
[type] => 2
[constant] => 0
[content] => Array
(
[0] => Array
(
[start] => 1117
[headerlength] => 2
[type] => 6
[content] => 1.3.6.1.4.1.311.20.2.3
[length] => 12
)
)
[length] => 14
[start] => 1115
[headerlength] => 2
)
[1] => Array
(
[type] => 2
[constant] => 0
[content] => Array
(
[0] => Array
(
[start] => 1131
[headerlength] => 2
[type] => 12
[content] => metaclassing#secure
[length] => 21
)
)
[length] => 23
[start] => 1129
[headerlength] => 2
)
)
[length] => 39
)
)
[length] => 41
[start] => 1111
[headerlength] => 2
)
)
[length] => 48
)
At this point I think I need to wait for the library developers to review the ASN.1 parsing function/library and review why these certs make it so incredibly unhappy. Patching this is somewhat beyond my capabilities at the moment.
Related
What's the size of a HMACSHA2 hash in Crypt::PBKDF2 in Perl?
I want to store password hashes in a database. Hashes will be generated with my $PBKDF2 = Crypt::PBKDF2->new( hash_class => 'HMACSHA2', hash_args => { sha_size => 512, }, iterations => 10000, salt_len => 10, ); In the Pod of Crypt::PBKDF2 I find: The default size (in bytes, not bits) of the output hash. If a value isn't provided, the output size depends on the hash_class / hasher selected, and will equal the output size of the backend hash (e.g. 20 bytes for HMACSHA1). But what actually IS the default output size?
32 bytes You can find this information in the source code of Crypt::PBKDF2::Hash::HMACSHA2. The code defining the default size is: has 'sha_size' => ( is => 'ro', isa => Type::Tiny->new( name => 'SHASize', parent => Enum[qw( 224 256 384 512 )], display_name => 'valid number of bits for SHA-2', ), default => 256, ); The function used to return the size divides sha_size by 8: sub hash_len { my $self = shift; return $self->sha_size() / 8; } Thus returning 256/8 = 32 by default.
text in result instead of number returns mysqli unknown column error
print_r($cfs); Array ( [0] => Array ( [0] => MST Ice P [1] => MST [2] => Ice ) [1] => Array ( [0] => MST Ice P [1] => MST [2] => Ice ) [2] => Array ( [0] => MST 298 P [1] => MST [2] => 298 ) [3] => Array ( [0] => MST Ice P [1] => MST [2] => Ice ) [4] => Array ( [0] => MST 302 P [1] => MST [2] => 302 ) ) here is my mysqli: for($i = 0; $i < count($cfs); $i++) { $con->query( "UPDATE gages SET cfs = {$cfs[$i][2]}, modtime = now() WHERE usgs = {$stano[$i][1]}") or die($con->error); } Here is my error: Unknown column 'Ice' in 'field list' This works fine in the summer when there in no ice.
If as you say the cfs column is VARCHAR then it should have been coded like this with single quotes around the value. I dont see how it ever worked without them. for($i = 0; $i < count($cfs); $i++) { $con->query("UPDATE gages SET cfs = '{$cfs[$i][2]}', modtime = now() WHERE usgs = {$stano[$i][1]}") or die($con->error); }
Escaping commas in macro output
I am trying to write a macro which enables me to transform (a, b, c, d) to (a, a + b, a + b + c, a + b + c + d), etc. Here is what I have got so far: macro_rules! pascal_next { ($x: expr) => ($x); ($x: expr, $y: expr) => ( ($x, $x + $y) ); ($x: expr, $y: expr, $($rest: expr),+) => ( ($x, pascal_next!( $x + $y, $($rest),+ ) ) ); } However, there is a problem that it would actually output (a, (a + b, (a + b + c, a + b + c +d))). The origin is that the second matching rule ($x: expr, $y: expr) => (($x, $x + $y));, produces an extra bracket, so that there would be nested brackets. If I don't put a bracket outside, I would get the error error: unexpected token: , So is it possible to output a comma , in Rust macros?
No; the result of a macro must be a complete grammar construct like an expression or an item. You absolutely cannot have random bits of syntax like a comma or a closing brace. You can get around this by simply not outputting anything until you have a complete, final expression. Behold! #![feature(trace_macros)] macro_rules! pascal_impl { /* The input to this macro takes the following form: ```ignore ( // The current output accumulator. ($($out:tt)*); // The current additive prefix. $prefix:expr; // The remaining, comma-terminated elements. ... ) ``` */ /* Termination condition: there is no input left. As such, dump the output. */ ( $out:expr; $_prefix:expr; ) => { $out }; /* Otherwise, we have more to scrape! */ ( ($($out:tt)*); $prefix:expr; $e:expr, $($rest:tt)* ) => { pascal_impl!( ($($out)* $prefix+$e,); $prefix+$e; $($rest)* ) }; } macro_rules! pascal { ($($es:expr),+) => { pascal_impl!((); 0; $($es),+,) }; } trace_macros!(true); fn main() { println!("{:?}", pascal!(1, 2, 3, 4)); } Note: To use this on a stable compiler, you will need to delete the #![feature(trace_macros)] and trace_macros!(true); lines. Everything else should be fine. What this does is it recursively munches away at the input, passing the partial (and potentially semantically invalid) output as input to the next level of recursion. This lets us build up an "open list", which we couldn't otherwise do. Then, once we're out of input, we just re-interpret our partial output as a complete expression and... done. The reason I including the tracing stuff is so I could show you what it looks like as it runs: pascal! { 1 , 2 , 3 , 4 } pascal_impl! { ( ) ; 0 ; 1 , 2 , 3 , 4 , } pascal_impl! { ( 0 + 1 , ) ; 0 + 1 ; 2 , 3 , 4 , } pascal_impl! { ( 0 + 1 , 0 + 1 + 2 , ) ; 0 + 1 + 2 ; 3 , 4 , } pascal_impl! { ( 0 + 1 , 0 + 1 + 2 , 0 + 1 + 2 + 3 , ) ; 0 + 1 + 2 + 3 ; 4 , } pascal_impl! { ( 0 + 1 , 0 + 1 + 2 , 0 + 1 + 2 + 3 , 0 + 1 + 2 + 3 + 4 , ) ; 0 + 1 + 2 + 3 + 4 ; } And the output is: (1, 3, 6, 10) One thing to be aware of: large numbers of un-annotated integer literals can cause a dramatic increase in compile times. If this happens, you can solve it by simply annotating all of your integer literals (like 1i32).
I have a really confusing issue with checkboxes
I have a problem with the value that checkboxes returns to me: first of all i start with that 2 variables: $blabla_severity_levels = variable_get('blabla_severity_levels', array()); $blabla_severity_options = array(); then i make a for each for the watchdogs: foreach (watchdog_severity_levels() as $severity_numbers => $severity) { $blabla_severity_options[$severity_numbers] = check_plain(drupal_ucfirst($severity)); } then i generate the form: $blabla_form['blabla_severity_levels'] = array( '#type' => 'checkboxes', '#title' => t('Type of log messages'), '#options' => $blabla_severity_options, '#default_value' => array_values($blabla_severity_levels), '#required' => TRUE, ); return system_settings_form($blabla_form); and in my function i call it like: $severity_levels = variable_get('blabla_severity_levels', array()); now if i make a dpm($severity_levels) it gets me out an array with error (String, 5 characters ) error warning (String, 7 characters ) warning emergency (Integer) 0 alert (Integer) 0 critical (Integer) 0 notice (Integer) 0 info (Integer) 0 debug (Integer) 0 but i am really needing is the blabla_severity_options which is: error (String, 5 characters ) 3 warning (String, 7 characters ) 4 emergency (Integer) 0 alert (Integer) 0 critical (Integer) 0 notice (Integer) 0 info (Integer) 0 debug (Integer) 0 I want to save the values...... any ideas plz?? i really need help
MongoDB does not return all points in MaxDistance
I have this search query in php: [$near] => Array( [0] => -73.5539925 [1] => 45.5086699 ) [$maxDistance] => 0.269978401728 0.269978401728 was calculated this way: 30/111.12 problem is that it doesn't find point with these values: -73.8260706 45.4293058 it finds it only if distance is increased to 32. If I check gmap walking distance between these points is 26.6 which is longer than direct line. Then even with 25km it should probably work, but now it doesnt work with 30. Do I miss something? Thanks
I changed query to use within instead of near and now it works fine $radiusOfEarth = 6378.1;//radius in km $calculated_distance = (float) $distance / $radiusOfEarth; $search = array( 'loc' => array('$within' => array('$centerSphere' => array( array(floatval($lng), floatval($lat)), $calculated_distance ) ) ) ); or [loc] => Array ( [$within] => Array ( [$centerSphere] => Array ( [0] => Array ( [0] => -73.5539925 [1] => 45.5086699 ) [1] => 0.00501716812217 ) ) )