summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPauli <paul.dale@oracle.com>2018-11-16 11:44:30 +1000
committerPauli <paul.dale@oracle.com>2019-02-18 13:28:14 +1000
commit1bdbdaffdc66be457a40f33640b523aaf21138c6 (patch)
tree23bd17995698f4f14780fc9b9a3dad589f2ea96a
parente3ac3654892246d7492f1012897e42ad7efd13ce (diff)
Properties for implementation selection.
Properties are a sequence of comma separated name=value pairs. A name without a corresponding value is assumed to be a Boolean and have the true value 'yes'. Values are either strings or numbers. Strings can be quoted either _"_ or _'_ or unquoted (with restrictions). There are no escape characters inside strings. Number are either decimal digits or '0x' followed by hexidecimal digits. Numbers are represented internally as signed sixty four bit values. Queries on properties are a sequence comma separated conditional tests. These take the form of name=value (equality test), name!=value (inequality test) or name (Boolean test for truth). Queries can be parsed, compared against a definition or merged pairwise. Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tim Hudson <tjh@openssl.org> (Merged from https://github.com/openssl/openssl/pull/8224)
-rw-r--r--CHANGES4
-rw-r--r--crypto/build.info2
-rw-r--r--crypto/err/err_all.c4
-rw-r--r--crypto/err/openssl.ec1
-rw-r--r--crypto/lhash/lhash.c12
-rw-r--r--crypto/property/README87
-rw-r--r--crypto/property/build.info3
-rw-r--r--crypto/property/defn_cache.c105
-rw-r--r--crypto/property/properties.ebnf17
-rw-r--r--crypto/property/properties.xhtml685
-rw-r--r--crypto/property/property.c508
-rw-r--r--crypto/property/property_lcl.h51
-rw-r--r--crypto/property/property_parse.c545
-rw-r--r--crypto/property/property_string.c137
-rw-r--r--crypto/sparse_array.c2
-rw-r--r--doc/internal/man3/OSSL_METHOD_STORE.pod103
-rw-r--r--doc/man3/OPENSSL_LH_COMPFUNC.pod11
-rw-r--r--include/internal/property.h35
-rw-r--r--include/openssl/err.h2
-rw-r--r--include/openssl/lhash.h5
-rw-r--r--include/openssl/stack.h2
-rw-r--r--test/build.info6
-rw-r--r--test/property_test.c374
-rw-r--r--test/recipes/03-test_property.t12
-rw-r--r--test/sparse_array_test.c2
25 files changed, 2705 insertions, 10 deletions
diff --git a/CHANGES b/CHANGES
index cca9ed9b80..5fcf667736 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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"/>