// This file is under GNU General Public License 3.0
|
|
// see LICENSE.txt
|
|
|
|
// DeviceGroup protocol for p≡p
|
|
|
|
// Copyleft (c) 2016, p≡p foundation
|
|
|
|
// Written by Volker Birk
|
|
|
|
include ./fsm.yml2
|
|
|
|
protocol DeviceGroup {
|
|
// all messages have a timestamp, time out and are removed after timeout
|
|
|
|
broadcast sendBeacon;
|
|
broadcast sendGroupUpdate;
|
|
broadcast sendUpdateRequest;
|
|
unencrypted sendBeacon;
|
|
|
|
fsm DeviceState filename=sync {
|
|
condition deviceGrouped();
|
|
condition keyElectionWon(Identity partner);
|
|
condition sameIdentities(Identity a, Identity b);
|
|
condition sameKeyAndAddress(Identity a, Identity b);
|
|
|
|
state InitState {
|
|
on Init {
|
|
if deviceGrouped()
|
|
go Grouped;
|
|
go Sole;
|
|
}
|
|
}
|
|
|
|
state Sole end=1 {
|
|
on KeyGen {
|
|
do sendBeacon;
|
|
go SoleWaiting;
|
|
}
|
|
on CannotDecrypt {
|
|
do sendBeacon;
|
|
go SoleWaiting;
|
|
}
|
|
on Beacon(Identity partner){
|
|
do sendHandshakeRequest(partner);
|
|
go SoleBeaconed(partner);
|
|
}
|
|
on HandshakeRequest(Identity partner) {
|
|
do sendHandshakeRequest(partner);
|
|
go HandshakingSole(partner);
|
|
}
|
|
}
|
|
|
|
// copy of sole state with a timeout to enable fast polling for a second
|
|
// TODO use more YSLT power here (substates ?)
|
|
state SoleWaiting timeout=60 {
|
|
on KeyGen {
|
|
do sendBeacon;
|
|
}
|
|
on CannotDecrypt {
|
|
do sendBeacon;
|
|
}
|
|
on Beacon(Identity partner){
|
|
do sendHandshakeRequest(partner);
|
|
go SoleBeaconed(partner);
|
|
}
|
|
on HandshakeRequest(Identity partner) {
|
|
do sendHandshakeRequest(partner);
|
|
go HandshakingSole(partner);
|
|
}
|
|
on Timeout go Sole;
|
|
}
|
|
|
|
state SoleBeaconed timeout=600 (Identity expected) {
|
|
on KeyGen{
|
|
do sendBeacon;
|
|
go Sole;
|
|
}
|
|
on CannotDecrypt{
|
|
do sendBeacon;
|
|
go Sole;
|
|
}
|
|
on Beacon(Identity partner) {
|
|
do sendHandshakeRequest(partner);
|
|
go SoleBeaconed(partner);
|
|
}
|
|
on HandshakeRequest(Identity partner) {
|
|
if sameIdentities(partner, expected) {
|
|
// do nothing, to avoid sending handshake request twice
|
|
} else {
|
|
do sendHandshakeRequest(partner);
|
|
}
|
|
go HandshakingSole(partner);
|
|
}
|
|
on Timeout go Sole;
|
|
}
|
|
|
|
state HandshakingSole timeout=600 (Identity expected) {
|
|
on Init{
|
|
if keyElectionWon(expected) {
|
|
do notifyInitFormGroup(expected);
|
|
} else {
|
|
do notifyInitAddOurDevice(expected);
|
|
}
|
|
}
|
|
on HandshakeRejected(Identity partner) {
|
|
do rejectHandshake(partner);
|
|
go Sole;
|
|
}
|
|
on HandshakeAccepted(Identity partner) {
|
|
if sameIdentities(partner, expected) {
|
|
do acceptHandshake(partner);
|
|
if keyElectionWon(partner) {
|
|
do makeGroup;
|
|
do sendGroupKeys(partner);
|
|
do renewUUID;
|
|
do notifyAcceptedGroupCreated(partner);
|
|
go Grouped;
|
|
}
|
|
go WaitForGroupKeysSole(partner);
|
|
}
|
|
go Sole;
|
|
}
|
|
on Cancel go Sole;
|
|
on GroupKeys(Identity partner, GroupKeys groupkeys) {
|
|
if keyElectionWon(expected) {
|
|
// not supposed to receive groupkeys - ignore
|
|
} else {
|
|
// UUID changes in between, so we can only check for same address and fpr
|
|
if sameKeyAndAddress(partner, expected) {
|
|
go WaitForAcceptSole(partner, groupkeys);
|
|
}
|
|
}
|
|
}
|
|
on Timeout {
|
|
do notifyTimeout(expected);
|
|
do sendBeacon;
|
|
go Sole;
|
|
}
|
|
}
|
|
|
|
state WaitForGroupKeysSole timeout=600 (Identity expected) {
|
|
on GroupKeys(Identity partner, GroupKeys groupkeys) {
|
|
// UUID changes in between, so we can only check for same address and fpr
|
|
if sameKeyAndAddress(partner, expected) {
|
|
do storeGroupKeys(partner, groupkeys);
|
|
do sendGroupUpdate;
|
|
do renewUUID;
|
|
do notifyAcceptedDeviceAdded(partner);
|
|
go Grouped;
|
|
}
|
|
}
|
|
on Timeout {
|
|
do notifyTimeout(expected);
|
|
go Sole;
|
|
}
|
|
}
|
|
|
|
state WaitForAcceptSole timeout=600 (Identity expected, GroupKeys groupkeys) {
|
|
on HandshakeRejected(Identity partner) {
|
|
do rejectHandshake(partner);
|
|
go Sole;
|
|
}
|
|
on HandshakeAccepted(Identity partner) {
|
|
// UUID changes in between, so we can only check for same address and fpr
|
|
if sameKeyAndAddress(partner, expected) {
|
|
do acceptHandshake(partner);
|
|
do storeGroupKeys(partner, groupkeys);
|
|
do sendGroupUpdate;
|
|
do renewUUID;
|
|
do notifyAcceptedDeviceAdded(partner);
|
|
go Grouped;
|
|
}
|
|
go Sole;
|
|
}
|
|
on Cancel go Sole;
|
|
on Timeout {
|
|
do notifyTimeout(expected);
|
|
go Sole;
|
|
}
|
|
}
|
|
|
|
state Grouped end=1 {
|
|
on KeyGen
|
|
do sendGroupUpdate;
|
|
on CannotDecrypt {
|
|
do sendUpdateRequest;
|
|
do sendBeacon;
|
|
go GroupWaiting;
|
|
}
|
|
on UpdateRequest
|
|
do sendGroupUpdate;
|
|
on Beacon(Identity partner){
|
|
do sendHandshakeRequest(partner);
|
|
go GroupedBeaconed(partner);
|
|
}
|
|
on HandshakeRequest(Identity partner) {
|
|
do sendHandshakeRequest(partner);
|
|
go HandshakingGrouped(partner);
|
|
}
|
|
on GroupUpdate(Identity partner, IdentityList keys)
|
|
do storeGroupUpdate(partner, keys);
|
|
}
|
|
|
|
// copy of grouped state, with a timeout to enable fast poling for a minut
|
|
state GroupWaiting timeout=60 {
|
|
on KeyGen
|
|
do sendGroupUpdate;
|
|
on CannotDecrypt {
|
|
do sendUpdateRequest;
|
|
do sendBeacon;
|
|
}
|
|
on UpdateRequest
|
|
do sendGroupUpdate;
|
|
on Beacon(Identity partner){
|
|
do sendHandshakeRequest(partner);
|
|
go GroupedBeaconed(partner);
|
|
}
|
|
on HandshakeRequest(Identity partner) {
|
|
do sendHandshakeRequest(partner);
|
|
go HandshakingGrouped(partner);
|
|
}
|
|
on GroupUpdate(Identity partner, IdentityList keys)
|
|
do storeGroupUpdate(partner, keys);
|
|
on Timeout go Grouped;
|
|
}
|
|
|
|
state GroupedBeaconed timeout=600 (Identity expected){
|
|
on KeyGen
|
|
do sendGroupUpdate;
|
|
on CannotDecrypt {
|
|
do sendUpdateRequest;
|
|
do sendBeacon;
|
|
}
|
|
on UpdateRequest
|
|
do sendGroupUpdate;
|
|
on Beacon(Identity partner){
|
|
do sendHandshakeRequest(partner);
|
|
go GroupedBeaconed(partner);
|
|
}
|
|
on HandshakeRequest(Identity partner) {
|
|
if sameIdentities(partner, expected) {
|
|
// do nothing, to avoid sending handshake request twice
|
|
} else {
|
|
do sendHandshakeRequest(partner);
|
|
}
|
|
go HandshakingGrouped(partner);
|
|
}
|
|
on GroupUpdate(Identity partner, IdentityList keys)
|
|
do storeGroupUpdate(partner, keys);
|
|
on Timeout go Grouped;
|
|
}
|
|
|
|
state HandshakingGrouped timeout=600 (Identity expected) {
|
|
// HandshakeRequest from same group are filtered in receive_sync_msg
|
|
on Init{
|
|
if keyElectionWon(expected) {
|
|
do notifyInitAddOtherDevice(partner);
|
|
} else {
|
|
do notifyInitMoveOurDevice(partner);
|
|
}
|
|
}
|
|
on HandshakeRejected(Identity partner) {
|
|
do rejectHandshake(partner); // stores rejection of partner
|
|
do sendGroupUpdate;
|
|
go Grouped;
|
|
}
|
|
on HandshakeAccepted(Identity partner) {
|
|
do acceptHandshake(partner);
|
|
do sendGroupUpdate;
|
|
if keyElectionWon(partner) {
|
|
do sendGroupKeys(partner);
|
|
do notifyAcceptedDeviceAdded(partner);
|
|
go Grouped;
|
|
}
|
|
go WaitForGroupKeysGrouped(partner);
|
|
}
|
|
on Cancel go Grouped;
|
|
on GroupKeys(Identity partner, GroupKeys groupkeys) {
|
|
if keyElectionWon(expected) {
|
|
// not supposed to receive groupkeys - ignore
|
|
} else {
|
|
// UUID changes in between, so we can only check for same address and fpr
|
|
if sameKeyAndAddress(partner, expected) {
|
|
go WaitForAcceptGrouped(partner, groupkeys);
|
|
}
|
|
}
|
|
}
|
|
on GroupUpdate(Identity partner, IdentityList keys) {
|
|
do notifyOvertaken(partner);
|
|
do storeGroupUpdate(partner, keys);
|
|
go Grouped;
|
|
}
|
|
on Timeout {
|
|
do notifyTimeout(expected);
|
|
go Grouped;
|
|
}
|
|
}
|
|
|
|
state WaitForGroupKeysGrouped timeout=600 (Identity expected) {
|
|
on GroupKeys(Identity partner, GroupKeys groupkeys) {
|
|
if sameIdentities(partner, expected) {
|
|
do storeGroupKeys(partner, groupkeys);
|
|
do sendGroupUpdate;
|
|
do renewUUID;
|
|
do notifyAcceptedDeviceMoved(partner);
|
|
go Grouped;
|
|
}
|
|
}
|
|
on GroupUpdate(Identity partner, IdentityList keys) {
|
|
do notifyOvertaken(partner);
|
|
do storeGroupUpdate(partner, keys);
|
|
go Grouped;
|
|
}
|
|
on Timeout {
|
|
do notifyTimeout(expected);
|
|
go Grouped;
|
|
}
|
|
}
|
|
|
|
state WaitForAcceptGrouped timeout=600 (Identity expected, GroupKeys groupkeys) {
|
|
on HandshakeRejected(Identity partner) {
|
|
do rejectHandshake(partner);
|
|
do sendGroupUpdate;
|
|
go Grouped;
|
|
}
|
|
on HandshakeAccepted(Identity partner) {
|
|
if sameIdentities(partner, expected) {
|
|
do acceptHandshake(partner);
|
|
do storeGroupKeys(partner, groupkeys);
|
|
do sendGroupUpdate;
|
|
do renewUUID;
|
|
do notifyAcceptedDeviceMoved(partner);
|
|
}
|
|
go Grouped;
|
|
}
|
|
on Cancel go Grouped;
|
|
on GroupUpdate(Identity partner, IdentityList keys) {
|
|
do notifyOvertaken(partner);
|
|
do storeGroupUpdate(partner, keys);
|
|
go Grouped;
|
|
}
|
|
on Timeout {
|
|
do notifyTimeout(expected);
|
|
go Grouped;
|
|
}
|
|
}
|
|
|
|
tag Init 1;
|
|
tag Beacon 2;
|
|
tag HandshakeRequest 3;
|
|
tag GroupKeys 4;
|
|
}
|
|
}
|
|
|