From 917c11b939233fc29632ca4c36d473de871b5f12 Mon Sep 17 00:00:00 2001 From: "Morten V. Christiansen" Date: Wed, 11 Mar 2026 07:50:48 +0100 Subject: [PATCH] doxs, refactor, extra test --- PROJECT_CONTEXT.rtf | 689 +++++++++++------- .../zenroom_service_client.cpython-313.pyc | Bin 20790 -> 22829 bytes ca_core/crypto/zenroom_service_client.py | 334 ++++++--- ...est_zenroom_service_client.cpython-313.pyc | Bin 8351 -> 9203 bytes tests/test_zenroom_service_client.py | 22 +- 5 files changed, 652 insertions(+), 393 deletions(-) diff --git a/PROJECT_CONTEXT.rtf b/PROJECT_CONTEXT.rtf index e57b979..8f2faae 100644 --- a/PROJECT_CONTEXT.rtf +++ b/PROJECT_CONTEXT.rtf @@ -1,361 +1,492 @@ {\rtf1\ansi\deff3\adeflang1025 -{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Liberation Serif{\*\falt Times New Roman};}{\f4\fswiss\fprq2\fcharset0 Liberation Sans{\*\falt Arial};}{\f5\froman\fprq2\fcharset0 Helvetica{\*\falt Arial};}{\f6\froman\fprq2\fcharset0 Courier{\*\falt Courier New};}{\f7\fnil\fprq2\fcharset0 Noto Sans CJK SC;}{\f8\fnil\fprq2\fcharset0 Noto Sans Devanagari;}{\f9\fswiss\fprq0\fcharset128 Noto Sans Devanagari;}} +{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Liberation Serif{\*\falt Times New Roman};}{\f4\fmodern\fprq1\fcharset0 Liberation Mono{\*\falt Courier New};}{\f5\fnil\fprq0\fcharset2 OpenSymbol{\*\falt Arial Unicode MS};}{\f6\froman\fprq2\fcharset0 Liberation Sans{\*\falt Arial};}{\f7\froman\fprq2\fcharset0 Helvetica{\*\falt Arial};}{\f8\froman\fprq2\fcharset0 Courier{\*\falt Courier New};}{\f9\fmodern\fprq1\fcharset0 Noto Sans Mono CJK SC;}{\f10\fnil\fprq2\fcharset0 0;}{\f11\fnil\fprq2\fcharset0 Noto Sans CJK SC;}{\f12\fnil\fprq2\fcharset0 Noto Serif CJK SC;}{\f13\fnil\fprq2\fcharset0 Noto Sans Devanagari;}} {\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;} -{\stylesheet{\s0\snext0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052 Normal;} -{\s15\sbasedon0\snext16\rtlch\af8\afs28 \ltrch\hich\af4\loch\sb240\sa120\keepn\f4\fs28\dbch\af7 Heading;} -{\s16\sbasedon0\snext16\loch\sl276\slmult1\sb0\sa140 Body Text;} -{\s17\sbasedon16\snext17\rtlch\af9 \ltrch List;} -{\s18\sbasedon0\snext18\rtlch\af9\afs24\ai \ltrch\loch\sb120\sa120\noline\fs24\i caption;} -{\s19\sbasedon0\snext19\rtlch\af9 \ltrch\loch\noline Index;} -}{\*\generator LibreOffice/25.2.7.2$Linux_X86_64 LibreOffice_project/520$Build-2}{\info{\creatim\yr0\mo0\dy0\hr0\min0}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}}{\*\userprops}\deftab720 -\hyphauto1\viewscale100\formshade\paperh15840\paperw12240\margl1440\margr1440\margt1440\margb1440\sectd\sbknone\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ftnbj\ftnstart1\ftnrstcont\ftnnar\fet\aftnrstcont\aftnstart1\aftnnrlc -{\*\ftnsep\chftnsep}\pgndec\pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs36\b\f5\loch +{\stylesheet{\s0\snext0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052 Normal;} +{\s1\sbasedon19\snext20\rtlch\af13\afs48\ab \ltrch\hich\af3\loch\ilvl0\outlinelevel0\sb240\sa120\f3\fs48\b\dbch\af12 heading 1;} +{\s2\sbasedon19\snext20\rtlch\af13\afs36\ab \ltrch\hich\af3\loch\ilvl1\outlinelevel1\sb200\sa120\f3\fs36\b\dbch\af12 heading 2;} +{\*\cs15\snext15\rtlch\ab \ltrch\loch\b Strong;} +{\*\cs16\snext16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9 Source Text;} +{\*\cs17\snext17\rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 Bullets;} +{\*\cs18\snext18 Numbering Symbols;} +{\s19\sbasedon0\snext20\rtlch\af13\afs28 \ltrch\hich\af6\loch\sb240\sa120\keepn\f6\fs28\dbch\af11 Heading;} +{\s20\sbasedon0\snext20\loch\sl276\slmult1\sb0\sa140 Body Text;} +{\s21\sbasedon20\snext21\rtlch\af13 \ltrch\loch\sl240\slmult1\sb0\sa0 List;} +{\s22\sbasedon0\snext22\rtlch\af13\afs24\ai \ltrch\loch\sb120\sa120\fs24\i caption;} +{\s23\sbasedon0\snext23\rtlch\af13 \ltrch Index;} +{\s24\sbasedon0\snext24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9 Preformatted Text;} +{\s25\sbasedon0\snext20\rtlch\afs12 \ltrch\loch\sb0\sa283\brdrt\brdrnone\brdrl\brdrnone\brdrb\brdrdb\brdrw1\brdrcf15\brsp0\brdrr\brdrnone\noline\fs12 Horizontal Line;} +{\s26\sbasedon0\snext26\loch\nowidctlpar\noline Table Contents;} +{\s27\sbasedon26\snext27\rtlch\ab \ltrch\loch\qc\noline\b Table Heading;} +}{\*\listtable{\list\listtemplateid1 +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li709} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li1418} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li2127} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li2836} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li3545} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li4254} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li4963} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li5672} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li6381}\listid1} +{\list\listtemplateid2 +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li709} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li1418} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li2127} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li2836} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li3545} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li4254} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li4963} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li5672} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li6381}\listid2} +{\list\listtemplateid3 +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li709} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li1418} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li2127} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li2836} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li3545} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li4254} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li4963} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li5672} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li6381}\listid3} +{\list\listtemplateid4 +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li709} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li1418} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li2127} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li2836} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li3545} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li4254} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li4963} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li5672} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li6381}\listid4} +{\list\listtemplateid5 +{\listlevel\levelnfc0\leveljc0\levelstartat1\levelfollow0{\leveltext \'02\'00.;}{\levelnumbers\'01;}\fi-283\li709} +{\listlevel\levelnfc0\leveljc0\levelstartat1\levelfollow0{\leveltext \'02\'01.;}{\levelnumbers\'01;}\fi-283\li1418} +{\listlevel\levelnfc0\leveljc0\levelstartat1\levelfollow0{\leveltext \'02\'02.;}{\levelnumbers\'01;}\fi-283\li2127} +{\listlevel\levelnfc0\leveljc0\levelstartat1\levelfollow0{\leveltext \'02\'03.;}{\levelnumbers\'01;}\fi-283\li2836} +{\listlevel\levelnfc0\leveljc0\levelstartat1\levelfollow0{\leveltext \'02\'04.;}{\levelnumbers\'01;}\fi-283\li3545} +{\listlevel\levelnfc0\leveljc0\levelstartat1\levelfollow0{\leveltext \'02\'05.;}{\levelnumbers\'01;}\fi-283\li4254} +{\listlevel\levelnfc0\leveljc0\levelstartat1\levelfollow0{\leveltext \'02\'06.;}{\levelnumbers\'01;}\fi-283\li4963} +{\listlevel\levelnfc0\leveljc0\levelstartat1\levelfollow0{\leveltext \'02\'07.;}{\levelnumbers\'01;}\fi-283\li5672} +{\listlevel\levelnfc0\leveljc0\levelstartat1\levelfollow0{\leveltext \'02\'08.;}{\levelnumbers\'01;}\fi-283\li6381}\listid5} +{\list\listtemplateid6 +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li709} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li1418} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li2127} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li2836} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li3545} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li4254} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li4963} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li5672} +{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f5\rtlch\af5 \ltrch\loch\fi-283\li6381}\listid6} +{\list\listtemplateid7 +{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0} +{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0} +{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0} +{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0} +{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0} +{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0} +{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0} +{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0} +{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0}\listid7} +}{\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}{\listoverride\listid3\listoverridecount0\ls3}{\listoverride\listid4\listoverridecount0\ls4}{\listoverride\listid5\listoverridecount0\ls5}{\listoverride\listid6\listoverridecount0\ls6}{\listoverride\listid7\listoverridecount0\ls7}}{\*\generator LibreOffice/25.2.7.2$Linux_X86_64 LibreOffice_project/520$Build-2}{\info{\creatim\yr0\mo0\dy0\hr0\min0}{\revtim\yr2026\mo3\dy11\hr7\min49}{\printim\yr0\mo0\dy0\hr0\min0}}{\*\userprops}\deftab720 +\hyphauto1\viewscale100\formshade\paperh15840\paperw12240\margl1440\margr1440\margt1440\margb1440\sectd\sbknone\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\fet\aftnrstcont\aftnstart1\aftnnrlc +{\*\ftnsep\chftnsep}\pgndec\pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch CA/PKI Backend Project Context} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs32\b\f7\loch Stack} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab Python 3.13} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab FastAPI (web API)} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab psycopg (PostgreSQL driver)} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab PostgreSQL database: }{\hich\af6\loch\f6\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch +\tab PostgreSQL database: }{\hich\af8\loch\f8\loch ca} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab Unit tests: }{\hich\af6\loch\f6\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch +\tab Unit tests: }{\hich\af8\loch\f8\loch unittest} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab HTTP testing: }{\hich\af6\loch\f6\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch +\tab HTTP testing: }{\hich\af8\loch\f8\loch fastapi.testclient} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab Zenroom cryptographic runtime (Docker container)} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch +\tab Zenroom cryptographic runtime }{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs24\b\f7\dbch\af10\loch +(local execution via Python wrapper)} +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Run tests:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af8\loch\f8\loch python3 -m unittest discover} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Integration tests require:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af8\loch\f8\loch export DATABASE_URL="postgresql:///ca"} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5 +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7 \u8212\'97\u8212\'97\u8212\'97\u8212\'97\u8212\'97} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs36\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch Project Structure} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af8\loch\f8\loch pki/\line \u9500\'3f\u9472\'3f\u9472\'3f ca_core/\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f entity.py\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f group_member.py\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f property.py\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f metadata.py\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f db_logging.py\line \u9474\'3f \u9492\'3f\u9472\'3f\u9472\'3f crypto/\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f zenroom_client.py\line \u9474\'3f \u9492\'3f\u9472\'3f\u9472\'3f zenroom_service_client.py\line \u9474\'3f\line \u9500\'3f\u9472\'3f\u9472\'3f ca_api/\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f app.py\line \u9474\'3f \u9492\'3f\u9472\'3f\u9472\'3f db.py\line \u9474\'3f\line \u9500\'3f\u9472\'3f\u9472\'3f tests/\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f test_entity.py\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f test_group.py\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f test_property.py\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f test_metadata.py\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f test_api_smoke.py\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f test_api_unit.py\line \u9474\'3f \u9500\'3f\u9472\'3f\u9472\'3f test_api_integration.py\line \u9474\'3f \u9492\'3f\u9472\'3f\u9472\'3f integration/\line \u9474\'3f \u9492\'3f\u9472\'3f\u9472\'3f zenroom tests\line \u9474\'3f\line \u9500\'3f\u9472\'3f\u9472\'3f create_tables.sql\line \u9492\'3f\u9472\'3f\u9472\'3f PROJECT_CONTEXT.md} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5 +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7 \u8212\'97\u8212\'97\u8212\'97\u8212\'97\u8212\'97} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs36\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch Architecture} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af8\loch\f8\loch HTTP API (FastAPI)\line \u9474\'3f\line \u9660\'3f\line ca_api (thin HTTP adapter)\line \u9474\'3f\line \u9660\'3f\line ca_core (business logic)\line \u9474\'3f\line \u9660\'3f\line PostgreSQL} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch The system is layered to keep business logic independent from the HTTP interface.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5 +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7 \u8212\'97\u8212\'97\u8212\'97\u8212\'97\u8212\'97} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs36\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch Database Overview} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Database: }{\hich\af5\loch\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch +Database: }{\hich\af7\loch\b\f7\loch ca} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Core tables:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab entity} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab group_member} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab property} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab metadata} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab log} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs32\b\f7\loch Entity Rules} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Entity types:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab person} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab group} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab device} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Status values:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab active} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab revoked} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Groups must include }{\hich\af6\loch\f6\loch -ca_reference}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch +Groups must include }{\hich\af8\loch\f8\loch +ca_reference}{\hich\af7\loch\f7\loch .} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Persons and devices must }{\hich\af5\loch\b\f5\loch -not}{\hich\af5\loch\f5\loch - include }{\hich\af6\loch\f6\loch -ca_reference}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch +Persons and devices must }{\hich\af7\loch\b\f7\loch +not}{\hich\af7\loch\f7\loch + include }{\hich\af8\loch\f8\loch +ca_reference}{\hich\af7\loch\f7\loch .} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Revoked entities are immutable.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5 +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7 \u8212\'97\u8212\'97\u8212\'97\u8212\'97\u8212\'97} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs36\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch Logging} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch All mutations must call:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af8\loch\f8\loch log_change(cursor, message)} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Exactly }{\hich\af5\loch\b\f5\loch -one log entry must be produced per mutation}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch +Exactly }{\hich\af7\loch\b\f7\loch +one log entry must be produced per mutation}{\hich\af7\loch\f7\loch .} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Logging occurs inside the same transaction.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5 +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7 \u8212\'97\u8212\'97\u8212\'97\u8212\'97\u8212\'97} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs36\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch Core Modules} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs32\b\f7\loch entity.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Provides:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab insert_creator} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab enroll_person} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab create_group} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab get_entity} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab set_entity_status} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs32\b\f7\loch group_member.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Provides:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab add_group_member} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab remove_group_member} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab get_members_of_group} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs32\b\f7\loch property.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Provides:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab set_property} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab delete_property} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab get_properties} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\fs32\b\f7\loch metadata.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7\loch Provides:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab get_name} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab get_comment} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab get_public_key} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab get_defense_p} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab set_name} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0{\hich\af7\loch\f7 +\u8226\'95}{\hich\af7\loch\f7\loch \tab set_defense_p} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5 +\par \pard\plain \s0\rtlch\af13\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180{\hich\af7\loch\f7 \u8212\'97\u8212\'97\u8212\'97\u8212\'97\u8212\'97} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs36\b\f5\loch +\par \pard\plain \s2\rtlch\af13\afs36\ab \ltrch\hich\af3\loch\ilvl1\outlinelevel1\sb200\sa120\f3\fs36\b\dbch\af12\ql\fi0\li0\lin0\sb0\sa180{\rtlch\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch Cryptographic Layer} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -The system integrates }{\hich\af5\loch\b\f5\loch -Zenroom}{\hich\af5\loch\f5\loch - for cryptographic operations.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Zenroom runs in an isolated environment, typically a }{\hich\af5\loch\b\f5\loch -Docker container}{\hich\af5\loch\f5\loch +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +The system integrates }{\hich\af7\loch\cs15\rtlch\ab \ltrch\loch\b\fs36\f7\loch +Zenroom}{\hich\af7\loch\fs36\b\f7\loch + as its cryptographic runtime.} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Zenroom is a deterministic virtual machine designed for secure execution of\line cryptographic protocols defined in }{\hich\af7\loch\cs15\rtlch\ab \ltrch\loch\b\fs36\f7\loch +Zencode scripts}{\hich\af7\loch\fs36\b\f7\loch .} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch -ca_core\line \u9474\'3f\line \u9660\'3f\line crypto clients\line \u9474\'3f\line \u9660\'3f\line Zenroom runtime} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch -zenroom_client.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Local execution interface.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Responsibilities:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab execute Zenroom scripts} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab parse output} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab raise errors} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Used for development and some integration tests.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +In this project Zenroom is executed }{\hich\af7\loch\cs15\rtlch\ab \ltrch\loch\b\fs36\f7\loch +locally via the official Python wrapper}{\hich\af7\loch\fs36\b\f7\loch +.\line No Docker container or HTTP service is used.} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\fi0\li0\lin0\ri0\rin0\sb0\sa180{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch +ca_core} +\par \pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\sb0\sa180{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10 + \u9492\'3f\u9472\'3f }{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch +crypto} +\par \pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\sb0\sa180{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10 + \u9492\'3f\u9472\'3f }{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch zenroom_service_client.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -HTTP client for a Zenroom service container.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\sb0\sa180{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10 + \u9474\'3f} +\par \pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\sb0\sa180{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10 + \u9660\'3f} +\par \pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\sb0\sa180{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10 + }{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch +zenroom Python wrapper} +\par \pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\sb0\sa180{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10 + \u9474\'3f} +\par \pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\sb0\sa180{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10 + \u9660\'3f} +\par \pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10 + }{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch +Zenroom runtime (local process)} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +This architecture removes the previous HTTP service layer and simplifies\line deployment and testing.} +\par \pard\plain \s25\rtlch\afs12 \ltrch\loch\sb0\sa283\brdrt\brdrnone\brdrl\brdrnone\brdrb\brdrdb\brdrw1\brdrcf15\brsp0\brdrr\brdrnone\noline\fs12\sb0\sa180\rtlch\afs24 \ltrch\hich\af7\loch\fs36\b\f7\loch + +\par \pard\plain \s2\rtlch\af13\afs36\ab \ltrch\hich\af3\loch\ilvl1\outlinelevel1\sb200\sa120\f3\fs36\b\dbch\af12\sb0\sa180{\rtlch\afs24 \ltrch\hich\af7\loch\f7\dbch\af10\loch +zenroom_service_client.py} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\fs36\b\f7\loch +zenroom_service_client.py}{\hich\af7\loch\fs36\b\f7\loch + provides the }{\hich\af7\loch\cs15\rtlch\ab \ltrch\loch\b\fs36\f7\loch +adapter between the CA backend and Zenroom}{\hich\af7\loch\fs36\b\f7\loch +.} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch Responsibilities:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab send scripts} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab pass JSON input} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab return parsed result} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Typical flow:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch -Python\line \u8595\'3f\line ZenroomServiceClient\line \u8595\'3f\line HTTP request\line \u8595\'3f\line Zenroom container\line \u8595\'3f\line JSON result} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5 -\u8212\'97\u8212\'97\u8212\'97\u8212\'97\u8212\'97} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs36\b\f5\loch -API Layer (ca_api)} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -FastAPI provides the HTTP interface.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls1 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +execute Zencode scripts using the Zenroom Python wrapper} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls1 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +pass structured JSON input} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls1 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +parse Zenroom output} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls1 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +normalize results into Python structures} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls1 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +translate runtime errors into }{\hich\af7\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\fs36\b\f7\loch +ZenroomServiceError} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +All cryptographic operations are exposed through a }{\hich\af7\loch\cs15\rtlch\ab \ltrch\loch\b\fs36\f7\loch +single client class}{\hich\af7\loch\fs36\b\f7\loch +:} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\fi0\li0\lin0\ri0\rin0{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch +{\*\bkmkstart code-block-viewer}{\*\bkmkend code-block-viewer}ZenroomServiceClient} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s25\rtlch\afs12 \ltrch\loch\sb0\sa283\brdrt\brdrnone\brdrl\brdrnone\brdrb\brdrdb\brdrw1\brdrcf15\brsp0\brdrr\brdrnone\noline\fs12\sb0\sa180\rtlch\afs24 \ltrch\hich\af7\loch\fs36\b\f7\loch + +\par \pard\plain \s2\rtlch\af13\afs36\ab \ltrch\hich\af3\loch\ilvl1\outlinelevel1\sb200\sa120\f3\fs36\b\dbch\af12\sb0\sa180{\rtlch\afs24 \ltrch\hich\af7\loch\f7\dbch\af10\loch +Supported Operations} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +The client provides the following cryptographic operations:} +\par \trowd\trql\ltrrow\trpaddft3\trpaddt0\trpaddfl3\trpaddl0\trpaddfb3\trpaddb0\trpaddfr3\trpaddr0\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx3145\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx6933\pard\plain \s27\rtlch\ab \ltrch\loch\qc\noline\b\intbl{\loch +Method}\cell\pard\plain \s27\rtlch\ab \ltrch\loch\qc\noline\b\intbl{\loch +Description}\cell\row\pard \trowd\trql\ltrrow\trpaddft3\trpaddt0\trpaddfl3\trpaddl0\trpaddfb3\trpaddb0\trpaddfr3\trpaddr0\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx3145\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx6933\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\loch +generate_keypair()}\cell\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch +Create an ECDH keypair}\cell\row\pard \trowd\trql\ltrrow\trpaddft3\trpaddt0\trpaddfl3\trpaddl0\trpaddfb3\trpaddb0\trpaddfr3\trpaddr0\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx3145\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx6933\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\loch +generate_public_key()}\cell\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch +Derive public key from keyring}\cell\row\pard \trowd\trql\ltrrow\trpaddft3\trpaddt0\trpaddfl3\trpaddl0\trpaddfb3\trpaddb0\trpaddfr3\trpaddr0\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx3145\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx6933\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\loch +symmetric_encrypt()}\cell\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch +Encrypt message using password}\cell\row\pard \trowd\trql\ltrrow\trpaddft3\trpaddt0\trpaddfl3\trpaddl0\trpaddfb3\trpaddb0\trpaddfr3\trpaddr0\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx3145\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx6933\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\loch +symmetric_decrypt()}\cell\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch +Decrypt password-encrypted message}\cell\row\pard \trowd\trql\ltrrow\trpaddft3\trpaddt0\trpaddfl3\trpaddl0\trpaddfb3\trpaddb0\trpaddfr3\trpaddr0\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx3145\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx6933\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\loch +asymmetric_encrypt()}\cell\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch +Encrypt message for a recipient}\cell\row\pard \trowd\trql\ltrrow\trpaddft3\trpaddt0\trpaddfl3\trpaddl0\trpaddfb3\trpaddb0\trpaddfr3\trpaddr0\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx3145\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx6933\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\loch +asymmetric_decrypt()}\cell\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch +Decrypt message from a sender}\cell\row\pard \trowd\trql\ltrrow\trpaddft3\trpaddt0\trpaddfl3\trpaddl0\trpaddfb3\trpaddb0\trpaddfr3\trpaddr0\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx3145\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx6933\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\loch +sign_objects()}\cell\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch +Sign a string field}\cell\row\pard \trowd\trql\ltrrow\trpaddft3\trpaddt0\trpaddfl3\trpaddl0\trpaddfb3\trpaddb0\trpaddfr3\trpaddr0\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx3145\clpadfl3\clpadl28\clpadft3\clpadt28\clpadfb3\clpadb28\clpadfr3\clpadr28\clvertalc\cellx6933\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\loch +verify_signature()}\cell\pard\plain \s26\loch\nowidctlpar\noline\intbl{\loch +Verify an ECDH signature}\cell\row\pard \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +These operations correspond directly to the Zencode scripts embedded in the module.} +\par \pard\plain \s25\rtlch\afs12 \ltrch\loch\sb0\sa283\brdrt\brdrnone\brdrl\brdrnone\brdrb\brdrdb\brdrw1\brdrcf15\brsp0\brdrr\brdrnone\noline\fs12\sb0\sa180\rtlch\afs24 \ltrch\hich\af7\loch\fs36\b\f7\loch + +\par \pard\plain \s2\rtlch\af13\afs36\ab \ltrch\hich\af3\loch\ilvl1\outlinelevel1\sb200\sa120\f3\fs36\b\dbch\af12\sb0\sa180{\rtlch\afs24 \ltrch\hich\af7\loch\f7\dbch\af10\loch +Script Execution} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +All Zencode scripts are executed through a single internal helper:} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\fi0\li0\lin0\ri0\rin0{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch +{\*\bkmkstart code-block-viewer Copy 1}{\*\bkmkend code-block-viewer Copy 1}_run_script()} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch Responsibilities:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab routing} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab validation} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab DB connection handling} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab error mapping} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Flow:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch -HTTP request\line \u8595\'3f\line FastAPI route\line \u8595\'3f\line db_cursor()\line \u8595\'3f\line ca_core function\line \u8595\'3f\line commit / rollback} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5 -\u8212\'97\u8212\'97\u8212\'97\u8212\'97\u8212\'97} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs36\b\f5\loch +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls2 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +invoke }{\hich\af7\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\fs36\b\f7\loch +zenroom.zencode_exec()} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls2 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +pass JSON input} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls2 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +capture Zenroom logs} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls2 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +parse returned JSON output} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls2 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +raise }{\hich\af7\loch\cs16\rtlch\af4 \ltrch\hich\af4\loch\f4\dbch\af9\fs36\b\f7\loch +ZenroomServiceError}{\hich\af7\loch\fs36\b\f7\loch + on failure} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +This centralizes Zenroom interaction and ensures consistent error handling.} +\par \pard\plain \s25\rtlch\afs12 \ltrch\loch\sb0\sa283\brdrt\brdrnone\brdrl\brdrnone\brdrb\brdrdb\brdrw1\brdrcf15\brsp0\brdrr\brdrnone\noline\fs12\sb0\sa180\rtlch\afs24 \ltrch\hich\af7\loch\fs36\b\f7\loch + +\par \pard\plain \s2\rtlch\af13\afs36\ab \ltrch\hich\af3\loch\ilvl1\outlinelevel1\sb200\sa120\f3\fs36\b\dbch\af12\sb0\sa180{\rtlch\afs24 \ltrch\hich\af7\loch\f7\dbch\af10\loch +Error Handling} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Zenroom failures are converted into Python exceptions.} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\fi0\li0\lin0\ri0\rin0{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch +{\*\bkmkstart code-block-viewer Copy 2}{\*\bkmkend code-block-viewer Copy 2}ZenroomServiceError} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +This exception is raised when:} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls3 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Zenroom execution fails} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls3 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +output cannot be parsed} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls3 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +expected fields are missing} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls3 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +verification fails} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +This allows the rest of the system to treat cryptographic failures as standard\line Python exceptions.} +\par \pard\plain \s25\rtlch\afs12 \ltrch\loch\sb0\sa283\brdrt\brdrnone\brdrl\brdrnone\brdrb\brdrdb\brdrw1\brdrcf15\brsp0\brdrr\brdrnone\noline\fs12\sb0\sa180\rtlch\afs24 \ltrch\hich\af7\loch\fs36\b\f7\loch + +\par \pard\plain \s2\rtlch\af13\afs36\ab \ltrch\hich\af3\loch\ilvl1\outlinelevel1\sb200\sa120\f3\fs36\b\dbch\af12\sb0\sa180{\rtlch\afs24 \ltrch\hich\af7\loch\f7\dbch\af10\loch Testing Strategy} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Three layers of tests exist.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch -Core Unit Tests} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Test domain logic directly.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Examples:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab test_entity.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab test_group.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab test_property.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab test_metadata.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch -API Unit Tests} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Test HTTP behaviour without DB.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Mocked components:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab db_cursor} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab ca_core functions} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -File:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab test_api_unit.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch -API Integration Tests} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Full stack tests:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch -FastAPI\line \u8595\'3f\line ca_core\line \u8595\'3f\line PostgreSQL} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -File:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab test_api_integration.py} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Requires:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch -export DATABASE_URL="postgresql:///ca"} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs32\b\f5\loch -Zenroom Integration Tests} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Located in:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch -tests/integration/} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Verify:} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab Zenroom script execution} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab service communication} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi-360\li360\lin360\sb0\sa0\ltrpar{\hich\af5\loch\f5 -\u8226\'95}{\hich\af5\loch\f5\loch -\tab error handling} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -Some tests require a running Zenroom container.} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\qc\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5 -\u8212\'97\u8212\'97\u8212\'97\u8212\'97\u8212\'97} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\fs36\b\f5\loch -Runtime Deployment Model} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af6\loch\f6\loch -FastAPI API\line \u9474\'3f\line \u9660\'3f\line ca_core\line \u9474\'3f\line \u9660\'3f\line crypto client\line \u9474\'3f\line \u9660\'3f\line Zenroom container\line \u9474\'3f\line \u9660\'3f\line PostgreSQL} -\par \pard\plain \s0\rtlch\af8\afs24\alang1081 \ltrch\lang1033\langfe2052\hich\af3\loch\widctlpar\hyphpar1\ltrpar\cf0\f3\fs24\lang1033\kerning1\dbch\af10\langfe2052\ql\fi0\li0\lin0\sb0\sa180\ltrpar{\hich\af5\loch\f5\loch -This design keeps cryptographic execution isolated while keeping the Python service lightweight.} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Cryptographic functionality is tested using }{\hich\af7\loch\cs15\rtlch\ab \ltrch\loch\b\fs36\f7\loch +direct functional tests}{\hich\af7\loch\fs36\b\f7\loch +.} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Tests execute the real Zenroom runtime through the adapter and verify:} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls4 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +key generation} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls4 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +encryption/decryption roundtrips} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls4 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +signing} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls4 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +signature verification} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls4 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +signature rejection for tampered messages} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Example tests:} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\fi0\li0\lin0\ri0\rin0{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch +{\*\bkmkstart code-block-viewer Copy 3}{\*\bkmkend code-block-viewer Copy 3}tests/test_zenroom_service_client.py} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +The suite currently contains }{\hich\af7\loch\cs15\rtlch\ab \ltrch\loch\b\fs36\f7\loch +9 tests}{\hich\af7\loch\fs36\b\f7\loch +:} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain 1.\tab}\ilvl0\ls5 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +generate keypair} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain 2.\tab}\ilvl0\ls5 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +generate public key} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain 3.\tab}\ilvl0\ls5 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +symmetric encryption} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain 4.\tab}\ilvl0\ls5 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +symmetric decryption} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain 5.\tab}\ilvl0\ls5 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +asymmetric encryption} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain 6.\tab}\ilvl0\ls5 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +asymmetric decryption} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain 7.\tab}\ilvl0\ls5 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +signing} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain 8.\tab}\ilvl0\ls5 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +successful verification} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain 9.\tab}\ilvl0\ls5 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +verification failure for modified message} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Tests run with:} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s24\rtlch\af4\afs20 \ltrch\hich\af4\loch\sb0\sa0\f4\fs20\dbch\af9\fi0\li0\lin0\ri0\rin0{\rtlch\af13\afs24 \ltrch\hich\af7\loch\fs36\b\f7\dbch\af10\loch +{\*\bkmkstart code-block-viewer Copy 4}{\*\bkmkend code-block-viewer Copy 4}python3 -m unittest discover} +\par \sect\sectd\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ltrsect\sbknone\pard\plain \s20\loch\sl276\slmult1\sb0\sa140\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Typical runtime is }{\hich\af7\loch\cs15\rtlch\ab \ltrch\loch\b\fs36\f7\loch +under 0.5 seconds}{\hich\af7\loch\fs36\b\f7\loch +.} +\par \pard\plain \s25\rtlch\afs12 \ltrch\loch\sb0\sa283\brdrt\brdrnone\brdrl\brdrnone\brdrb\brdrdb\brdrw1\brdrcf15\brsp0\brdrr\brdrnone\noline\fs12\sb0\sa180\rtlch\afs24 \ltrch\hich\af7\loch\fs36\b\f7\loch + +\par \pard\plain \s2\rtlch\af13\afs36\ab \ltrch\hich\af3\loch\ilvl1\outlinelevel1\sb200\sa120\f3\fs36\b\dbch\af12\sb0\sa180{\rtlch\afs24 \ltrch\hich\af7\loch\f7\dbch\af10\loch +Security Notes} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls6 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Zenroom scripts execute in an isolated runtime.} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls6 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +All inputs are passed as structured JSON.} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls6 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Dynamic script generation restricts field names to safe identifiers.} +\par \pard\plain \s20\loch\sl276\slmult1\sb0\sa140{\listtext\pard\plain \rtlch\af5 \ltrch\hich\af5\loch\f5\dbch\af5 \u8226\'95\tab}\ilvl0\ls6 \fi-283\li0\lin0\tx0\fi-283\li709\lin709\sb0\sa180{\hich\af7\loch\fs36\b\f7\loch +Cryptographic failures are surfaced immediately as exceptions.} +\par \pard\plain \s25\rtlch\afs12 \ltrch\loch\sb0\sa283\brdrt\brdrnone\brdrl\brdrnone\brdrb\brdrdb\brdrw1\brdrcf15\brsp0\brdrr\brdrnone\noline\fs12\sb0\sa180\hich\af7\loch\fs36\b\f7\loch + \par } \ No newline at end of file diff --git a/ca_core/crypto/__pycache__/zenroom_service_client.cpython-313.pyc b/ca_core/crypto/__pycache__/zenroom_service_client.cpython-313.pyc index 9067c0461d256148fddf041a32de1ec3a8ce767c..96453b4acf2a1f2c1047d10edf20eebb66a8a9a4 100644 GIT binary patch delta 9539 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 delta 7271 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 None: + """Construct a local client. No external service configuration is needed.""" pass + # ------------------------------------------------------------------------- + # Validation helpers + # ------------------------------------------------------------------------- + @staticmethod def _require_non_empty_str(name: str, value: str) -> str: + """Validate that a value is a non-empty string and return the stripped value.""" if not isinstance(value, str): raise TypeError(f"{name} must be a string") - v = value.strip() - if not v: + value = value.strip() + if not value: raise ValueError(f"{name} cannot be empty") - return v + return value @staticmethod def _require_dict(name: str, value: Any) -> Dict[str, Any]: + """Validate that a value is a dict.""" if not isinstance(value, dict): raise TypeError(f"{name} must be a dict") return value @staticmethod def _require_keys(d: Dict[str, Any], *, required: Tuple[str, ...], ctx: str) -> None: + """Ensure a dict contains the required keys.""" missing = [k for k in required if k not in d] if missing: raise ZenroomServiceError(f"Missing {missing} in {ctx}: {d!r}") @@ -142,16 +165,31 @@ Then print the '{field_name}' @staticmethod def _require_safe_field_name(field_name: str) -> str: """ - Restrict dynamic field names used inside generated Zencode to avoid script injection. + Restrict dynamic field names used in generated Zencode. + + This avoids accidentally generating unsafe script fragments. """ if not isinstance(field_name, str): raise TypeError("field_name must be a string") if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", field_name): raise ValueError( - "field_name must match [A-Za-z_][A-Za-z0-9_]* for safe Zencode generation" + "field_name must match [A-Za-z_][A-Za-z0-9_]*" ) return field_name + # ------------------------------------------------------------------------- + # Zenroom execution helpers + # ------------------------------------------------------------------------- + + @staticmethod + def _normalize_logs(logs: Any) -> str: + """Convert Zenroom logs to a readable string for error messages.""" + if logs is None: + return "" + if isinstance(logs, list): + return "\n".join(str(item) for item in logs) + return str(logs) + def _run_script( self, script_name: str, @@ -161,25 +199,31 @@ Then print the '{field_name}' keys: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: """ - Execute a local Zencode script and return parsed JSON result as a dict. + Execute a Zencode script locally and return the parsed JSON result. + + Zenroom's Python wrapper returns an object with attributes such as: + - output : raw textual output + - logs : Zenroom logs + - result : parsed JSON object when output is JSON + + We prefer `result`, but fall back to parsing `output` if necessary. """ try: - result = zenroom.zencode_exec( + exec_result = zenroom.zencode_exec( script_text, data=json.dumps(data or {}), keys=json.dumps(keys) if keys is not None else None, ) - except Exception as e: - raise ZenroomServiceError(f"Failed to execute Zenroom script {script_name}: {e}") from e - - logs = getattr(result, "logs", None) - output = getattr(result, "output", None) - parsed = getattr(result, "result", None) + except Exception as exc: + raise ZenroomServiceError( + f"Failed to execute Zenroom script {script_name}: {exc}" + ) from exc + parsed = getattr(exec_result, "result", None) if isinstance(parsed, dict): return parsed - # Fallback: sometimes output may still be JSON text even if .result is None. + output = getattr(exec_result, "output", None) if isinstance(output, str): try: parsed_output = json.loads(output) @@ -188,23 +232,23 @@ Then print the '{field_name}' if isinstance(parsed_output, dict): return parsed_output - log_text = logs - if isinstance(log_text, list): - log_text = "\n".join(str(x) for x in log_text) - elif log_text is None: - log_text = "" - + logs_text = self._normalize_logs(getattr(exec_result, "logs", None)) out_preview = output if isinstance(output, str) else repr(output) + raise ZenroomServiceError( f"Zenroom script {script_name} did not return a JSON object.\n" f"Output: {out_preview[:800]}\n" - f"Logs: {str(log_text)[:2000]}" + f"Logs: {logs_text[:2000]}" ) - def _pick_owner_block(self, res: Dict[str, Any], my_name: str, ctx: str) -> Dict[str, Any]: + @staticmethod + def _pick_owner_block(res: Dict[str, Any], my_name: str, ctx: str) -> Dict[str, Any]: """ - Zenroom often returns: { "": { ... } } - Prefer res[my_name] when present, else accept single-entry dict. + Zenroom often returns data shaped like: + { "": { ... } } + + Prefer `res[my_name]` when present. If not present, accept a single-entry + dict as long as it is unambiguous. """ if my_name in res: owner = res[my_name] @@ -219,10 +263,48 @@ Then print the '{field_name}' raise ZenroomServiceError(f"Invalid {ctx} response structure: {res!r}") return owner + @staticmethod + def _validate_secret_box(secret: Dict[str, Any], *, ctx: str) -> Dict[str, str]: + """ + Validate the standard Zenroom encrypted object shape. + + Expected keys: + - checksum + - header + - iv + - text + """ + if not isinstance(secret, dict): + raise ZenroomServiceError(f"Invalid {ctx} response: {secret!r}") + + required = ("checksum", "header", "iv", "text") + missing = [k for k in required if not isinstance(secret.get(k), str) or not secret[k].strip()] + if missing: + raise ZenroomServiceError(f"Invalid {ctx} fields {missing}: {secret!r}") + + return { + "checksum": secret["checksum"], + "header": secret["header"], + "iv": secret["iv"], + "text": secret["text"], + } + # ------------------------------------------------------------------------- # Service 1: Generate-a-keypair,-reading-identity-from-data # ------------------------------------------------------------------------- + def generate_keypair(self, my_name: str) -> Dict[str, Any]: + """ + Generate a keypair for the supplied identity name. + + Returns a normalized structure: + { + "my_name": "", + "keyring": {"ecdh": ""}, + "private_key": "", + # optionally "public_key" if Zenroom emits it + } + """ my_name = self._require_non_empty_str("my_name", my_name) res = self._run_script( @@ -258,7 +340,9 @@ Then print the '{field_name}' # ------------------------------------------------------------------------- # Service 2: Generate-public-key # ------------------------------------------------------------------------- + def generate_public_key(self, keyring: Dict[str, Any]) -> str: + """Derive the public key from a Zenroom keyring.""" keyring = self._require_dict("keyring", keyring) res = self._run_script( @@ -267,15 +351,23 @@ Then print the '{field_name}' data={"keyring": keyring}, ) - pub = res.get("ecdh_public_key") - if not isinstance(pub, str) or not pub.strip(): + public_key = res.get("ecdh_public_key") + if not isinstance(public_key, str) or not public_key.strip(): raise ZenroomServiceError(f"Invalid public key response: {res!r}") - return pub + + return public_key # ------------------------------------------------------------------------- # Service 3: Encrypt-a-message-with-the-password # ------------------------------------------------------------------------- + def symmetric_encrypt(self, *, header: str, message: str, shared_key: str) -> Dict[str, str]: + """ + Encrypt a message symmetrically using a password/shared key. + + Returns a standard Zenroom encrypted object with checksum/header/iv/text. + Note that Zenroom may encode the returned header representation. + """ header = self._require_non_empty_str("header", header) message = self._require_non_empty_str("message", message) shared_key = self._require_non_empty_str("shared_key", shared_key) @@ -283,48 +375,44 @@ Then print the '{field_name}' res = self._run_script( "Encrypt-a-message-with-the-password", self.SCRIPT_SYMMETRIC_ENCRYPT, - data={"header": header, "message": message, "password": shared_key}, + data={ + "header": header, + "message": message, + "password": shared_key, + }, ) - sm = res.get("secret_message") - if not isinstance(sm, dict): - raise ZenroomServiceError( - f"Invalid encrypt response (missing secret_message): {res!r}" - ) - - self._require_keys(sm, required=("checksum", "header", "iv", "text"), ctx="secret_message") - for k in ("checksum", "header", "iv", "text"): - if not isinstance(sm.get(k), str) or not sm[k].strip(): - raise ZenroomServiceError(f"Invalid secret_message.{k}: {sm!r}") - - return { - "checksum": sm["checksum"], - "header": sm["header"], - "iv": sm["iv"], - "text": sm["text"], - } + secret_message = res.get("secret_message") + return self._validate_secret_box(secret_message, ctx="secret_message") # ------------------------------------------------------------------------- # Service 4: Decrypt-the-message-with-the-password # ------------------------------------------------------------------------- + def symmetric_decrypt(self, *, secret_message: Dict[str, Any], shared_key: str) -> str: + """Decrypt a symmetrically encrypted secret message and return plaintext.""" secret_message = self._require_dict("secret_message", secret_message) shared_key = self._require_non_empty_str("shared_key", shared_key) res = self._run_script( "Decrypt-the-message-with-the-password", self.SCRIPT_SYMMETRIC_DECRYPT, - data={"secret_message": secret_message, "password": shared_key}, + data={ + "secret_message": secret_message, + "password": shared_key, + }, ) - txt = res.get("textDecrypted") - if not isinstance(txt, str): + text = res.get("textDecrypted") + if not isinstance(text, str): raise ZenroomServiceError(f"Invalid decrypt response: {res!r}") - return txt + + return text # ------------------------------------------------------------------------- # Service 5: Encrypt-a-message-for-two-recipients-using-asymmetric-cryptography # ------------------------------------------------------------------------- + def asymmetric_encrypt( self, *, @@ -333,6 +421,12 @@ Then print the '{field_name}' header: str, message: str, ) -> Dict[str, str]: + """ + Encrypt a message asymmetrically for a receiver using sender key material. + + Note: the Zencode script expects the legacy spelling `reciever`, so the + payload preserves that exact key. + """ receiver_public_key = self._require_non_empty_str( "receiver_public_key", receiver_public_key ) @@ -351,27 +445,13 @@ Then print the '{field_name}' }, ) - sec = res.get("secret") - if not isinstance(sec, dict): - raise ZenroomServiceError( - f"Invalid asymmetric encrypt response (missing secret): {res!r}" - ) - - self._require_keys(sec, required=("checksum", "header", "iv", "text"), ctx="secret") - for k in ("checksum", "header", "iv", "text"): - if not isinstance(sec.get(k), str) or not sec[k].strip(): - raise ZenroomServiceError(f"Invalid secret.{k}: {sec!r}") - - return { - "checksum": sec["checksum"], - "header": sec["header"], - "iv": sec["iv"], - "text": sec["text"], - } + secret = res.get("secret") + return self._validate_secret_box(secret, ctx="secret") # ------------------------------------------------------------------------- # Service 6: Decrypt-a-message-for-two-recipients-using-asymmetric-cryptography # ------------------------------------------------------------------------- + def asymmetric_decrypt( self, *, @@ -379,6 +459,15 @@ Then print the '{field_name}' receiver_keyring: Dict[str, Any], secret: Dict[str, Any], ) -> Dict[str, str]: + """ + Decrypt an asymmetrically encrypted message. + + Returns: + { + "header": "...", + "text": "..." + } + """ sender_public_key = self._require_non_empty_str("sender_public_key", sender_public_key) receiver_keyring = self._require_dict("receiver_keyring", receiver_keyring) secret = self._require_dict("secret", secret) @@ -393,24 +482,25 @@ Then print the '{field_name}' }, ) - hdr = res.get("header") - txt = res.get("text") - if not isinstance(hdr, str) or not isinstance(txt, str): + header = res.get("header") + text = res.get("text") + if not isinstance(header, str) or not isinstance(text, str): raise ZenroomServiceError(f"Invalid asymmetric decrypt response: {res!r}") - return {"header": hdr, "text": txt} + return {"header": header, "text": text} # ------------------------------------------------------------------------- # Service 7: Sign-objects-using-asymmetric-cryptography # ------------------------------------------------------------------------- + def sign_objects(self, *, objects: Dict[str, Any], signer_keyring: Dict[str, Any]) -> Dict[str, Any]: """ - Signs exactly one string field from `objects`. + Sign exactly one string field from `objects`. - Example: - sign_objects(objects={"myMessage": "hello"}, signer_keyring=...) + Example input: + {"myMessage": "hello"} - Returns e.g.: + Example output: { "myMessage": "hello", "myMessage.signature": {"r": "...", "s": "..."} @@ -440,12 +530,12 @@ Then print the '{field_name}' ) sig_key = f"{field_name}.signature" - sig = res.get(sig_key) + signature = res.get(sig_key) - if not isinstance(sig, dict): + if not isinstance(signature, dict): raise ZenroomServiceError(f"Invalid signature object for {sig_key}: {res!r}") - if not isinstance(sig.get("r"), str) or not isinstance(sig.get("s"), str): - raise ZenroomServiceError(f"Invalid signature fields for {sig_key}: {sig!r}") + if not isinstance(signature.get("r"), str) or not isinstance(signature.get("s"), str): + raise ZenroomServiceError(f"Invalid signature fields for {sig_key}: {signature!r}") if not isinstance(res.get(field_name), str): raise ZenroomServiceError(f"Missing signed field {field_name!r} in response: {res!r}") @@ -455,6 +545,7 @@ Then print the '{field_name}' # ------------------------------------------------------------------------- # Service 8: Verify-asymmetric-cryptography-signature # ------------------------------------------------------------------------- + def verify_signature( self, *, @@ -463,6 +554,12 @@ Then print the '{field_name}' signature: Dict[str, Any], signer_public_key: str, ) -> bool: + """ + Verify a signature over one named string field. + + Returns True on success. Raises ZenroomServiceError on failure or if the + returned structure is unexpectedly malformed. + """ message_field = self._require_safe_field_name(message_field) message_value = self._require_non_empty_str("message_value", message_value) signature = self._require_dict("signature", signature) @@ -470,39 +567,50 @@ Then print the '{field_name}' script = self.SCRIPT_VERIFY_TEMPLATE.format(field_name=message_field) - payload: Dict[str, Any] = { - message_field: message_value, - f"{message_field}.signature": signature, - "signer": {"public_key": signer_public_key}, - } - res = self._run_script( "Verify-asymmetric-cryptography-signature", script, - data=payload, + data={ + message_field: message_value, + f"{message_field}.signature": signature, + "signer": {"public_key": signer_public_key}, + }, ) - out = res.get("output") - if isinstance(out, list) and out: + output = res.get("output") + if isinstance(output, list) and output: return True - # Some Zenroom variants may print the success string directly as a named field - # or return the verified message only. If execution succeeded and no exception - # was raised, we still treat that as success when the original message is present. + # Some Zenroom variants may return only the verified field itself. if res.get(message_field) == message_value: return True raise ZenroomServiceError(f"Invalid verify response: {res!r}") # ------------------------------------------------------------------------- - # Backward-compatible alias names + # Backward-compatible aliases # ------------------------------------------------------------------------- + def generate_a_keypair_reading_identity_from_data(self, my_name: str) -> Dict[str, Any]: + """Backward-compatible alias.""" return self.generate_keypair(my_name) - def encrypt_a_message_with_the_password(self, *, header: str, message: str, password: str) -> Dict[str, str]: + def encrypt_a_message_with_the_password( + self, + *, + header: str, + message: str, + password: str, + ) -> Dict[str, str]: + """Backward-compatible alias.""" return self.symmetric_encrypt(header=header, message=message, shared_key=password) - def decrypt_the_message_with_the_password(self, *, secret_message: Dict[str, Any], password: str) -> Dict[str, str]: - txt = self.symmetric_decrypt(secret_message=secret_message, shared_key=password) - return {"textDecrypted": txt} + def decrypt_the_message_with_the_password( + self, + *, + secret_message: Dict[str, Any], + password: str, + ) -> Dict[str, str]: + """Backward-compatible alias preserving the historical return shape.""" + text = self.symmetric_decrypt(secret_message=secret_message, shared_key=password) + return {"textDecrypted": text} diff --git a/tests/__pycache__/test_zenroom_service_client.cpython-313.pyc b/tests/__pycache__/test_zenroom_service_client.cpython-313.pyc index e9f17782d6796cbd69520afd7603d6053aaed493..e78ca278aaeb84775a10458082d649715d8869f5 100644 GIT binary patch delta 1865 zcmb7EU1%It6ux(6cC&vo*-SRc?k3rt#A>rijQQ8JHMOm6Y(sv?wi0a0xJ@SMYBrnR znZzc70YR)!5-tT%p{)f$-(*23K1h8N1RJYnnZEd71*_22R0@6aoSUXe3qE+4{pOrG z=brn``MDPlYz$}{sv01$7PT+3Z$wwMXzL&wr7}tB|tmWtP1# z>(~+~hQWe-NExOo|5&+7HU6^amL3KJ15zaAWqy>a_>ecIM^TI;)S^vh0e;TgK!f~4 z?{OOCcK|hhGZ-tJD*HjwTfoPavWu?bSm&Hn8T0c4ftsg8kQfH3L3kLB)q%QA>8-#h zO%}cn1gVI5b*u@!QwX~engLRhtJ&5hvm7&L*>+~eVo&31Ws`IV$Zmzbl*?HT%TAh8 z;JP&Luxj-20QuiSeL&t&M`_8tP(&4YgVhVg_9KVE#1Id8Fcyi66sS5!w)h z`V30#04e{2Ea8g=09Vg!A8XBR9{XP(E~tE2VN}(_PTK=u;9@}x6yr%f#udGr4ix(I zG$sQ%ep-2(#zmF-!Aq2i?MLYagaiEZs;(8mQPA@}MvuJ+UXO6xO3gM$k)$$){lX3- z9Kk)|%q;YxIlpikp62V;5*t8UAHp60y9_|kH)L!qvwF;nO>5ugnM9nA>y`Z1>TB>3 zVJTMlx@J_OVIGb@L3Q38-#4+@YT)jM?Ci{(nLquSHR;%Fo0Vu8q_*8RQ0;A!m_pZ& zn={S=bh!dL(g7;p#gBO31l3k}Kcuvy_72qw4Rw2ILgW#WOfqFZg|mR+36+o;hCGW0qunmMCL~LpAaI*)x#h2)?xdc2o#7x81R9 z$Lup7PpZi*(F$fRKb4)%T2osC@6w}i*Cjgs$CfL2>_zCuLo1hdUyn2wYmG~>wc3`F zhv>~EBI|+k-co>Q`nsA}QxiWXJAYEUilOlPgYONlhnm(xO~pj2m}n}-jq44)Yw>4` zjh!VwQB!{rkE-31iPpWYcKxh&-M2$<)U`I_V2%7uP2k{@Whp8qaX3}(`b=|<>&cEb z^kE5vT?m~Bdl6nj7)3aRFoTfev&s4xwvV_Tm=`4kY)!j|f0Ar7{%#F>8vofGNtq;DZi)4RJt3=N>uyc+@3p3-^f9TC8of$5-j%C#LE(x+@}x{^YJt?WWm46# z>fhH>YA@#6a;z3RzeI>{5dz%Q_SU#c!(sVNVtAx+w1^%A`wQE@J+E_fHRYP}5*?+w ze3{;*M%Pw$DUHj&)urqJa6_Fl(P;%jfxvNiLbkL;nvi$39cs!>Y}Xou1kV7tI*;Re zShjTA8o{NFP{5qZ2jnOENvPb>>ohCVMxC17hVekL9)okvJ@VLB@7&#R+ z$YX;X!eJ{u3SnQ)=ZOj}c7IPKDd_ntxl?@v*^eTeLT~|GB}@y};4JXkObCCK@dv&e~o@R)fHgkLl(bXtC7z6vjzE!tfxol}#z`%ry$ zZG~s#bY^ZGH^avf9!H46)3}-ha0d=1gby6~WhP(zzgm<$r<$ZhXJ}pFt^9+!p6XG+tx`;3S%2B7>ls zzDwY2`?_$3;Q$u+i%|TJlBwC$Ifhp@j4U1oG)0f zd=*I=Qg{p79d-}n7ust#J1sWD@kfXMAAw%jQz@mtkilIt|4?62X!+BPpH58OJ2CZ; gV6?Zc5iRxp8$YP_&syreYwuip_xhenv_jPRA3Yl#B>(^b diff --git a/tests/test_zenroom_service_client.py b/tests/test_zenroom_service_client.py index 84cb7d5..e94a157 100644 --- a/tests/test_zenroom_service_client.py +++ b/tests/test_zenroom_service_client.py @@ -1,6 +1,9 @@ import unittest -from ca_core.crypto.zenroom_service_client import ZenroomServiceClient +from ca_core.crypto.zenroom_service_client import ( + ZenroomServiceClient, + ZenroomServiceError, +) class TestZenroomServiceClient(unittest.TestCase): @@ -157,6 +160,23 @@ class TestZenroomServiceClient(unittest.TestCase): self.assertTrue(verified) + def test_9_verify_signature_rejects_modified_message(self): + alice = self.client.generate_keypair("Alice") + alice_public_key = self.client.generate_public_key(alice["keyring"]) + + signed = self.client.sign_objects( + objects={"myMessage": "hello"}, + signer_keyring=alice["keyring"], + ) + + with self.assertRaises(ZenroomServiceError): + self.client.verify_signature( + message_field="myMessage", + message_value="tampered", + signature=signed["myMessage.signature"], + signer_public_key=alice_public_key, + ) + if __name__ == "__main__": unittest.main()