Browse Source

cut things in

sync_and_key_reset
Volker Birk 3 years ago
parent
commit
f6dd7267bf
15 changed files with 1084 additions and 1957 deletions
  1. +1
    -0
      .hgignore
  2. +1
    -1
      Makefile
  3. +15
    -20
      asn.1/Makefile
  4. +14
    -13
      asn.1/pEp.asn1
  5. +3
    -3
      asn.1/pEpEngineASN1/pEpEngineASN1.vcxproj
  6. +1
    -2
      default.conf
  7. +315
    -360
      src/sync_actions.c
  8. +112
    -944
      src/sync_impl.c
  9. +59
    -37
      src/sync_impl.h
  10. +20
    -20
      sync/Makefile
  11. +42
    -7
      sync/fsm.yml2
  12. +43
    -2
      sync/functions.ysl2
  13. +38
    -256
      sync/gen_actions.ysl2
  14. +3
    -4
      sync/gen_dot.ysl2
  15. +417
    -288
      sync/gen_statemachine.ysl2

+ 1
- 0
.hgignore View File

@ -9,6 +9,7 @@ syntax: regexp
syntax: glob
*.orig
*.old
*.d
*.d.*
*.o


+ 1
- 1
Makefile View File

@ -21,9 +21,9 @@ endif
.PHONY: all
all:
$(MAKE) -C sync
$(MAKE) -C asn.1 generate
$(MAKE) -C asn.1
$(MAKE) -C sync
$(MAKE) -C src all
.PHONY: install


+ 15
- 20
asn.1/Makefile View File

@ -1,35 +1,30 @@
# Copyright 2017, pEp Foundation
# This file is part of pEpEngine
# This file may be used under the terms of the GNU General Public License version 3
# This file is under GNU General Public License 3.0
# see LICENSE.txt
include ../default.conf
ALL_SOURCE=$(wildcard *.c)
ALL_SOURCE=$(subst $(NO_SOURCE),,$(wildcard *.c))
ALL_OBJECTS=$(subst .c,.o,$(ALL_SOURCE))
all: generate
make libasn1.a
libasn1.a: $(ALL_OBJECTS)
ar -rc $@ $^
ar -rc $@ $(ALL_OBJECTS)
# "converter-sample.c" is the example file containing a "main()" function generated by ans1c.
.PHONY: generate
generate: Sync-Protocols.c
generate: Sync.c
rm -f converter-sample.c
%.o: %.c %.h
$(CC) $(CFLAGS) $(CFLAGS_GENERATED) -I. $(ASN1C_INC) -c $< -o $@
$(CC) $(CFLAGS) $(OPTIMIZE) -I. -I$(ASN1C_INC) -c $< -o $@
Sync-Protocols.c: pEp.asn1 devicegroup.asn1 protocols.asn1
$(ASN1C) -gen-PER -fincludes-quoted -fcompound-names -pdu=PEP.Message $^
Sync.c: sync.asn1 keysync.asn1 pEp.asn1
$(ASN1C) -gen-PER -fincludes-quoted -fcompound-names -pdu=PEP.Message pEp.asn1 keysync.asn1 $<
.PHONY: clean
clean:
rm -f *.a *.o *.c *.h *.sample
sync.asn1 keysync.asn1 pEp.asn1:
cp -f ../sync/generated/*.asn1 ../asn.1
.PHONY: install
install: libasn1.a
cp $< $(PREFIX)/lib/
.PHONY: clean
.PHONY: uninstall
uninstall:
rm -f $(PREFIX)/lib/libasn1.a
clean:
rm -f *.a *.o *.c *.h *.sample sync.asn1 keysync.asn1

+ 14
- 13
asn.1/pEp.asn1 View File

@ -1,5 +1,9 @@
/* This file is under GNU General Public License 3.0 */
/* see LICENSE.txt */
-- This file is under BSD License 2.0
-- Sync protocol for p≡p
-- Copyright (c) 2016, 2017 p≡p foundation
-- Written by Volker Birk
PEP
{ iso(1) org(3) dod(6) internet(1) private(4) enterprise(1) pEp(47878) basic(0) }
@ -8,27 +12,24 @@ DEFINITIONS AUTOMATIC TAGS EXTENSIBILITY IMPLIED ::=
BEGIN
EXPORTS Version, Identity, IdentityList;
EXPORTS Identity, IdentityList, TID, Hash;
ISO639-1 ::= PrintableString(FROM ("a".."z")) (SIZE(2))
Hex ::= PrintableString(FROM ("A".."F") | FROM ("0".."9"))
Hash ::= Hex(SIZE(1..128)) -- SHA1 to SHA512 in hex
Hash ::= Hex(SIZE(16..128)) -- 32bit Key ID to SHA512 in hex
PString ::= UTF8String (SIZE(1..1024))
TID ::= OCTET STRING (SIZE(16)) -- UUID version 4 variant 1
Identity ::= SEQUENCE {
address UTF8String (SIZE(1..1024)) OPTIONAL,
address PString,
fpr Hash,
user-id UTF8String (SIZE(1..1024)) OPTIONAL,
username UTF8String (SIZE(1..1024)) OPTIONAL,
comm-type INTEGER (0..255) OPTIONAL,
user-id PString,
username PString,
comm-type INTEGER (0..255),
lang ISO639-1
}
IdentityList ::= SEQUENCE OF Identity
Version ::= SEQUENCE {
major INTEGER (0..255),
minor INTEGER (0..255)
}
END

+ 3
- 3
asn.1/pEpEngineASN1/pEpEngineASN1.vcxproj View File

