You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

692 lines
18 KiB

// This file is under BSD License 2.0
// Sync protocol for p≡p
// Copyright (c) 2016-2020, p≡p foundation
// Written by Volker Birk
include ./fsm.yml2
protocol Sync 1 {
// all messages have a timestamp, time out and are removed after timeout
fsm KeySync 1, threshold=300 {
version 1, 2;
state InitState {
on Init {
if deviceGrouped {
send SynchronizeGroupKeys;
go Grouped;
}
do newChallengeAndNegotiationBase;
debug > initial Beacon
send Beacon;
go Sole;
}
}
state Sole timeout=off {
on Init {
do showBeingSole;
}
on KeyGen {
debug > key generated
send Beacon;
}
on CannotDecrypt {
debug > cry, baby
send Beacon;
}
on Beacon {
if sameChallenge {
debug > this is our own Beacon; ignore
}
else {
if weAreOfferer {
do useOwnChallenge;
debug > we are Offerer
send Beacon;
}
else /* we are requester */ {
do openNegotiation;
do tellWeAreNotGrouped;
// requester is sending NegotiationRequest
do useOwnResponse;
send NegotiationRequest;
do useOwnChallenge;
}
}
}
// we get this from another sole device
on NegotiationRequest {
if sameChallenge { // challenge accepted
do storeNegotiation;
// offerer is accepting by confirming NegotiationOpen
// repeating response is implicit
send NegotiationOpen;
go HandshakingOfferer;
}
}
// we get this from an existing device group
on NegotiationRequestGrouped {
if sameChallenge { // challenge accepted
do storeNegotiation;
// offerer is accepting by confirming NegotiationOpen
// repeating response is implicit
send NegotiationOpen;
go HandshakingToJoin;
}
}
on NegotiationOpen {
if sameResponse {
debug > Requester is receiving NegotiationOpen
do storeNegotiation;
go HandshakingRequester;
}
else {
debug > cannot approve NegotiationOpen
}
}
}
// handshaking without existing Device group
state HandshakingOfferer timeout=600 {
on Init
do showSoleHandshake;
// Cancel is Rollback
on Cancel {
send Rollback;
go Sole;
}
on Rollback {
if sameNegotiation
go Sole;
}
// Reject is CommitReject
on Reject {
send CommitReject;
do disable;
go End;
}
on CommitReject {
if sameNegotiation {
do disable;
go End;
}
}
// Accept means init Phase1Commit
on Accept {
do trustThisKey;
go HandshakingPhase1Offerer;
}
// got a CommitAccept from requester
on CommitAcceptRequester {
if sameNegotiation
go HandshakingPhase2Offerer;
}
}
// handshaking without existing Device group
state HandshakingRequester timeout=600 {
on Init
do showSoleHandshake;
// Cancel is Rollback
on Cancel {
send Rollback;
go Sole;
}
on Rollback {
if sameNegotiation
go Sole;
}
// Reject is CommitReject
on Reject {
send CommitReject;
do disable;
go End;
}
on CommitReject {
if sameNegotiation {
do disable;
go End;
}
}
// Accept means init Phase1Commit
on Accept {
do trustThisKey;
send CommitAcceptRequester;
go HandshakingPhase1Requester;
}
}
state HandshakingPhase1Offerer {
on Rollback {
if sameNegotiation {
do untrustThisKey;
go Sole;
}
}
on CommitReject {
if sameNegotiation {
do untrustThisKey;
do disable;
go End;
}
}
on CommitAcceptRequester {
if sameNegotiation {
send CommitAcceptOfferer;
go FormingGroupOfferer;
}
}
}
state HandshakingPhase1Requester {
on Rollback {
if sameNegotiation {
do untrustThisKey;
go Sole;
}
}
on CommitReject {
if sameNegotiation {
do untrustThisKey;
do disable;
go End;
}
}
on CommitAcceptOfferer {
if sameNegotiation {
do prepareOwnKeys;
send OwnKeysRequester;
go FormingGroupRequester;
}
}
}
state HandshakingPhase2Offerer {
on Cancel {
send Rollback;
go Sole;
}
on Reject {
send CommitReject;
do disable;
go End;
}
on Accept {
do trustThisKey;
send CommitAcceptOfferer;
go FormingGroupOfferer;
}
}
state FormingGroupOfferer {
on Init {
// we need to keep in memory which keys we have before forming
// a new group
do prepareOwnKeys;
do backupOwnKeys;
}
on Cancel {
send Rollback;
go Sole;
}
on Rollback
go Sole;
on OwnKeysRequester {
if sameNegotiationAndPartner {
do saveGroupKeys;
do receivedKeysAreDefaultKeys;
// send the keys we had before forming a new group
do prepareOwnKeysFromBackup;
send OwnKeysOfferer;
do showGroupCreated;
go Grouped;
}
}
}
state FormingGroupRequester {
on Cancel {
send Rollback;
go Sole;
}
on Rollback
go Sole;
on OwnKeysOfferer {
if sameNegotiation {
do saveGroupKeys;
do prepareOwnKeys;
do ownKeysAreDefaultKeys;
do showGroupCreated;
go Grouped;
}
}
}
state Grouped timeout=off {
on Init {
do newChallengeAndNegotiationBase;
do showBeingInGroup;
}
on CannotDecrypt {
debug > cry, baby
send SynchronizeGroupKeys;
}
on SynchronizeGroupKeys {
do prepareOwnKeys;
send GroupKeysUpdate;
}
on GroupKeysUpdate {
if fromGroupMember // double check
do saveGroupKeys;
}
on KeyGen {
do prepareOwnKeys;
send GroupKeysUpdate;
}
on Beacon {
do openNegotiation;
do tellWeAreGrouped;
do useOwnResponse;
send NegotiationRequestGrouped;
do useOwnChallenge;
}
on NegotiationOpen {
if sameResponse {
do storeNegotiation;
do useThisKey;
send GroupHandshake;
go HandshakingGrouped;
}
else {
debug > cannot approve NegotiationOpen
}
}
on GroupHandshake {
do storeNegotiation;
do storeThisKey;
go HandshakingGrouped;
}
on GroupTrustThisKey {
if fromGroupMember // double check
do trustThisKey;
}
on LeaveDeviceGroup {
send InitUnledGroupKeyReset;
do disable;
do resetOwnKeysUngrouped;
}
on InitUnledGroupKeyReset {
debug > unled group key reset; new group keys will be elected
do useOwnResponse;
send ElectGroupKeyResetLeader;
go GroupKeyResetElection;
}
}
state GroupKeyResetElection {
on ElectGroupKeyResetLeader {
if sameResponse {
// the first one is from us, we're leading this
do resetOwnGroupedKeys;
go Grouped;
}
else {
// the first one is not from us
go Grouped;
}
}
}
// sole device handshaking with group
state HandshakingToJoin {
on Init
do showJoinGroupHandshake;
// Cancel is Rollback
on Cancel {
send Rollback;
go Sole;
}
on Rollback {
if sameNegotiation
go Sole;
}
// Reject is CommitReject
on Reject {
send CommitReject;
do disable;
go End;
}
on CommitAcceptForGroup {
if sameNegotiation
go HandshakingToJoinPhase2;
}
on CommitReject {
if sameNegotiation {
do disable;
go End;
}
}
// Accept is Phase1Commit
on Accept {
do trustThisKey;
go HandshakingToJoinPhase1;
}
}
state HandshakingToJoinPhase1 {
on Rollback {
if sameNegotiation {
do untrustThisKey;
go Sole;
}
}
on CommitReject {
if sameNegotiation {
do untrustThisKey;
do disable;
go End;
}
}
on CommitAcceptForGroup {
if sameNegotiation {
send CommitAccept;
go JoiningGroup;
}
}
}
state HandshakingToJoinPhase2 {
on Cancel {
send Rollback;
go Sole;
}
on Reject {
send CommitReject;
do disable;
go End;
}
on Accept {
do trustThisKey;
send CommitAccept;
go JoiningGroup;
}
}
state JoiningGroup {
on Init {
// we need to keep in memory which keys we have before joining
do prepareOwnKeys;
do backupOwnKeys;
}
on GroupKeysForNewMember {
if sameNegotiationAndPartner {
do saveGroupKeys;
do receivedKeysAreDefaultKeys;
// send the keys we had before joining
do prepareOwnKeysFromBackup;
send GroupKeysAndClose;
do showDeviceAdded;
go Grouped;
}
}
}
state HandshakingGrouped {
on Init
do showGroupedHandshake;
// Cancel is Rollback
on Cancel {
send Rollback;
go Grouped;
}
on Rollback {
if sameNegotiation
go Grouped;
}
// Reject is CommitReject
on Reject {
send CommitReject;
go Grouped;
}
on CommitReject {
if sameNegotiation
go Grouped;
}
// Accept is Phase1Commit
on Accept {
do trustThisKey;
go HandshakingGroupedPhase1;
}
on GroupTrustThisKey {
if fromGroupMember { // double check
do trustThisKey;
if sameNegotiation
go Grouped;
}
}
on GroupKeysUpdate {
if fromGroupMember // double check
do saveGroupKeys;
}
}
state HandshakingGroupedPhase1 {
on Init {
send GroupTrustThisKey;
send CommitAcceptForGroup;
}
on Rollback {
if sameNegotiation {
do untrustThisKey;
go Grouped;
}
}
on CommitReject {
if sameNegotiation {
do untrustThisKey;
go Grouped;
}
}
on CommitAccept {
if sameNegotiation {
do prepareOwnKeys;
send GroupKeysForNewMember;
do showDeviceAccepted;
go Grouped;
}
}
on GroupTrustThisKey {
if fromGroupMember // double check
do trustThisKey;
}
on GroupKeysUpdate {
if fromGroupMember // double check
do saveGroupKeys;
}
on GroupKeysAndClose {
if fromGroupMember { // double check
// do not save GroupKeys as default keys; key data is
// already imported
go Grouped;
}
}
}
external Accept 129;
external Reject 130;
external Cancel 131;
// beacons are always broadcasted
message Beacon 2, type=broadcast, ratelimit=10, security=unencrypted {
field TID challenge;
auto Version version;
}
message NegotiationRequest 3, security=untrusted {
field TID challenge;
field TID response;
auto Version version;
field TID negotiation;
field bool is_group;
}
message NegotiationOpen 4, security=untrusted {
field TID response;
auto Version version;
field TID negotiation;
}
message Rollback 5, security=untrusted {
field TID negotiation;
}
message CommitReject 6, security=untrusted {
field TID negotiation;
}
message CommitAcceptOfferer 7, security=untrusted {
field TID negotiation;
}
message CommitAcceptRequester 8, security=untrusted {
field TID negotiation;
}
message CommitAccept 9, security=untrusted {
field TID negotiation;
}
message CommitAcceptForGroup 10, security=untrusted {
field TID negotiation;
}
// default: security=trusted
// messages are only accepted when coming from the device group
message GroupTrustThisKey 11 {
field Hash key;
field TID negotiation;
}
// trust in future
message GroupKeysForNewMember 12, security=attach_own_keys_for_new_member {
field IdentityList ownIdentities;
}
message GroupKeysAndClose 13, security=attach_own_keys_for_group {
field IdentityList ownIdentities;
}
message OwnKeysOfferer 14, security=attach_own_keys_for_group {
field IdentityList ownIdentities;
}
message OwnKeysRequester 15, security=attach_own_keys_for_new_member {
field IdentityList ownIdentities;
}
// grouped handshake
message NegotiationRequestGrouped 16, security=untrusted {
field TID challenge;
field TID response;
auto Version version;
field TID negotiation;
field bool is_group;
}
message GroupHandshake 17 {
field TID negotiation;
field Hash key;
}
// update group
message GroupKeysUpdate 18, security=attach_own_keys_for_group {
field IdentityList ownIdentities;
}
// initiate unled group key reset
message InitUnledGroupKeyReset 19 {
}
message ElectGroupKeyResetLeader 20 {
field TID response;
}
message SynchronizeGroupKeys 21, ratelimit=60 {
}
// This could be part of TrustSync, but actually it does not matter.
// This message will not be sent by the Sync thread. It is used by
// decrypt_message() to mark a previously computed rating. It is only
// valid when signed with an own key.
message ReceiverRating 22, security=ignore {
field Rating rating;
}
}
}