diff options
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | crypto/build.info | 2 | ||||
-rw-r--r-- | crypto/err/err_all.c | 4 | ||||
-rw-r--r-- | crypto/err/openssl.ec | 1 | ||||
-rw-r--r-- | crypto/lhash/lhash.c | 12 | ||||
-rw-r--r-- | crypto/property/README | 87 | ||||
-rw-r--r-- | crypto/property/build.info | 3 | ||||
-rw-r--r-- | crypto/property/defn_cache.c | 105 | ||||
-rw-r--r-- | crypto/property/properties.ebnf | 17 | ||||
-rw-r--r-- | crypto/property/properties.xhtml | 685 | ||||
-rw-r--r-- | crypto/property/property.c | 508 | ||||
-rw-r--r-- | crypto/property/property_lcl.h | 51 | ||||
-rw-r--r-- | crypto/property/property_parse.c | 545 | ||||
-rw-r--r-- | crypto/property/property_string.c | 137 | ||||
-rw-r--r-- | crypto/sparse_array.c | 2 | ||||
-rw-r--r-- | doc/internal/man3/OSSL_METHOD_STORE.pod | 103 | ||||
-rw-r--r-- | doc/man3/OPENSSL_LH_COMPFUNC.pod | 11 | ||||
-rw-r--r-- | include/internal/property.h | 35 | ||||
-rw-r--r-- | include/openssl/err.h | 2 | ||||
-rw-r--r-- | include/openssl/lhash.h | 5 | ||||
-rw-r--r-- | include/openssl/stack.h | 2 | ||||
-rw-r--r-- | test/build.info | 6 | ||||
-rw-r--r-- | test/property_test.c | 374 | ||||
-rw-r--r-- | test/recipes/03-test_property.t | 12 | ||||
-rw-r--r-- | test/sparse_array_test.c | 2 |
25 files changed, 2705 insertions, 10 deletions
@@ -9,6 +9,10 @@ Changes between 1.1.1 and 3.0.0 [xx XXX xxxx] + *) Added property based algorithm implementation selection framework to + the core. + [Paul Dale] + *) Added SCA hardening for modular field inversion in EC_GROUP through a new dedicated field_inv() pointer in EC_METHOD. This also addresses a leakage affecting conversions from projective diff --git a/crypto/build.info b/crypto/build.info index fc0050ea4e..94ed06e65f 100644 --- a/crypto/build.info +++ b/crypto/build.info @@ -1,7 +1,7 @@ # Note that these directories are filtered in Configure. Look for %skipdir # there for further explanations. SUBDIRS=objects buffer bio stack lhash rand evp asn1 pem x509 x509v3 conf \ - txt_db pkcs7 pkcs12 ui kdf store \ + txt_db pkcs7 pkcs12 ui kdf store property \ md2 md4 md5 sha mdc2 gmac hmac ripemd whrlpool poly1305 blake2 \ siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \ seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \ diff --git a/crypto/err/err_all.c b/crypto/err/err_all.c index 3911ecc5c9..4bf020c281 100644 --- a/crypto/err/err_all.c +++ b/crypto/err/err_all.c @@ -39,6 +39,7 @@ #include <openssl/kdferr.h> #include <openssl/storeerr.h> #include <openssl/esserr.h> +#include "internal/propertyerr.h" int err_load_crypto_strings_int(void) { @@ -96,7 +97,8 @@ int err_load_crypto_strings_int(void) ERR_load_ASYNC_strings() == 0 || #endif ERR_load_KDF_strings() == 0 || - ERR_load_OSSL_STORE_strings() == 0) + ERR_load_OSSL_STORE_strings() == 0 || + ERR_load_PROP_strings() == 0) return 0; return 1; diff --git a/crypto/err/openssl.ec b/crypto/err/openssl.ec index 94d46d067b..901a847d29 100644 --- a/crypto/err/openssl.ec +++ b/crypto/err/openssl.ec @@ -35,6 +35,7 @@ L KDF include/openssl/kdf.h crypto/kdf/kdf_err.c L SM2 crypto/include/internal/sm2.h crypto/sm2/sm2_err.c L OSSL_STORE include/openssl/store.h crypto/store/store_err.c L ESS include/openssl/ess.h crypto/ess/ess_err.c +L PROP include/internal/property.h crypto/property/property_err.c # additional header files to be scanned for function names L NONE include/openssl/x509_vfy.h NONE diff --git a/crypto/lhash/lhash.c b/crypto/lhash/lhash.c index c826039807..aa0ca1c957 100644 --- a/crypto/lhash/lhash.c +++ b/crypto/lhash/lhash.c @@ -75,6 +75,16 @@ err: void OPENSSL_LH_free(OPENSSL_LHASH *lh) { + if (lh == NULL) + return; + + OPENSSL_LH_flush(lh); + OPENSSL_free(lh->b); + OPENSSL_free(lh); +} + +void OPENSSL_LH_flush(OPENSSL_LHASH *lh) +{ unsigned int i; OPENSSL_LH_NODE *n, *nn; @@ -89,8 +99,6 @@ void OPENSSL_LH_free(OPENSSL_LHASH *lh) n = nn; } } - OPENSSL_free(lh->b); - OPENSSL_free(lh); } void *OPENSSL_LH_insert(OPENSSL_LHASH *lh, void *data) diff --git a/crypto/property/README b/crypto/property/README new file mode 100644 index 0000000000..b3f56cfa2f --- /dev/null +++ b/crypto/property/README @@ -0,0 +1,87 @@ +Properties are associated with algorithms and are used to select between different implementations dynamically. + +This implementation is based on a number of assumptions: + +* Property definition is uncommon. I.e. providers will be loaded and + unloaded relatively infrequently, if at all. + +* The number of distinct property names will be small. + +* Providers will often give the same implementation properties to most or + all of their implemented algorithms. E.g. the FIPS property would be set + across an entire provider. Likewise for, hardware, accelerated, software, + HSM and, perhaps, constant_time. + +* There are a lot of algorithm implementations, therefore property + definitions should be space efficient. However... + +* ... property queries are very common. These must be fast. + +* Property queries come from a small set and are reused many times typically. + I.e. an application tends to use the same set of queries over and over, + rather than spanning a wide variety of queries. + +* Property queries can never add new property definitions. + + +Some consequences of these assumptions are: + +* That definition is uncommon and queries are very common, we can treat + the property definitions as almost immutable. Specifically, a query can + never change the state of the definitions. + +* That definition is uncommon and needs to be space efficient, it will + be feasible to use a hash table to contain the names (and possibly also + values) of all properties and to reference these instead of duplicating + strings. Moreover, such a data structure need not be garbage collected. + By converting strings to integers using a structure such as this, string + comparison degenerates to integer comparison. Additionally, lists of + properties can be sorted by the string index which makes comparisons linear + time rather than quadratic time - the O(n log n) sort cost being amortised. + +* A cache for property definitions is also viable, if only implementation + properties are used and not algorithm properties, or at least these are + maintained separately. This cache would be a hash table, indexed by + the property definition string, and algorithms with the same properties + would share their definition structure. Again, reducing space use. + +* A query cache is desirable. This would be a hash table keyed by the + algorithm identifier and the entire query string and it would map to + the chosen algorithm. When a provider is loaded or unloaded, this cache + must be invalidated. The cache will also be invalidated when the global + properties are changed as doing so removes the need to index on both the + global and requested property strings. + + +The implementation: + +* property_lock.c contains some wrapper functions to handle the global + lock more easily. The global lock is held for short periods of time with + per algorithm locking being used for longer intervals. + +* property_string.c contains the string cache which converts property + names and values to small integer indices. Names and values are stored in + separate hash tables. The two Boolean values, the strings "yes" and "no", + are populated as the first two members of the value table. All property + names reserved by OpenSSL are also populated here. No functions are + provided to convert from an index back to the original string (this can be + done by maintaining parallel stacks of strings if required). + +* property_parse.c contains the property definition and query parsers. + These convert ASCII strings into lists of properties. The resulting + lists are sorted by the name index. Some additional utility functions + for dealing with property lists are also included: comparison of a query + against a definition and merging two queries into a single larger query. + +* property.c contains the main APIs for defining and using properties. + Algorithms are discovered from their NID and a query string. + The results are cached. + + The caching of query results has to be efficient but it must also be robust + against a denial of service attack. The cache cannot be permitted to grow + without bounds and must garbage collect under-used entries. The garbage + collection does not have to be exact. + +* defn_cache.c contains a cache that maps property definition strings to + parsed properties. It is used by property.c to improve performance when + the same definition appears multiple times. diff --git a/crypto/property/build.info b/crypto/property/build.info new file mode 100644 index 0000000000..3a86b6e141 --- /dev/null +++ b/crypto/property/build.info @@ -0,0 +1,3 @@ +LIBS=../../libcrypto +SOURCE[../../libcrypto]=property_string.c property_parse.c property.c \ + property_err.c defn_cache.c diff --git a/crypto/property/defn_cache.c b/crypto/property/defn_cache.c new file mode 100644 index 0000000000..df87c19f55 --- /dev/null +++ b/crypto/property/defn_cache.c @@ -0,0 +1,105 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <string.h> +#include <openssl/err.h> +#include <openssl/lhash.h> +#include "internal/propertyerr.h" +#include "internal/property.h" +#include "property_lcl.h" + +/* + * Implement a property definition cache. + * These functions assume that they are called under a write lock. + * No attempt is made to clean out the cache, except when it is shut down. + */ + +typedef struct { + const char *prop; + OSSL_PROPERTY_LIST *defn; + char body[1]; +} PROPERTY_DEFN_ELEM; + +DEFINE_LHASH_OF(PROPERTY_DEFN_ELEM); + +static LHASH_OF(PROPERTY_DEFN_ELEM) *property_defns = NULL; + +static unsigned long property_defn_hash(const PROPERTY_DEFN_ELEM *a) +{ + return OPENSSL_LH_strhash(a->prop); +} + +static int property_defn_cmp(const PROPERTY_DEFN_ELEM *a, + const PROPERTY_DEFN_ELEM *b) +{ + return strcmp(a->prop, b->prop); +} + +static void property_defn_free(PROPERTY_DEFN_ELEM *elem) +{ + ossl_property_free(elem->defn); + OPENSSL_free(elem); +} + +int ossl_prop_defn_init(void) +{ + property_defns = lh_PROPERTY_DEFN_ELEM_new(&property_defn_hash, + &property_defn_cmp); + return property_defns != NULL; +} + +void ossl_prop_defn_cleanup(void) +{ + if (property_defns != NULL) { + lh_PROPERTY_DEFN_ELEM_doall(property_defns, &property_defn_free); + lh_PROPERTY_DEFN_ELEM_free(property_defns); + property_defns = NULL; + } +} + +OSSL_PROPERTY_LIST *ossl_prop_defn_get(const char *prop) +{ + PROPERTY_DEFN_ELEM elem, *r; + + elem.prop = prop; + r = lh_PROPERTY_DEFN_ELEM_retrieve(property_defns, &elem); + return r != NULL ? r->defn : NULL; +} + +int ossl_prop_defn_set(const char *prop, OSSL_PROPERTY_LIST *pl) +{ + PROPERTY_DEFN_ELEM elem, *old, *p = NULL; + size_t len; + + if (prop == NULL) + return 1; + + if (pl == NULL) { + elem.prop = prop; + lh_PROPERTY_DEFN_ELEM_delete(property_defns, &elem); + return 1; + } + len = strlen(prop); + p = OPENSSL_malloc(sizeof(*p) + len); + if (p != NULL) { + p->prop = p->body; + p->defn = pl; + memcpy(p->body, prop, len + 1); + old = lh_PROPERTY_DEFN_ELEM_insert(property_defns, p); + if (old != NULL) { + property_defn_free(old); + return 1; + } + if (!lh_PROPERTY_DEFN_ELEM_error(property_defns)) + return 1; + } + OPENSSL_free(p); + return 0; +} diff --git a/crypto/property/properties.ebnf b/crypto/property/properties.ebnf new file mode 100644 index 0000000000..9a6857f751 --- /dev/null +++ b/crypto/property/properties.ebnf @@ -0,0 +1,17 @@ +(* https://bottlecaps.de/rr/ui *) + +Definition + ::= PropertyName ( '=' Value )? ( ',' PropertyName ( '=' Value )? )* +Query ::= ( '-'? PropertyName | PropertyName ( '=' | '!=' ) Value ) + ( ',' ( '-'? PropertyName | PropertyName ( '=' | '!=' ) Value ) )* +Value ::= NumberLiteral + | StringLiteral +StringLiteral ::= QuotedString | UnquotedString +QuotedString ::= '"' [^"]* '"' + | "'" [^']* "'" +UnquotedString ::= [^{space},]+ +NumberLiteral + ::= '0' ( [0-7]* | 'x' [0-9A-Fa-f]+ ) + | '-'? [1-9] [0-9]+ +PropertyName + ::= [A-Z] [A-Z0-9_]* ( '.' [A-Z] [A-Z0-9_]* )* diff --git a/crypto/property/properties.xhtml b/crypto/property/properties.xhtml new file mode 100644 index 0000000000..33827226c0 --- /dev/null +++ b/crypto/property/properties.xhtml @@ -0,0 +1,685 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> + <meta name="generator" content="Railroad Diagram Generator 1.56.1774" /> + <style type="text/css"> + ::-moz-selection + { + color: #FFFCF0; + background: #0F0C00; + } + ::selection + { + color: #FFFCF0; + background: #0F0C00; + } + .ebnf a, .grammar a + { + text-decoration: none; + } + .ebnf a:hover, .grammar a:hover + { + color: #050400; + text-decoration: underline; + } + .signature + { + color: #806600; + font-size: 11px; + text-align: right; + } + body + { + font: normal 12px Verdana, sans-serif; + color: #0F0C00; + background: #FFFCF0; + } + a:link, a:visited + { + color: #0F0C00; + } + a:link.signature, a:visited.signature + { + color: #806600; + } + a.button, #tabs li a + { + padding: 0.25em 0.5em; + border: 1px solid #806600; + background: #F1E8C6; + color: #806600; + text-decoration: none; + font-weight: bold; + } + a.button:hover, #tabs li a:hover + { + color: #050400; + background: #FFF6D1; + border-color: #050400; + } + #tabs + { + padding: 3px 10px; + margin-left: 0; + margin-top: 58px; + border-bottom: 1px solid #0F0C00; + } + #tabs li + { + list-style: none; + margin-left: 5px; + display: inline; + } + #tabs li a + { + border-bottom: 1px solid #0F0C00; + } + #tabs li a.active + { + color: #0F0C00; + background: #FFFCF0; + border-color: #0F0C00; + border-bottom: 1px solid #FFFCF0; + outline: none; + } + #divs div + { + display: none; + overflow:auto; + } + #divs div.active + { + display: block; + } + #text + { + border-color: #806600; + background: #FFFEFA; + color: #050400; + } + .small + { + vertical-align: top; + text-align: right; + font-size: 9px; + font-weight: normal; + line-height: 120%; + } + td.small + { + padding-top: 0px; + } + .hidden + { + visibility: hidden; + } + td:hover .hidden + { + visibility: visible; + } + div.download + { + display: none; + background: #FFFCF0; + position: absolute; + right: 34px; + top: 94px; + padding: 10px; + border: 1px dotted #0F0C00; + } + #divs div.ebnf, .ebnf code + { + display: block; + padding: 10px; + background: #FFF6D1; + width: 1000px; + } + #divs div.grammar + { + display: block; + padding-left: 16px; + padding-top: 2px; + padding-bottom: 2px; + background: #FFF6D1; + } + pre + { + margin: 0px; + } + .ebnf div + { + padding-left: 13ch; + text-indent: -13ch; + } + .ebnf code, .grammar code, textarea, pre + { + font:12px SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace; + } + tr.option-line td:first-child + { + text-align: right + } + tr.option-text td + { + padding-bottom: 10px + } + table.palette + { + border-top: 1px solid #050400; + border-right: 1px solid #050400; + margin-bottom: 4px + } + td.palette + { + border-bottom: 1px solid #050400; + border-left: 1px solid #050400; + } + a.palette + { + padding: 2px 3px 2px 10px; + text-decoration: none; + } + .palette + { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + -ms-user-select: none; + } + </style><svg xmlns="http://www.w3.org/2000/svg"> + <defs> + <style type="text/css"> + @namespace "http://www.w3.org/2000/svg"; + .line {fill: none; stroke: #332900;} + .bold-line {stroke: #141000; shape-rendering: crispEdges; stroke-width: 2; } + .thin-line {stroke: #1F1800; shape-rendering: crispEdges} + .filled {fill: #332900; stroke: none;} + text.terminal {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #141000; + font-weight: bold; + } + text.nonterminal {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #1A1400; + font-weight: normal; + } + text.regexp {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #1F1800; + font-weight: normal; + } + rect, circle, polygon {fill: #332900; stroke: #332900;} + rect.terminal {fill: #FFDB4D; stroke: #332900;} + rect.nonterminal {fill: #FFEC9E; stroke: #332900;} + rect.text {fill: none; stroke: none;} + polygon.regexp {fill: #FFF4C7; stroke: #332900;} + </style> + </defs></svg></head> + <body> + <xhtml:p xmlns:xhtml="http://www.w3.org/1999/xhtml" style="font-size: 14px; font-weight:bold"><xhtml:a name="Definition">Definition:</xhtml:a></xhtml:p><svg xmlns="http://www.w3.org/2000/svg" width="375" height="113"> + <defs> + <style type="text/css"> + @namespace "http://www.w3.org/2000/svg"; + .line {fill: none; stroke: #332900;} + .bold-line {stroke: #141000; shape-rendering: crispEdges; stroke-width: 2; } + .thin-line {stroke: #1F1800; shape-rendering: crispEdges} + .filled {fill: #332900; stroke: none;} + text.terminal {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #141000; + font-weight: bold; + } + text.nonterminal {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #1A1400; + font-weight: normal; + } + text.regexp {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #1F1800; + font-weight: normal; + } + rect, circle, polygon {fill: #332900; stroke: #332900;} + rect.terminal {fill: #FFDB4D; stroke: #332900;} + rect.nonterminal {fill: #FFEC9E; stroke: #332900;} + rect.text {fill: none; stroke: none;} + polygon.regexp {fill: #FFF4C7; stroke: #332900;} + </style> + </defs> + <polygon points="9 61 1 57 1 65"/> + <polygon points="17 61 9 57 9 65"/><a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#PropertyName" xlink:title="PropertyName"> + <rect x="51" y="47" width="110" height="32"/> + <rect x="49" y="45" width="110" height="32" class="nonterminal"/> + <text class="nonterminal" x="59" y="65">PropertyName</text></a><rect x="201" y="79" width="30" height="32" rx="10"/> + <rect x="199" y="77" width="30" height="32" class="terminal" rx="10"/> + <text class="terminal" x="209" y="97">=</text><a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#Value" xlink:title="Value"> + <rect x="251" y="79" width="56" height="32"/> + <rect x="249" y="77" width="56" height="32" class="nonterminal"/> + <text class="nonterminal" x="259" y="97">Value</text></a><rect x="51" y="3" width="24" height="32" rx="10"/> + <rect x="49" y="1" width="24" height="32" class="terminal" rx="10"/> + <text class="terminal" x="59" y="21">,</text> + <svg:path xmlns:svg="http://www.w3.org/2000/svg" class="line" d="m17 61 h2 m20 0 h10 m110 0 h10 m20 0 h10 m0 0 h116 m-146 0 h20 m126 0 h20 m-166 0 q10 0 10 10 m146 0 q0 -10 10 -10 m-156 10 v12 m146 0 v-12 m-146 12 q0 10 10 10 m126 0 q10 0 10 -10 m-136 10 h10 m30 0 h10 m0 0 h10 m56 0 h10 m-296 -32 l20 0 m-1 0 q-9 0 -9 -10 l0 -24 q0 -10 10 -10 m296 44 l20 0 m-20 0 q10 0 10 -10 l0 -24 q0 -10 -10 -10 m-296 0 h10 m24 0 h10 m0 0 h252 m23 44 h-3"/> + <polygon points="365 61 373 57 373 65"/> + <polygon points="365 61 357 57 357 65"/></svg><xhtml:p xmlns:xhtml="http://www.w3.org/1999/xhtml"> + <xhtml:div class="ebnf"><xhtml:code> + <div><a href="#Definition" title="Definition">Definition</a></div> + <div> ::= <a href="#PropertyName" title="PropertyName">PropertyName</a> ( '=' <a href="#Value" title="Value">Value</a> )? ( ',' <a href="#PropertyName" title="PropertyName">PropertyName</a> ( '=' <a href="#Value" title="Value">Value</a> )? )*</div></xhtml:code></xhtml:div> + </xhtml:p> + <xhtml:p xmlns:xhtml="http://www.w3.org/1999/xhtml">no references</xhtml:p><xhtml:br xmlns:xhtml="http://www.w3.org/1999/xhtml" /><xhtml:p xmlns:xhtml="http://www.w3.org/1999/xhtml" style="font-size: 14px; font-weight:bold"><xhtml:a name="Query">Query:</xhtml:a></xhtml:p><svg xmlns="http://www.w3.org/2000/svg" width="419" height="201"> + <defs> + <style type="text/css"> + @namespace "http://www.w3.org/2000/svg"; + .line {fill: none; stroke: #332900;} + .bold-line {stroke: #141000; shape-rendering: crispEdges; stroke-width: 2; } + .thin-line {stroke: #1F1800; shape-rendering: crispEdges} + .filled {fill: #332900; stroke: none;} + text.terminal {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #141000; + font-weight: bold; + } + text.nonterminal {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #1A1400; + font-weight: normal; + } + text.regexp {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #1F1800; + font-weight: normal; + } + rect, circle, polygon {fill: #332900; stroke: #332900;} + rect.terminal {fill: #FFDB4D; stroke: #332900;} + rect.nonterminal {fill: #FFEC9E; stroke: #332900;} + rect.text {fill: none; stroke: none;} + polygon.regexp {fill: #FFF4C7; stroke: #332900;} + </style> + </defs> + <polygon points="9 61 1 57 1 65"/> + <polygon points="17 61 9 57 9 65"/> + <rect x="91" y="79" width="26" height="32" rx="10"/> + <rect x="89" y="77" width="26" height="32" class="terminal" rx="10"/> + <text class="terminal" x="99" y="97">-</text><a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#PropertyName" xlink:title="PropertyName"> + <rect x="157" y="47" width="110" height="32"/> + <rect x="155" y="45" width="110" height="32" class="nonterminal"/> + <text class="nonterminal" x="165" y="65">PropertyName</text></a><a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#PropertyName" xlink:title="PropertyName"> + <rect x="71" y="123" width="110" height="32"/> + <rect x="69" y="121" width="110" height="32" class="nonterminal"/> + <text class="nonterminal" x="79" y="141">PropertyName</text></a><rect x="221" y="123" width="30" height="32" rx="10"/> + <rect x="219" y="121" width="30" height="32" class="terminal" rx="10"/> + <text class="terminal" x="229" y="141">=</text> + <rect x="221" y="167" width="34" height="32" rx="10"/> + <rect x="219" y="165" width="34" height="32" class="terminal" rx="10"/> + <text class="terminal" x="229" y="185">!=</text><a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#Value" xlink:title="Value"> + <rect x="295" y="123" width="56" height="32"/> + <rect x="293" y="121" width="56" height="32" class="nonterminal"/> + <text class="nonterminal" x="303" y="141">Value</text></a><rect x="51" y="3" width="24" height="32" rx="10"/> + <rect x="49" y="1" width="24" height="32" class="terminal" rx="10"/> + <text class="terminal" x="59" y="21">,</text> + <svg:path xmlns:svg="http://www.w3.org/2000/svg" class="line" d="m17 61 h2 m60 0 h10 m0 0 h36 m-66 0 h20 m46 0 h20 m-86 0 q10 0 10 10 m66 0 q0 -10 10 -10 m-76 10 v12 m66 0 v-12 m-66 12 q0 10 10 10 m46 0 q10 0 10 -10 m-56 10 h10 m26 0 h10 m20 -32 h10 m110 0 h10 m0 0 h84 m-320 0 h20 m300 0 h20 m-340 0 q10 0 10 10 m320 0 q0 -10 10 -10 m-330 10 v56 m320 0 v-56 m-320 56 q0 10 10 10 m300 0 q10 0 10 -10 m-310 10 h10 m110 0 h10 m20 0 h10 m30 0 h10 m0 0 h4 m-74 0 h20 m54 0 h20 m-94 0 q10 0 10 10 m74 0 q0 -10 10 -10 m-84 10 v24 m74 0 v-24 m-74 24 q0 10 10 10 m54 0 q10 0 10 -10 m-64 10 h10 m34 0 h10 m20 -44 h10 m56 0 h10 m-340 -76 l20 0 m-1 0 q-9 0 -9 -10 l0 -24 q0 -10 10 -10 m340 44 l20 0 m-20 0 q10 0 10 -10 l0 -24 q0 -10 -10 -10 m-340 0 h10 m24 0 h10 m0 0 h296 m23 44 h-3"/> + <polygon points="409 61 417 57 417 65"/> + <polygon points="409 61 401 57 401 65"/></svg><xhtml:p xmlns:xhtml="http://www.w3.org/1999/xhtml"> + <xhtml:div class="ebnf"><xhtml:code> + <div><a href="#Query" title="Query">Query</a> ::= ( '-'? <a href="#PropertyName" title="PropertyName">PropertyName</a> | <a href="#PropertyName" title="PropertyName">PropertyName</a> ( '=' | '!=' ) <a href="#Value" title="Value">Value</a> ) ( ',' ( '-'? <a href="#PropertyName" title="PropertyName">PropertyName</a> | <a href="#PropertyName" title="PropertyName">PropertyName</a> ( '=' | '!=' ) <a href="#Value" title="Value">Value</a> ) )*</div></xhtml:code></xhtml:div> + </xhtml:p> + <xhtml:p xmlns:xhtml="http://www.w3.org/1999/xhtml">no references</xhtml:p><xhtml:br xmlns:xhtml="http://www.w3.org/1999/xhtml" /><xhtml:p xmlns:xhtml="http://www.w3.org/1999/xhtml" style="font-size: 14px; font-weight:bold"><xhtml:a name="Value">Value:</xhtml:a></xhtml:p><svg xmlns="http://www.w3.org/2000/svg" width="207" height="81"> + <defs> + <style type="text/css"> + @namespace "http://www.w3.org/2000/svg"; + .line {fill: none; stroke: #332900;} + .bold-line {stroke: #141000; shape-rendering: crispEdges; stroke-width: 2; } + .thin-line {stroke: #1F1800; shape-rendering: crispEdges} + .filled {fill: #332900; stroke: none;} + text.terminal {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #141000; + font-weight: bold; + } + text.nonterminal {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #1A1400; + font-weight: normal; + } + text.regexp {font-family: Verdana, Sans-serif; + font-size: 12px; + fill: #1F1800; + font-weight: normal; + } + rect, circle, polygon {fill: #332900; stroke: #332900;} + rect.terminal {fill: #FFDB4D; stroke: #332900;} + rect.nonterminal {fill: #FFEC9E; stroke: #332900;} + rect.text {fill: none; stroke: none;} + polygon.regexp {fill: #FFF4C7; stroke: #332900;} + </style> + </defs> + <polygon points="9 17 1 13 1 21"/> + <polygon points="17 17 9 13 9 21"/><a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#NumberLiteral" xlink:title="NumberLiteral"> + <rect x="51" y="3" width="108" height="32"/> + <rect x="49" y="1" width="108" height="32" class="nonterminal"/> + <text class="nonterminal" x="59" y="21">NumberLiteral</text></a><a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#StringLiteral" xlink:title="StringLiteral"> + <rect x="51" y="47" width="96" height="32"/> + <rect x="49" y="45" width="96" height="32" class="nonterminal"/> + <text class="nonterminal" x="59" y="65">StringLiteral</text></a><svg:path xmlns:svg="http://www.w3.org/2000/svg" class="line" d="m17 17 h2 m20 0 h10 m108 0 h10 m-148 0 h20 m128 0 h20 m-168 0 q10 0 10 10 m148 0 q0 -10 10 -10 m-158 10 v24 m148 0 v-24 m-148 24 q0 10 10 10 m128 0 q10 0 10 -10 m-138 10 h10 m96 0 h10 m0 0 h12 m23 -44 h-3"/> + <polygon points="197 17 205 13 205 21"/> + <polygon points="197 17 189 13 189 21"/></svg><xhtml:p xmlns:xhtml="http://www.w3.org/1999/xhtml"> + <xhtml:div class="ebnf"><xhtml:code> + <div><a href="#Value" title="Value">Value</a> ::= <a href="#NumberLiteral" title="NumberLiteral">NumberLiteral</a></div> + <div> | <a href="#StringLiteral" title="StringLiteral">StringLiteral</a></div></xhtml:code></xhtml:div> + </xhtml:p> + <xhtml:p xmlns:xhtml="http://www.w3.org/1999/xhtml">referenced by: + <xhtml:ul> + <xhtml:li><xhtml:a href="#Definition" title="Definition">Definition</xhtml:a></xhtml:li> + <xhtml:li><xhtml:a href="#Query" title="Query">Query</xhtml:a></xhtml:li> + </xhtml:ul> + </xhtml:p><xhtml:br xmlns:xhtml="http://www.w3.org/1999/xhtml" /><xhtml:p xmlns:xhtml="http://www.w3.org/1999/xhtml" style="font-size: 14px; font-weight:bold"><xhtml:a name="StringLiteral">StringLiteral:</xhtml:a></xhtml:p><svg xmlns="http://www.w3.org/2000/svg" width="219" height="81"> + <defs> + <style type="text/css"> < |