@ -62,7 +62,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PreBuildEvent>
<Command>cd "$(ProjectDir).." &amp;&amp; "$(SolutionDir)Tools\asn1c\bin\asn1c" -S "$(SolutionDir)Tools\asn1c\share\asn1c" -gen-PER -fincludes-quoted -fcompound-names -pdu=PEP.Message pEp.asn1 devicegroup.asn1 protocols.asn1
<Command>cd "$(ProjectDir).." &amp;&amp; "$(SolutionDir)Tools\asn1c\bin\asn1c" -S "$(SolutionDir)Tools\asn1c\share\asn1c" -gen-PER -fincludes-quoted -fcompound-names -pdu=PEP.Message pEp.asn1 keysync.asn1 sync.asn.1
cd "$(ProjectDir).." &amp;&amp; del converter-sample.c
</Command>
</PreBuildEvent>
@ -99,7 +99,7 @@ cd "$(ProjectDir).." &amp;&amp; del converter-sample.c
<OptimizeReferences>true</OptimizeReferences>
</Link>
<PreBuildEvent>
<Command>cd "$(ProjectDir).." &amp;&amp; "$(SolutionDir)Tools\asn1c\bin\asn1c" -S "$(SolutionDir)Tools\asn1c\share\asn1c" -gen-PER -fincludes-quoted -fcompound-names -pdu=PEP.Message pEp.asn1 devicegroup.asn1 protocols.asn1
<Command>cd "$(ProjectDir).." &amp;&amp; "$(SolutionDir)Tools\asn1c\bin\asn1c" -S "$(SolutionDir)Tools\asn1c\share\asn1c" -gen-PER -fincludes-quoted -fcompound-names -pdu=PEP.Message pEp.asn1 keysync.asn1 sync.asn.1
cd "$(ProjectDir).." &amp;&amp; del converter-sample.c
</Command>
<Message>compiling ASN.1 description</Message>
@ -215,4 +215,4 @@ cd "$(ProjectDir).." &amp;&amp; del converter-sample.c
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

+ 1
- 2
default.conf View File

@ -182,8 +182,7 @@ YML2_OPTS=--encoding=utf8
ASN1C=asn1c
# asn1c include search flag
ASN1C_INC=
#ASN1C_INC=-I$(HOME)/include
ASN1C_INC=-I$(PREFIX)/include
######### libetpan #########


+ 315
- 360
src/sync_actions.c View File

