Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions developer/seb-config-key.html
Original file line number Diff line number Diff line change
Expand Up @@ -268,20 +268,31 @@ <h5>How to transition to the SEB JavaScript API</h5>
<h4 class="seb-section-header">How the Config Key is generated in SEB</h4>

<p>Unlike the <a href="documents/SEB-Specification-BrowserExamKey.pdf" target="_blank">Browser Exam Key</a>, the Config Key uses a standardized method to generate a checksum of all setting key/values of a SEB config file. As long as the same method is used in all SEB versions and in e-assessment systems (server-side), then the generated Config Key will be identical. There is another important difference in how the Config Key is generated: The SEB client only uses setting key/values to calculate the checksum, which are actually contained in an opened config file. This is important in case the SEB exam client is updated and this new version introduces new setting options. If the new keys and their (default) values would be included to calculate the Config Key checksum, the key would change, even though the config file didn't change. That's why when calculating the Config Key, <strong>the SEB client</strong> skips newly introduced keys (as long as these have their default values, which are always defined as &quot;safe&quot; values). <strong>The exam system</strong> uses all key/values (exceptions see below) it generates for calculating the Config Key.<br/>
When an existing config file is modified in the SEB preferences window or SEB Config Tool and then saved, the Config Key is re-calculated and then uses all keys the according SEB version supports. This new config file will then have another Config Key value, but when opened for an exam in an older SEB version, that version will still calculate this same new value for the Config Key (even though the older SEB version doesn't support the newly added setting keys). <br/>
The standardized method to calculate the Config Key hash (checksum) converts SEB settings into <a href="https://en.wikipedia.org/wiki/JSON" target="_blank">JSON</a> objects, where the key/values in the root-level and all higher-level objects (dictionaries) are alphabetically sorted by their key names. This is necessary, because the order of elements when applying a hash function matters (if the order isn't same, the hash function will return a different value). As key/values in standard JSON objects aren't ordered by definition, you might have to implement a custom converter for transforming the SEB property list (<a href="http://en.wikipedia.org/wiki/Plist" target="_blank">plist</a>) XML into this sorted JSON-like (&quot;SEB-JSON&quot; object. It's also important to use the proper format for the single elements (key names, values) of the SEB-JSON object, see below.</p>
When an existing config file is modified in the SEB preferences window or SEB Config Tool and then saved, the Config Key is re-calculated and then uses all keys the according SEB version supports. This new config file will then have another Config Key value, but when opened for an exam in an older SEB version, that version will still calculate this same new value for the Config Key (even though the older SEB version doesn't support the newly added setting keys).</p>

<h4 class="seb-section-header" id="seb-json">SEB-JSON</h4>

<p>The standardized Config Key method converts SEB settings from their <a href="https://en.wikipedia.org/wiki/Property_list" target="_blank">property list (plist)</a> representation into a deterministic, JSON-like byte representation called SEB-JSON. Key/value pairs in the root-level and all nested objects (dictionaries) are alphabetically sorted by key name because element order affects the resulting hash. Standard <a href="https://en.wikipedia.org/wiki/JSON" target="_blank">JSON</a> objects are unordered, so implementations may need a custom converter to produce this ordered representation. Values must use the formatting described below.</p>
<p>Despite its name, SEB-JSON is not standard JSON and is not necessarily valid JSON. String contents are written without JSON character escaping. The following examples show the same dictionaries encoded in both formats (the labels are not part of the encoded values):</p>
<pre><code>Standard JSON: {&quot;rule&quot;:&quot;say \&quot;hello\&quot;&quot;}
SEB-JSON: {&quot;rule&quot;:&quot;say &quot;hello&quot;&quot;}

Standard JSON: {&quot;socket&quot;:&quot;ws:\\localhost:8706&quot;}
SEB-JSON: {&quot;socket&quot;:&quot;ws:\localhost:8706&quot;}</code></pre>
<p>In both cases, standard JSON adds backslashes that are not present in the SEB-JSON byte representation. Because the Config Key depends on the exact bytes, the resulting hashes differ.</p>
<p>The <a href="https://packagist.org/packages/cameron1729/seb-json" target="_blank">cameron1729/seb-json Composer package</a> is the only known publicly available SEB-JSON encoder whose output is automatically verified against the official SEB for Windows and macOS implementations. It encodes prepared PHP values; callers remain responsible for the other Config Key generation steps described below.</p>

<h4 class="seb-section-header">Summary Config Key Generation</h4>

<ol>
<li><strong>Convert the plist XML of a decrypted/unencrypted SEB config file to a ordered JSON-like &quot;SEB-JSON&quot; object</strong>. Consider following special formatting details:
<li><strong>Convert the plist XML of a decrypted/unencrypted SEB config file to an ordered JSON-like &quot;SEB-JSON&quot; object</strong>. Consider the following special formatting details:
<ul>
<li><strong>Remove the key &quot;originatorVersion&quot; first.</strong> This key is exempted from the SEB-JSON hash (it's a special key which doesn't have any functionality, it's just meta data indicating which SEB version saved the config file)</li>
<li>Don't add any whitespace or line formatting to the SEB-JSON string. </li>
<li><strong>Don't add character escaping</strong> (also back shlashes &quot;\&quot; as found in URL filter rules should not be escaped).</li>
<li><strong>Don't add character escaping</strong> (also backslashes &quot;\&quot; as found in URL filter rules should not be escaped).</li>
<li>All &lt;dict&gt; elements from the plist XML must be ordered (alphabetically sorted) by their key names. Use a recursive method to apply ordering also to nested dictionaries contained in the root-level dictionary and in arrays. <strong>Use non-localized (culture invariant)</strong>, ideally non-ASCII value based <strong>case insensitive ordering</strong> (non-ASCII value based case insensitive ordering: for example the key &lt;key&gt;allowWlan&lt;/key&gt; comes before &lt;key&gt;allowWLAN&lt;/key&gt;). <strong>Note:</strong> We removed the requirement for using non-ASCII value based case insensitive ordering, as APIs in operating systems often don't support non-ASCII value based case insensitive ordering reliably (it's undefined if lower or upper case letters are ordered higher). Instead same key names with different lower/uppercase letters should not be used.</li>
<li><strong>Remove empty &lt;dict&gt; elements</strong> (key/value). Current versions of SEB clients should anyways not generate empty dictionaries, but this was possible with outdated versions. If config files have been generated that time, such elements might still be around.</li>
<li>All string elements must be UTF8 encoded.</li>
<li>All string elements must be UTF-8 encoded.</li>
<li>Base16 strings should use lower-case a-f characters, even though this isn't relevant in the current implementation of the Config Key calculation.</li>
<li>&lt;data&gt; plist XML elements must be converted to <a href="http://en.wikipedia.org/wiki/Base64" target="_blank">Base64</a> strings.</li>
<li>&lt;date&gt; plist XML elements must be converted to <a href="http://en.wikipedia.org/wiki/ISO_8601" target="_blank">ISO 8601</a> formatted strings.</li>
Expand All @@ -298,7 +309,7 @@ <h4 class="seb-section-header">Summary Config Key Generation</h4>
</ol>
<h4 class="seb-section-header">Checking the Config Key which SEB clients send with HTTP requests</h4>
<ol>
<li><strong>Create a SHA256 hash value from the absolute URL without Fragment part</strong><strong> with appended Config Key hash string</strong>. The absolute URL (as a <strong>UTF8 encoded string</strong>) is created by resolving the relative URL against its base according to the algorithm given in <a href="http://www.ietf.org/rfc/rfc1808.txt" target="_blank">RFC 1808</a>. If the absolute URL contains a Fragment (the last part of an URL starting with #, when using page anchors), the fragment needs to be removed from the URL. Then concatenate this URL string with the Config Key hash string and apply SHA256. The result is again a SHA256 hash value (32 Bytes, 64 chars string when encoded Base16).
<li><strong>Create a SHA256 hash value from the absolute URL without Fragment part</strong><strong> with appended Config Key hash string</strong>. The absolute URL (as a <strong>UTF-8 encoded string</strong>) is created by resolving the relative URL against its base according to the algorithm given in <a href="http://www.ietf.org/rfc/rfc1808.txt" target="_blank">RFC 1808</a>. If the absolute URL contains a Fragment (the last part of an URL starting with #, when using page anchors), the fragment needs to be removed from the URL. Then concatenate this URL string with the Config Key hash string and apply SHA256. The result is again a SHA256 hash value (32 Bytes, 64 chars string when encoded Base16).
<br/>
 </li>
<li><strong>Compare the resulting hash string with the one received in the custom header in each HTTP request</strong>: <br>
Expand Down