@ -1,454 +1,409 @@
// This file is under GNU General Public License 3.0
// see LICENSE.txt
// Actions for DeviceState state machine
#include "Sync_impl.h"
#include "KeySync_fsm.h"
#include <assert.h>
#include "pEp_internal.h"
#include "message.h"
#include "sync_fsm.h"
#include "sync_impl.h"
#include "map_asn1.h"
#include "baseprotocol.h"
PEP_STATUS deviceGrouped(PEP_SESSION session, bool *result)
{
assert(session && result);
if (!(session && result))
return PEP_ILLEGAL_VALUE;
// conditions
static const char *sql = "select count(*) from identity where user_id = '"PEP_OWN_USERID"' and (flags & 4) = 4;";
static const size_t len = strlen(sql);
sqlite3_stmt *_sql;
int int_result = sqlite3_prepare_v2(session->db, sql, (int) len, &_sql, NULL);
assert(int_result == SQLITE_OK);
if (!(int_result == SQLITE_OK))
return PEP_UNKNOWN_ERROR;
int _result = 0;
int_result = sqlite3_step(_sql);
assert(int_result == SQLITE_ROW);
if (int_result == SQLITE_ROW)
_result = sqlite3_column_int(_sql, 0);
sqlite3_finalize(_sql);
if (int_result != SQLITE_ROW)
return PEP_UNKNOWN_ERROR;
*result = _result > 0;
return PEP_STATUS_OK;
}
int deviceGrouped(PEP_SESSION session)
PEP_STATUS challengeAccepted(PEP_SESSION session, bool *result)
{
assert(session);
if (!session)
return invalid_condition; // error
assert(session && result);
if (!(session && result))
return PEP_ILLEGAL_VALUE;
TID_t *t1 = &session->sync_state.keysync.challenge;
TID_t *t2 = &session->own_sync_state.challenge;
char *devgrp = NULL;
int res = 0;
PEP_STATUS status;
*result = t1->size == t2->size && memcmp(t1->buf, t2->buf, t1->size) == 0;
status = get_device_group(session, &devgrp);
return PEP_STATUS_OK;
}
if (status == PEP_STATUS_OK && devgrp && devgrp[0])
res = 1;
PEP_STATUS partnerIsGrouped(PEP_SESSION session, bool *result)
{
assert(session && result);
if (!(session && result))
return PEP_ILLEGAL_VALUE;
free(devgrp);
*result = session->sync_state.keysync.is_group;
return res;
return PEP_STATUS_OK;
}
int keyElectionWon(PEP_SESSION session, Identity partner)
PEP_STATUS keyElectionWon(PEP_SESSION session, bool *result)
{
assert(session);
assert(partner);
if (!(session && partner))
return invalid_condition; // error
int partner_is_group = partner->flags & PEP_idf_devicegroup;
if (deviceGrouped(session)){
// existing group always wins against sole device
if(!partner_is_group)
return 1;
} else {
// sole device always loses against group
if(partner_is_group)
return 0;
}
assert(session && result);
if (!(session && result))
return PEP_ILLEGAL_VALUE;
// two groups or two sole are elected based on key age
// key created first wins
pEp_identity *from = session->sync_state.basic.from;
Identity me = NULL;
char* own_id = NULL;
PEP_STATUS status = get_default_own_userid(session, &own_id);
if (own_id) {
status = get_identity(session, partner->address, own_id,
&me);
free(own_id);
assert(from && from->fpr && from->fpr[0] && from->address && from->address[0]);
if (!(from && from->fpr && from->fpr[0] && from->address && from->address[0]))
return PEP_ILLEGAL_VALUE;
pEp_identity *me = NULL;
PEP_STATUS status = get_identity(session, from->address, PEP_OWN_USERID, &me);
assert(status == PEP_STATUS_OK);
if (status)
return status;
assert(me->fpr && me->fpr[0]);
if (!(me->fpr && me->fpr[0])) {
free_identity(me);
return PEP_ILLEGAL_VALUE;
}
if (status == PEP_OUT_OF_MEMORY)
return invalid_out_of_memory;
if (status != PEP_STATUS_OK)
return invalid_condition; // error
int result = invalid_condition; // error state has to be overwritten
size_t len = MIN(strlen(from->fpr), strlen(me->fpr));
*result = strncasecmp(from->fpr, me->fpr, len) > 0;
free_identity(me);
time_t own_created;
time_t partners_created;
return PEP_STATUS_OK;
}
status = key_created(session, me->fpr, &own_created);
if (status != PEP_STATUS_OK)
goto the_end;
PEP_STATUS closeHandshakeDialog(PEP_SESSION session)
{
assert(session);
if (!session)
return PEP_ILLEGAL_VALUE;
status = key_created(session, partner->fpr, &partners_created);
if (status != PEP_STATUS_OK)
goto the_end;
assert(session->notifyHandshake);
if (!session->notifyHandshake)
return PEP_SYNC_NO_NOTIFY_CALLBACK;
if (own_created > partners_created)
result = 0;
else
result = 1;
PEP_STATUS status = session->notifyHandshake(
session->sync_management, NULL, NULL, SYNC_NOTIFY_OVERTAKEN);
if (status)
return status;
the_end:
free_identity(me);
return result;
return PEP_STATUS_OK;
}
int sameIdentities(PEP_SESSION session, Identity a, Identity b)
PEP_STATUS openChallenge(PEP_SESSION session)
{
assert(session);
assert(a);
assert(b);
if (!(session && a && b))
return invalid_condition; // error
if (a->fpr == NULL || b->fpr == NULL ||
(!_same_fpr(a->fpr, strlen(a->fpr), b->fpr, strlen(b->fpr))) ||
a->address == NULL || b->address == NULL ||
strcmp(a->address, b->address) != 0 ||
a->user_id == NULL || b->user_id == NULL ||
strcmp(a->user_id, b->user_id) != 0)
return 0;
return 1;
if (!session)
return PEP_ILLEGAL_VALUE;
pEpUUID c;
uuid_generate_random(c);
OCTET_STRING_fromBuf(&session->own_sync_state.challenge, c, 16);
return PEP_STATUS_OK;
}
int sameKeyAndAddress(PEP_SESSION session, Identity a, Identity b)
PEP_STATUS storeChallenge(PEP_SESSION session)
{
assert(session);
assert(a);
assert(b);
if (!(session && a && b))
return invalid_condition; // error
if (a->fpr == NULL || b->fpr == NULL ||
(!_same_fpr(a->fpr, strlen(a->fpr), b->fpr, strlen(b->fpr))) ||
a->address == NULL || b->address == NULL ||
strcmp(a->address, b->address) != 0)
return 0;
return 1;
}
if (!session)
return PEP_ILLEGAL_VALUE;
TID_t *src = &session->sync_state.keysync.challenge;
TID_t *dst = &session->own_sync_state.challenge;
// actions
assert(src->size == 16);
if (!(src->size == 16))
return PEP_UNKNOWN_ERROR;
PEP_STATUS _notifyHandshake(
PEP_SESSION session,
Identity partner,
sync_handshake_signal signal
)
OCTET_STRING_fromBuf(dst, (char *) src->buf, src->size);
return PEP_STATUS_OK;
}
PEP_STATUS openTransaction(PEP_SESSION session)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
if (!session)
return PEP_ILLEGAL_VALUE;
pEpUUID c;
uuid_generate_random(c);
OCTET_STRING_fromBuf(&session->own_sync_state.transaction, c, 16);
return PEP_STATUS_OK;
}
PEP_STATUS storeTransaction(PEP_SESSION session)
{
assert(session);
assert(partner);
if (!session)
return PEP_ILLEGAL_VALUE;
TID_t *src = &session->sync_state.keysync.transaction;
TID_t *dst = &session->own_sync_state.transaction;
assert(src->size == 16);
if (!(src->size == 16))
return PEP_UNKNOWN_ERROR;
if (!(session && partner))
OCTET_STRING_fromBuf(dst, (char *) src->buf, src->size);
return PEP_STATUS_OK;
}
PEP_STATUS showSoleHandshake(PEP_SESSION session)
{
assert(session);
if (!session)
return PEP_ILLEGAL_VALUE;
assert(session->notifyHandshake);
if (!session->notifyHandshake)
return PEP_SYNC_NO_NOTIFY_CALLBACK;
assert(session->sync_state.basic.from);
if (!session->sync_state.basic.from)
return PEP_ILLEGAL_VALUE;
char* own_id = NULL;
status = get_default_own_userid(session, &own_id);
// notifyHandshake take ownership of given identities
pEp_identity *me = NULL;
if (own_id) {
status = get_identity(session, partner->address, own_id, &me);
free(own_id);
}
if (status != PEP_STATUS_OK)
goto error;
pEp_identity *_partner = NULL;
_partner = identity_dup(partner);
if (_partner == NULL){
status = PEP_OUT_OF_MEMORY;
goto error;
PEP_STATUS status = get_identity(session, from->address, PEP_OWN_USERID, &me);
assert(status == PEP_STATUS_OK);
if (status)
return status;
assert(me->fpr && me->fpr[0]);
if (!(me->fpr && me->fpr[0])) {
free_identity(me);
return PEP_ILLEGAL_VALUE;
}
status = session->notifyHandshake(session->sync_obj, me, _partner, signal);
if (status != PEP_STATUS_OK)
goto error;
pEp_identity *partner = identity_dup(session->sync_state.basic.from);
if (!partner) {
free_identity(me);
return PEP_OUT_OF_MEMORY;
}
return status;
PEP_STATUS status = session->notifyHandshake(
session->sync_management, me, partner, SYNC_NOTIFY_INIT_FORM_GROUP);
if (status)
return status;
error:
free_identity(me);
return status;
return PEP_STATUS_OK;
}
// acceptHandshake() - stores acception of partner
//
// params:
// session (in) session handle
// state (in) state the state machine is in
// partner (in) partner to communicate with
//
// returns:
// PEP_STATUS_OK or any other value on error
PEP_STATUS acceptHandshake(
PEP_SESSION session,
DeviceState_state state,
Identity partner,
void *extra
)
PEP_STATUS disable(PEP_SESSION session)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(partner);
assert(extra == NULL);
if (!(session && partner))
if (!session)
return PEP_ILLEGAL_VALUE;
status = trust_personal_key(session, partner);
return status;
return PEP_STATUS_OK;
}
// rejectHandshake() - stores rejection of partner
//
// params:
// session (in) session handle
// state (in) state the state machine is in
// partner (in) partner to communicate with
//
// returns:
// PEP_STATUS_OK or any other value on error
PEP_STATUS rejectHandshake(
PEP_SESSION session,
DeviceState_state state,
Identity partner,
void *extra
)
PEP_STATUS saveGroupKeys(PEP_SESSION session)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(partner);
assert(extra == NULL);
if (!(session && partner))
if (!session)
return PEP_ILLEGAL_VALUE;
// TODO : disable sync globally if not in a group
status = set_identity_flags(session, partner,
partner->flags | PEP_idf_not_for_sync);
identity_list *il = IdentityList_to_identity_list(&session->sync_state.keysync.identities, NULL);
if (!il)
return PEP_OUT_OF_MEMORY;
// BUG: this should be a transaction and been rolled back completely on error
for (identity_list *_il = il; _il && _il->ident; _il = _il->next) {
PEP_STATUS status = set_identity(session, _il->ident);
if (status) {
free_identity_list(il);
return status;
}
}
free_identity_list(il);
return status;
return PEP_STATUS_OK;
}
PEP_STATUS _storeGroupKeys(
PEP_SESSION session,
identity_list *group_keys
)
PEP_STATUS ownKeysAreGroupKeys(PEP_SESSION session)
{
PEP_STATUS status = PEP_STATUS_OK;
char* own_id = NULL;
status = get_default_own_userid(session, &own_id);
// FIXME: Is this where and what we wanna do with this?
if (status != PEP_STATUS_OK)
return status;
for (identity_list *il = group_keys; il && il->ident; il = il->next) {
assert(session);
if (!session)
return PEP_ILLEGAL_VALUE;
if (strcmp(il->ident->user_id, own_id)!=0) {
assert(0);
continue;
}
// Check that identity isn't excluded from sync.
pEp_identity *stored_identity = NULL;
status = get_identity(session, il->ident->address, own_id,
&stored_identity);
if (status == PEP_STATUS_OK) {
if(stored_identity->flags & PEP_idf_not_for_sync){
free_identity(stored_identity);
continue;
static const char *sql = "select fpr, username, comm_type, lang,"
" identity.flags | pgp_keypair.flags"
" from identity"
" join person on id = identity.user_id"
" join pgp_keypair on fpr = identity.main_key_id"
" join trust on id = trust.user_id"
" and pgp_keypair_fpr = identity.main_key_id"
" where identity.user_id = '" PEP_OWN_USERID "';";
static const size_t len = strlen(sql);
sqlite3_stmt *_sql;
int int_result = sqlite3_prepare_v2(session->db, sql, (int) len, &_sql, NULL);
assert(int_result == SQLITE_OK);
if (!(int_result == SQLITE_OK))
return PEP_UNKNOWN_ERROR;
identity_list *il = new_identity_list(NULL);
if (!il)
return PEP_OUT_OF_MEMORY;
identity_list *_il = il;
int result;
do {
result = sqlite3_step(_sql);
pEp_identity *_identity = NULL;
switch (result) {
case SQLITE_ROW:
_identity = new_identity(
address,
(const char *) sqlite3_column_text(_sql, 0),
user_id,
(const char *) sqlite3_column_text(_sql, 1)
);
assert(_identity);
if (_identity == NULL)
return PEP_OUT_OF_MEMORY;
_identity->comm_type = (PEP_comm_type)
sqlite3_column_int(_sql, 2);
const char* const _lang = (const char *)
sqlite3_column_text(_sql, 3);
if (_lang && _lang[0]) {
assert(_lang[0] >= 'a' && _lang[0] <= 'z');
assert(_lang[1] >= 'a' && _lang[1] <= 'z');
assert(_lang[2] == 0);
_identity->lang[0] = _lang[0];
_identity->lang[1] = _lang[1];
_identity->lang[2] = 0;
}
free_identity(stored_identity);
}
_identity->flags = (unsigned int)
sqlite3_column_int(_sql, 4);
_il = identity_list_add(_il, _identity);
if (!_il) {
free_identity_list(il);
free_identity(_identity);
return PEP_OUT_OF_MEMORY;
}
break;
status = set_identity(session, il->ident);
if (status != PEP_STATUS_OK)
case SQLITE_DONE:
break;
}
free(own_id);
return status;
default:
free_identity_list(il);
return PEP_UNKOWN_ERROR;
}
} while (result != SQLITE_DONE);
IdentityList_t *r = IdentityList_from_identity_list(il, &session->sync_state.keysync.identities);
free_identity_list(il);
if (!r)
return PEP_OUT_OF_MEMORY;
return PEP_STATUS_OK;
}
// storeGroupKeys() -
//
// params:
// session (in) session handle
// state (in) state the state machine is in
// partner (in) partner to communicate with
// _group_keys (in) group keys received from partner
//
// returns:
// PEP_STATUS_OK or any other value on error
PEP_STATUS storeGroupKeys(
PEP_SESSION session,
DeviceState_state state,
Identity partner,
void *group_keys_extra_
)
PEP_STATUS showJoinGroupHandshake(PEP_SESSION session)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(partner);
assert(group_keys_extra_);
if (!(session && partner && group_keys_extra_))
if (!session)
return PEP_ILLEGAL_VALUE;
group_keys_extra_t *group_keys_extra =
(group_keys_extra_t*) group_keys_extra_;
identity_list *group_keys = group_keys_extra->group_keys;
char *group_id = group_keys_extra->group_id;
status = _storeGroupKeys(session, group_keys);
if (status != PEP_STATUS_OK)
return status;
assert(session->notifyHandshake);
if (!session->notifyHandshake)
return PEP_SYNC_NO_NOTIFY_CALLBACK;
assert(session->sync_state.basic.from);
if (!session->sync_state.basic.from)
return PEP_ILLEGAL_VALUE;
// set group id according to given group-id
status = set_device_group(session, group_id);
if (status != PEP_STATUS_OK)
pEp_identity *me = NULL;
PEP_STATUS status = get_identity(session, from->address, PEP_OWN_USERID, &me);
assert(status == PEP_STATUS_OK);
if (status)
return status;
return status;
}
// storeGroupUpdate() -
//
// params:
// session (in) session handle
// state (in) state the state machine is in
// partner (in) partner to communicate with
// _group_keys (in) group keys received from partner
//
// returns:
// PEP_STATUS_OK or any other value on error
PEP_STATUS storeGroupUpdate(
PEP_SESSION session,
DeviceState_state state,
Identity partner,
void *group_keys_
)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(partner);
assert(group_keys_);
if (!(session && partner && group_keys_))
assert(me->fpr && me->fpr[0]);
if (!(me->fpr && me->fpr[0])) {
free_identity(me);
return PEP_ILLEGAL_VALUE;
}
identity_list *group_keys = (identity_list*) group_keys_;
status = _storeGroupKeys(session, group_keys);
pEp_identity *partner = identity_dup(session->sync_state.basic.from);
if (!partner) {
free_identity(me);
return PEP_OUT_OF_MEMORY;
}
PEP_STATUS status = session->notifyHandshake(
session->sync_management, me, partner, SYNC_NOTIFY_INIT_ADD_OUR_DEVICE);
if (status)
return status;
return status;
return PEP_STATUS_OK;
}
// makeGroup() -
//
// params:
// session (in) session handle
// state (in) state the state machine is in
// partner (in) ignored
// extra (in) ignored
//
// returns:
// PEP_STATUS_OK or any other value on error
PEP_STATUS makeGroup(
PEP_SESSION session,
DeviceState_state state,
Identity partner,
void *extra
)
PEP_STATUS showGroupedHandshake(PEP_SESSION session)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
// make a new uuid
char new_uuid[37];
pEpUUID uuid;
uuid_generate_random(uuid);
uuid_unparse_upper(uuid, new_uuid);
// take that new uuid as group-id
status = set_device_group(session, new_uuid);
return status;
}
if (!session)
return PEP_ILLEGAL_VALUE;
// renewUUID() -
//
// params:
// session (in) session handle
// state (in) state the state machine is in
// partner (in) ignored
// extra (in) ignored
//
// returns:
// PEP_STATUS_OK or any other value on error
PEP_STATUS renewUUID(
PEP_SESSION session,
DeviceState_state state,
Identity partner,
void *extra
)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session->notifyHandshake);
if (!session->notifyHandshake)
return PEP_SYNC_NO_NOTIFY_CALLBACK;
assert(session->sync_state.basic.from);
if (!session->sync_state.basic.from)
return PEP_ILLEGAL_VALUE;
assert(session);
pEp_identity *me = NULL;
PEP_STATUS status = get_identity(session, from->address, PEP_OWN_USERID, &me);
assert(status == PEP_STATUS_OK);
if (status)
return status;
// change sync_uuid when entering group
// thus ignoring unprocessed handshakes
// addressed to previous self (sole) once in.
pEpUUID uuid;
uuid_generate_random(uuid);
uuid_unparse_upper(uuid, session->sync_uuid);
return status;
}
assert(me->fpr && me->fpr[0]);
if (!(me->fpr && me->fpr[0])) {
free_identity(me);
return PEP_ILLEGAL_VALUE;
}
// leaveGroup() -
//
// params:
// session (in) session handle
// state (in) state the state machine is in
// partner (in) ignored
// extra (in) ignored
//
// returns:
// PEP_STATUS_OK or any other value on error
PEP_STATUS leaveGroup(
PEP_SESSION session,
DeviceState_state state,
Identity partner,
void *extra
)
{
PEP_STATUS status = PEP_STATUS_OK;
pEp_identity *partner = identity_dup(session->sync_state.basic.from);
if (!partner) {
free_identity(me);
return PEP_OUT_OF_MEMORY;
}
assert(session);
PEP_STATUS status = session->notifyHandshake(
session->sync_management, me, partner, SYNC_NOTIFY_INIT_ADD_OTHER_DEVICE);
if (status)
return status;
status = set_device_group(session, NULL);
return status;
return PEP_STATUS_OK;
}

+ 112
- 944
src/sync_impl.c
File diff suppressed because it is too large
View File


+ 59
- 37
src/sync_impl.h View File

@ -3,60 +3,82 @@
#pragma once
#include "../asn.1/DeviceGroup-Protocol.h"
#include "message.h"
#include "sync.h"
#include "sync_fsm.h"
#include "fsm_common.h"
#include "message_api.h"
#include "../asn.1/Sync.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _group_keys_extra {
identity_list *group_keys;
char *group_id;
} group_keys_extra_t;
// event struct
void free_group_keys_extra(group_keys_extra_t* groupkeys);
group_keys_extra_t* group_keys_extra_dup(group_keys_extra_t* groupkeys);
typedef struct _Sync_event {
Sync_PR fsm;
int event;
Sync_t *msg;
} Sync_event_t;
PEP_STATUS receive_sync_msg(
PEP_SESSION session,
sync_msg_t *sync_msg,
time_t *timeout
// conditions
PEP_STATUS deviceGrouped(PEP_SESSION session, bool *result);
PEP_STATUS challengeAccepted(PEP_SESSION session, bool *result);
PEP_STATUS partnerIsGrouped(PEP_SESSION session, bool *result);
PEP_STATUS keyElectionWon(PEP_SESSION session, bool *result);
// actions
PEP_STATUS closeHandshakeDialog(PEP_SESSION session);
PEP_STATUS openChallenge(PEP_SESSION session);
PEP_STATUS storeChallenge(PEP_SESSION session);
PEP_STATUS openTransaction(PEP_SESSION session);
PEP_STATUS storeTransaction(PEP_SESSION session);
PEP_STATUS showSoleHandshake(PEP_SESSION session);
PEP_STATUS disable(PEP_SESSION session);
PEP_STATUS saveGroupKeys(PEP_SESSION session);
PEP_STATUS ownKeysAreGroupKeys(PEP_SESSION session);
PEP_STATUS showJoinGroupHandshake(PEP_SESSION session);
PEP_STATUS showGroupedHandshake(PEP_SESSION session);
// send event to own state machine, use state to generate
// Sync message if necessary
PEP_STATUS Sync_send(
PEP_SESSION session,
Sync_PR fsm,
int message_type
);
PEP_STATUS inject_DeviceState_event(
PEP_SESSION session,
DeviceState_event event,
Identity partner,
void *extra);
// send message to partners
PEP_STATUS receive_DeviceState_msg(
PEP_SESSION session,
message *src,
PEP_rating rating,
stringlist_t *keylist);
PEP_STATUS send_Sync_message(
PEP_SESSION session,
Sync_PR fsm,
int event
);
DeviceGroup_Protocol_t *new_DeviceGroup_Protocol_msg(DeviceGroup_Protocol__payload_PR type);
void free_DeviceGroup_Protocol_msg(DeviceGroup_Protocol_t *msg);
// receive event, free Sync_event_t structure if call does not fail
// with PEP_ILLEGAL_VALUE
PEP_STATUS unicast_msg(
PEP_SESSION session,
const Identity partner,
DeviceState_state state,
DeviceGroup_Protocol_t *msg,
bool encrypted
PEP_STATUS recv_Sync_event(
PEP_SESSION session,
Sync_event_t *ev
);
PEP_STATUS multicast_self_msg(
// state machine driver
// if fsm or event set to 0 use fields in src if present
PEP_STATUS Sync_driver(
PEP_SESSION session,
DeviceState_state state,
DeviceGroup_Protocol_t *msg,
bool encrypted
Sync_PR fsm,
int event
);
PEP_STATUS inject_Sync_event(
PEP_SESSION session,
Sync_PR fsm,
int event
);
bool is_double(DeviceGroup_Protocol_t *msg);
#ifdef __cplusplus
}


+ 20
- 20
sync/Makefile View File

@ -1,38 +1,38 @@
# Copyright 2017, pEp Foundation
# This file is part of pEpEngine
# This file may be used under the terms of the GNU General Public License version 3
# This file is under GNU General Public License 3.0
# see LICENSE.txt
include ../default.conf
.PHONY: all
all: .codegen
# Currently not in use, kept for historic reasons
skeleton: .actions
.codegen: .statemachines .actions .codecs .messages
cp -f generated/*.c generated/*.h ../src
touch .codegen
.codegen: .statemachines .actions
cp -f generated/*.* ../src
touch $@
.actions: sync.fsm gen_actions.ysl2 fsm.yml2 functions.ysl2 cond_act.yml2
$(YML2_PROC) -y gen_actions.ysl2 $< -o $@
.actions: devicegroup.fsm gen_actions.ysl2 fsm.yml2 functions.ysl2
$(YML2_PROC) $(YML2_OPTS) -y gen_actions.ysl2 $< -o $@
.statemachines: sync.fsm gen_statemachine.ysl2 fsm.yml2 functions.ysl2
$(YML2_PROC) -y gen_statemachine.ysl2 $< -o $@
.statemachines: devicegroup.fsm gen_statemachine.ysl2 fsm.yml2 functions.ysl2
$(YML2_PROC) $(YML2_OPTS) -y gen_statemachine.ysl2 $< -o $@
.codecs: sync.fsm gen_message_func.ysl2 fsm.yml2 functions.ysl2
$(YML2_PROC) -y gen_message_func.ysl2 $< -o $@
.messages: sync.fsm gen_messages.ysl2 fsm.yml2 functions.ysl2
$(YML2_PROC) -y gen_messages.ysl2 $< -o $@
.PHONY: clean
clean:
rm -f *.xml *.xsl *.dot *.svg \
$(patsubst generated/%,../src/%,$(wildcard generated/*.*)) \
../generated/* ../skeletons/* .statemachines .actions .codegen \
generated/Makefile.protocols
rm -f *.xml *.xsl \
$(pathsub generated/%, ../src/% $(wildcard generated/*.*)) \
../generated/* .statemachines .actions .codecs .messages *.dot *.svg
%.xml: %.fsm
$(YML2_PATH)/yml2c $< -o $@
yml2c $< -o $@
%.dot: gen_dot.ysl2 devicegroup.fsm
$(YML2_PROC) $(YML2_OPTS) -y $^
%.dot: sync.fsm gen_dot.ysl2
yml2proc -y gen_dot.ysl2 $<
%.svg: %.dot
dot -Tsvg -o $@ $<

+ 42
- 7
sync/fsm.yml2 View File

@ -1,19 +1,54 @@
// This file is under GNU General Public License 3.0
// see LICENSE.txt
// FSM Y language 1.0
// FSM Y language 1.2
// Copyleft (c) 2016, p≡p foundation
// Copyleft (c) 2016, 2017, p≡p foundation
// Written by Volker Birk
decl protocol @name;
decl fsm @name;
decl version(major, minor);
// a protocol family has a name and an ID
decl protocol @name (id);
// each protocol in a family has a finite state machine
decl fsm< protocol >;
// a state has a name and a timeout; after the timeout the state machine will
// be reset
decl state @name (timeout=0);
// events have names
decl event @name, on is event;
// a transistion moves the statemachine to another state
decl transition @target, go is transition;
// an action is executed; if an action name starts with 'send' then it is
// sending a message
decl action @name, do is action;
// a condition is for different cases
decl condition @name, if is condition;
decl alternative, else is alternative;
decl interface @name;
decl tag @name (id);
// some events have messages on the line signalling the event to the
// communication partner
decl message @name (id);
// messages can have transmitted fields…
decl field @type @name;
// … or automatically calculated fields…
decl auto < field >;

+ 43
- 2
sync/functions.ysl2 View File

@ -8,8 +8,49 @@ def "func:distinctName" {
choose {
when "not($nodes)"
result "/..";
otherwise {
otherwise
result "$nodes[1] | func:distinctName($nodes[position() > 1])[@name != $nodes[1]/@name]";
}
}
}
def "func:distinctType" {
param "nodes", "/..";
choose {
when "not($nodes)"
result "/..";
otherwise
result "$nodes[1] | func:distinctType($nodes[position() > 1])[@type != $nodes[1]/@type]";
}
}
def "func:asn1name"
result "translate(@name, '_', '-')";
def "func:asn1type" {
choose {
when "@type='bool'"
result "'BOOLEAN'";
when "@type='int'"
result "'INTEGER'";
otherwise
result "translate(@type, '_', '-')";
}
}
def "func:basicType" {
choose {
when "substring(@type,1,1)=yml:lcase(substring(@type,1,1))"
result "true()";
otherwise
result "false()";
}
}
def "func:ctype" {
choose {
when "func:basicType()"
result "@type";
otherwise
result "concat(@type,'_t')";
}
}

+ 38
- 256
sync/gen_actions.ysl2 View File

@ -1,297 +1,79 @@
// This file is under GNU General Public License 3.0
// see LICENSE.txt
// generate actions skeleton
// generate conditions and actions
// Copyleft (c) 2016, p≡p foundation
// Copyleft (c) 2017, p≡p foundation
// Written by Volker Birk
include yslt.yml2
decl _func *name (*type) alias - {
template %name=*name, %type=*type, "%type[@name='%name']"
call *type with "content" content;
};
decl condition is _func (*type="condition");
decl action is _func (*type="action");
tstylesheet {
include standardlib.ysl2
include ./functions.ysl2
template "/protocol" {
apply "fsm", mode=send, 0;
apply "fsm", mode=other, 0;
}
template "fsm", mode=send document "generated/{@filename}_send_actions.c", "text" {
const "name", "@name";
const "filename", "@filename";
||
// Send Actions for «@name» state machine
#include <assert.h>
#include "pEp_internal.h"
#include "keymanagement.h"
#include "message.h"
#include "«@filename»_fsm.h"
#include "baseprotocol.h"
#include "map_asn1.h"
#include "../asn.1/DeviceGroup-Protocol.h"
#include "sync_impl.h"
||
for "func:distinctName(//action)"
if "substring(@name, 1, 4) = 'send'"
| #include "../asn.1/«substring(@name, 5, 255)».h"
|
for "func:distinctName(//action)"
if "substring(@name, 1, 4) = 'send'"
call "send_action"
with "action", ".",
with "fsm", "$name",
with "filename", "$filename";
include ./cond_act.yml2
||
PEP_STATUS _notifyHandshake(
PEP_SESSION session,
Identity partner,
sync_handshake_signal signal
);
||
for "func:distinctName(//action)"
if "substring(@name, 1, 6) = 'notify'"
call "notify_action"
with "action", ".",
with "fsm", "$name",
with "filename", "$filename";
}
template "fsm", mode=other document "skeletons/{@filename}_actions.c", "text" {
const "name", "@name";
const "filename", "@filename";
||
// Actions for «@name» state machine
#include <assert.h>
#include "pEp_internal.h"
#include "keymanagement.h"
#include "message.h"
#include "«@filename»_fsm.h"
#include "../asn.1/DeviceGroup-Protocol.h"
||
for "func:distinctName(//action)"
if "substring(@name, 1, 4) != 'send'"
call "other_action"
with "action", ".",
with "fsm", "$name",
with "filename", "$filename";
}
function "paramcheck" {
param "partner";
|> assert(session);
choose {
when "$partner"
||
assert(partner);
if (!(session && partner))
return PEP_ILLEGAL_VALUE;
||
otherwise
||
assert(!partner);
if (!(session && !partner))
return PEP_ILLEGAL_VALUE;
template "/protocol" {
document "generated/{@name}_actions.c", "text" {
||
}
}
function "other_action" {
param "action";
param "fsm";
param "filename", "'###'";
||
// This file is under GNU General Public License 3.0
// see LICENSE.txt
// «$action/@name»() -
//
// params:
// session (in) session handle
// state (in) state the state machine is in
`` if "parm" | // partner (in) partner to communicate with
`` if "not(parm)" | // partner (in) (must be NULL)
//
// returns:
// PEP_STATUS_OK or any other value on error
#include "«@name»_impl.h"
`` for "fsm" | #include "«@name»_fsm.h"
PEP_STATUS «$action/@name»(
PEP_SESSION session,
«$fsm»_state state,
Identity partner,
void *extra
)
{
PEP_STATUS status = PEP_STATUS_OK;
`` call "paramcheck" with "partner", "parm/partner";
// working code
// free extra
return status;
enomem:
status = PEP_OUT_OF_MEMORY;
error:
// free extra
return status;
||
apply "func:distinctName(//condition)", 0;
apply "func:distinctName(//action[not(starts-with(@name, 'send'))])", 0;
}
||
}
function "send_action" {
param "action";
param "fsm";
param "filename", "'###'";
const "name", "substring($action/@name, 5, 255)";
const "lname", "concat(yml:lcase(substring($name, 1, 1)), substring($name, 2))";
template "condition" | #error condition «@name» not implemented\n
template "action" | #error action «@name» not implemented\n
function "condition" {
param "content";
||
// «$action/@name»() - send «$name» message
//
// params:
// session (in) session handle
// state (in) state the state machine is in
`` if "parm" | // partner (in) partner to communicate with
`` if "not(parm)" | // partner (in) (must be NULL)
//
// returns:
// PEP_STATUS_OK or any other value on error
PEP_STATUS «$action/@name»(
PEP_SESSION session,
«$fsm»_state state,
Identity partner,
void *extra
)
PEP_STATUS «@name»(PEP_SESSION session, bool *result)
{
assert(session && state);
if (!(session && state))
assert(session && result);
if (!(session && result))
return PEP_ILLEGAL_VALUE;
PEP_STATUS status = PEP_STATUS_OK;
`` if "$name='GroupKeys' or $name='GroupUpdate'" |> identity_list *kl = new_identity_list(NULL);
DeviceGroup_Protocol_t *msg = new_DeviceGroup_Protocol_msg(DeviceGroup_Protocol__payload_PR_«$lname»);
if (!msg)
goto enomem;
||
choose {
when "$name='GroupKeys' or $name='GroupUpdate'" {
|
|> status = _own_identities_retrieve(session, &kl, PEP_idf_not_for_sync);
|> if (status != PEP_STATUS_OK)
|>> goto error;
|> if (IdentityList_from_identity_list(kl, &msg->payload.choice.«$lname».ownIdentities) == NULL)
|>> goto enomem;
}
}
choose {
when "$name='GroupKeys' or $name='HandshakeRequest'" {
|
|> msg->payload.choice.«$lname».partner_id =
|> OCTET_STRING_new_fromBuf(&asn_DEF_UTF8String,
|> partner->user_id, -1);
|> if (partner->user_id && !msg->payload.choice.«$lname».partner_id)
|> goto enomem;
|
|> char *devgrp = NULL;
|> status = get_device_group(session, &devgrp);
|> if (status == PEP_STATUS_OK && devgrp && devgrp[0])
|> msg->payload.choice.«$lname».group_id =
|> OCTET_STRING_new_fromBuf(&asn_DEF_UTF8String,
|> devgrp, -1);
|> free(devgrp);
|> if (devgrp && !msg->payload.choice.«$lname».partner_id)
|> goto enomem;
}
}
copy "$content";
||
||
choose {
when "count(/protocol/unencrypted/*[name()=$action/@name]) = 0"
|> bool encrypted = true;
otherwise
|> bool encrypted = false;
}
choose {
when "count(/protocol/broadcast/*[name()=$action/@name]) = 0"
|> status = unicast_msg(session, partner, state, msg, encrypted);
otherwise
|> status = multicast_self_msg(session, state, msg, encrypted);
}
||
if (status != PEP_STATUS_OK)
goto error;
`` if "$name='GroupKeys' or $name='GroupUpdate'" |> free_identity_list(kl);
free_DeviceGroup_Protocol_msg(msg);
return PEP_STATUS_OK;
enomem:
status = PEP_OUT_OF_MEMORY;
error:
free_DeviceGroup_Protocol_msg(msg);
`` if "$name='GroupKeys'" |> free_identity_list(kl);
return status;
}
||
}
function "UnCamelUp" {
param "text";
const "tokens", "str:tokenize($text, '')";
for "$tokens" {
choose {
when "contains('ABCDEFGHIJKLMNOPQRSTUVWXYZ',.)" > _«.»
otherwise value "yml:ucase(.)";
}
}
}
function "notify_action" {
param "action";
param "fsm";
param "filename", "'###'";
const "name", "substring($action/@name, 7, 255)";
const "uname" call "UnCamelUp" with "text", "$name";
function "action" {
param "content";
||
// «$action/@name»() - notify «$name» to app
//
// params:
// session (in) session handle
// state (in) state the state machine is in
// partner (in) partner to communicate with
//
// returns:
// PEP_STATUS_OK or any other value on error
PEP_STATUS «$action/@name»(
PEP_SESSION session,
«$fsm»_state state,
Identity partner,
void *extra
)
PEP_STATUS «@name»(PEP_SESSION session)
{
assert(session && state);
assert(extra == NULL);
if (!(session && state && extra == NULL))
assert(session);
if (!session)
return PEP_ILLEGAL_VALUE;
return _notifyHandshake(session, partner, SYNC_NOTIFY«$uname»);
||
copy "$content";
||
return PEP_STATUS_OK;
}
||


+ 3
- 4
sync/gen_dot.ysl2 View File

@ -4,7 +4,7 @@
include yslt.yml2
tstylesheet {
template "protocol/fsm" document "{@filename}.dot", "text"
template "protocol/fsm" document "{@name}.dot", "text"
||
digraph finite_state_machine {
rankdir=LR;
@ -21,10 +21,9 @@ tstylesheet {
template "event" {
param "state";
const "transitions", "transition|descendant::condition/transition|descendant::alternative/transition";
choose {
when "count($transitions) > 0"
apply "$transitions", 0
when "count(transition) > 0"
apply "transition|condition/transition", 0
with "state", "$state", with "event", "@name";
otherwise
if "@name != 'Init'"


+ 417
- 288
sync/gen_statemachine.ysl2 View File

@ -3,7 +3,7 @@
// generate state machine code
// Copyleft (c) 2016, p≡p foundation
// Copyleft (c) 2016, 2017, p≡p foundation
// Written by Volker Birk
@ -14,402 +14,531 @@ tstylesheet {
include ./functions.ysl2
template "/protocol" {
document "generated/Makefile.protocols", "text"
apply "fsm", 0, mode="make";
apply "fsm", 0, mode=gen;
}
template "fsm", mode=make
||
«@filename»_fsm.c: ../sync/devicegroup.fsm
\tmake -C ../«@filename»
||
template "fsm", mode=gen {
document "generated/{@filename}_fsm.h", "text" {
document "generated/{@name}_impl.h", "text" {
||
#pragma once
// This file is under GNU General Public License 3.0
// see LICENSE.txt
// state machine for «@name»
#pragma once
#include "fsm_common.h"
#include "message_api.h"
#include "../asn.1/Sync.h"
#ifdef __cplusplus
extern "C" {
#endif
// types
typedef pEp_identity * Identity;
typedef stringlist_t * Stringlist;
// event struct
// error values
typedef enum _fsm_error {
// these error values are corresponding to
// PEP_SYNC_STATEMACHINE_ERROR - value
invalid_state = -2,
invalid_event = -3,
invalid_condition = -4,
invalid_action = -5,
// out of memory condition
invalid_out_of_memory = -128
} fsm_error;
typedef struct _«@name»_event {
«@name»_PR fsm;
int event;
«@name»_t *msg;
} «@name»_event_t;
// conditions
`` for "func:distinctName(condition)" | int «@name»(PEP_SESSION session`apply "parm", 0`);
// states
typedef enum _«@name»_state {
// error values also in this namespace
«@name»_state_invalid_state = (int) invalid_state,
«@name»_state_invalid_event = (int) invalid_event,
«@name»_state_invalid_condition = (int) invalid_condition,
«@name»_state_invalid_action = (int) invalid_action,
«@name»_state_invalid_out_of_memory = (int) invalid_out_of_memory,
«@name»_state_NONE = 0,
`` for "func:distinctName(state)" |> «@name»`if "position()!=last()" > , `
} «@name»_state;
// events
typedef enum _«@name»_event {
«@name»_event_NONE = 0,
||
for "func:distinctName(state/event[not(not(/protocol/fsm/tag/@name=@name))])" {
const "name", "@name";
|> «$name» = «/protocol/fsm/tag[@name=$name]/@id»,
}
for "func:distinctName(state/event[not(/protocol/fsm/tag/@name=@name)])"
|> «@name»`if "position()!=last()" > , `
for "func:distinctName(*//condition)"
| PEP_STATUS «@name»(PEP_SESSION session, bool *result);
||
} «@name»_event;
// actions
`` const "name", "@name"
`` for "func:distinctName(//action)" | PEP_STATUS «@name»(PEP_SESSION session, «$name»_state state, Identity partner, void *extra);
// event injector
||
const "name", "@name";
for "func:distinctName(*//action[not(starts-with(@name, 'send'))])"
| PEP_STATUS «@name»(PEP_SESSION session);
||
PEP_STATUS inject_DeviceState_event(
PEP_SESSION session,
DeviceState_event event,
Identity partner,
void *extra);
// send event to own state machine, use state to generate
// «@name» message if necessary
// message receiver
PEP_STATUS receive_DeviceState_msg(
PEP_STATUS «@name»_send(
PEP_SESSION session,
message *src,
PEP_rating rating,
stringlist_t *keylist
«@name»_PR fsm,
int message_type
);
// state machine
// send message to partners
«@name»_state fsm_«@name»(
PEP_SESSION session,
«@name»_state state,
«@name»_event event,
Identity partner,
void *extra,
time_t *timeout
PEP_STATUS send_«@name»_message(