In Cryptyc, you define your protocol, and specify certain "effects" called "correspondence assertions" (for instance, "Alice received message"). Cryptyc verifies that the effects are well-formed (more on that later).
This tutorial will describe the process of type-checking a protocol, building up a simple message-passing scheme using a common secret key.
The following is a simple protocol for Alice passing Bob a message using the shared secret key SKey.
We want a property that anytime Bob receives this message, Alice must have sent it. In Cryptyc-syntax Spi-calculus, the protocol would be represented as:
Message 1 Alice --> Bob: {msg}SKey
So now, we're ready to use Cryptyc to verify this protocol.client Sender at Alice is { establish Receiver at Bob is (socket : Socket); new (msg : Payload); output socket is ({msg}SKey); } server Receiver at Bob is (socket : Socket) { input socket is ({msg: Payload}SKey); }
Generic type definitions:You need to declare the types of all messages and keys.type Payload = Private; type Socket = Public; type Host = Public; type Server = Public; type Client = Public;Generic public variables:public Sender: Client; public Receiver: Server; public Alice : Host; public Bob : Host;Some protocol-specific types and variablestype MyKey = Key(Payload); private SKey: MyKey;And the rest of the protocol, from above:
Declares a the type MyKey of keys capable of decrypting messages of type Payloadtype MyKey = Key(Payload);
Declares that SKey is a shared key of type MyKey.private SKey: MyKey;
java -jar /home/comp527/cryptyc/cryptyc.jar <filename>The above example is in /home/comp527/cryptyc/example1.cry. If you run cryptyc on it, it should say the protocol is "OK".
$ java -jar /home/comp527/cryptyc/cryptyc.jar /home/comp527/cryptyc/example1.cry/home/comp527/cryptyc/example1.cry
Type checked OK!
In this protocol, our security assertion is that Bob's completion of this protocol corresponds to Alice's iniating of this protocol. So Bob's assertion at the end of its protocol run of "Sender sent msg" should correspond to a statement at the start of Alice's protocol run of "Sender sent msg".
These assertions are expressed through begin statements and end assertions. begin and end include some string indicating what is being begun or ended. end assertions are inserted at the point where the property should always be true. begin statements are inserted when you are about to begin the process of achieving some property. When an end assertion is declared, it creates an effect, an obligation that must be satisfied by a corresponding begin.
To verify the correctness of your protocol, Cryptyc checks that during any execution of the protocol, each end assertion's execution must occur after the execution of at least one corresponding one begin statement with the same description tag. Cryptyc runs this analysis assuming an intruder is on the network and could be running any possible Cryptyc program that does not contain any correspondence assertions (i.e. begin and end statements).
Note that it doesn't matter if there are too many begin tags. begin statements don't actually assert anything. They just indicate you would be interested in asserting something later. However, an end statement without a corresponding begin means that a legitimate participant has asserted a property, and no other legitimate participant has attempted to stated that property.
Here is our protocol, with a correspondence assertion added. We want to verify that a sender sent a message.
And the same protocol as a Cryptyc program.
Event 1 Alice begins msg sent Message 1 Alice --> Bob: {msg}SKey Event 2 Bob ends msg sent
This file is located at /home/comp527/cryptyc/example2.cry. Try running Cryptyc to check the file.type Payload = Private; type Socket = Public; type Host = Public; type Server = Public; type Client = Public; type Word = Public; public Sender: Client; public Receiver: Server; public Alice : Host; public Bob : Host;Note: any words used in begin and end statement descriptions that are not semantically relevant (such as sent) must still be declared, in this case as type Word (which is Public).public sent: Word; type MyKey = Key(Payload); private SKey: MyKey; client Sender at Alice is { establish Receiver at Bob is (socket : Socket); new (msg : Payload); begin (Sender sent msg); output socket is ({msg}SKey); } server Receiver at Bob is (socket : Socket) { input socket is ({msg: Payload}SKey); end (Sender sent msg); }
$ java -jar /home/comp527/cryptyc/cryptyc.jar /home/comp527/cryptyc/example2.cry /home/comp527/cryptyc/example2.cry Type error! At line 22: In server Receiver at Bob (socket : Socket ()): In end (Sender sent msg); the effect (end (Sender sent msg)) is unjustified. Effects can be justified either with a matching begin, or with appropriate nonce checks.
A nonce is an arbitrary one-time-use-only random bit-string. They are used to establish the uniqueness and timeliness of communications. That is, if Alice sends Bob a nonce, and Bob replies with the nonce encrypted with a shared secret key, Alice knows:
We try to fix this protocol by adding nonces.
Here is a Cryptyc program encoding the same protocol.
Event 1 Alice begins msg sent Message 1 Bob --> Alice N Message 2 Alice --> Bob: {msg,N}SKey Event 2 Bob ends msg sent
This file is located at /home/comp527/cryptyc/example3.cry.type Payload = Private; type Socket = Public; type Host = Public; type Server = Public; type Client = Public; type Word = Public; type Challenge = Public; public Sender: Client; public Receiver: Server; public sent: Word;Note: Struct types are used to encode structures in Cryptyc.type MyKey = Key (Struct(msg: Payload, nonce: Challenge)); public Alice : Host; public Bob : Host; private SKey: MyKey; client Sender at Alice is { new (msg : Payload); establish Receiver at Bob is (socket : Socket); input socket is (nonce : Challenge); begin (Sender sent msg); output socket is ({ msg, nonce }SKey); } server Receiver at Bob is (socket : Socket) { new (nonce : Challenge); output socket is (nonce); input socket is ({ msg : Payload, nonce' : Challenge }SKey);The check statement is used to check that nonces matchcheck nonce is nonce'; end (Sender sent msg); }
$ java -jar /home/comp527/cryptyc/cryptyc.jar /home/comp527/cryptyc/example3.cry /home/comp527/cryptyc/example3.cry Type error! At line 29: In check nonce is nonce': variable nonce' : Challenge () is not of nonce type. Challenge () = Public is not a nonce type.
To address this, Cryptyc has a special Nonce type which is used to encode the assertion an effect is trying to encode. The Nonce is a parametric type; there is a single parameter, the end assertion being transferred by the nonce. In the above case, the type would be
indicating that we are using this nonce to transfer the effect Sender send msg.Nonce (end (Sender sent msg));
We rewrite our protocol using this nonce type:
This file is located at /home/comp527/cryptyc/example4.cry.type Payload = Private; type Socket = Public; type Host = Public; type Server = Public; type Client = Public; type Word = Public; type Challenge = Public; public Sender: Client; public Receiver: Server; public sent: Word;The statement defining the MyNonce type as a Nonce.type MyNonce (msg : Payload) = Nonce (end (Sender sent msg));We also choose to define the message we sent as a type:type MyMsg = Struct (msg : Payload, nonce : MyNonce(msg)); type MyKey = Key (MyMsg); public Alice : Host; public Bob : Host; private SKey: MyKey; client Sender at Alice is { establish Receiver at Bob is (socket : Socket); input socket is (nonce : Challenge); new (msg : Payload); begin (Sender sent msg);The cast statement is used extract the unsatisfied effect from the Nonce so that the sender can be matched with the begin. It converts from Untyped to MyNonce(msg)cast nonce is (nonce' : MyNonce (msg)); output socket is ({ msg, nonce' }SKey); } server Receiver at Bob is (socket : Socket) { new (nonce : Challenge); output socket is (nonce); input socket is ({ msg : Payload, nonce' : MyNonce(msg) }SKey);The check statement is used to check that nonces match, enabling an end effect to be used. The syntax is:check <challenge> is <response>Where <challenge> is type Challenge (which is Public), and <response> is a nonce-type. Cryptyc won't let you check a nonce more than once.
check nonce is nonce'; end (Sender sent msg); }
Woo-hoo!$ java -jar /home/comp527/cryptyc/cryptyc.jar /home/comp527/cryptyc/example4.cry /home/comp527/cryptyc/example4.cry Type checked OK!
Note that, to achieve this, we also need to pass the sender and recipient in the message. This is what our protocol now looks like.
And here is the Cryptyc version.
Event 1 Alice begins Alice sent msg to Bob Message 1 Bob --> Alice N Message 2 Alice --> Bob: { Alice,Bob,msg,N}SKey Event 2 Bob ends a sent msg to b
This file is located at /home/comp527/cryptyc/example5.cry.type Payload = Private; type Socket = Public; type Host = Public; type Server = Public; type Client = Public; type Word = Public; type Challenge = Public; public Sender: Client; public Receiver: Server; public sent: Word; public to: Word;We've added parameters (Hosts a and b) to the nonce, to generate the parametric effect "a sent msg to b" for hosts a and b.type MyNonce (a: Host, b: Host, msg : Payload) = Nonce (end (a sent msg to b));Since we need to know who sent the message and who the intended recipient is, we add these parameters to the message Alice sendstype MyMsg = Struct (a: Host, b: Host, msg : Payload, nonce : MyNonce(a, b, msg)); type MyKey = Key (MyMsg); public Alice : Host; public Bob : Host; private SKey: MyKey; client Sender at Alice is { establish Receiver at Bob is (socket : Socket); input socket is (nonce : Challenge); new (msg : Payload); begin (Alice sent msg to Bob); cast nonce is (nonce' : MyNonce (Alice, Bob, msg)); output socket is ({ Alice, Bob, msg, nonce' }SKey); } server Receiver at Bob is (socket : Socket) { new (nonce : Challenge); output socket is (nonce); input socket is ({ a: Host, b: Host, msg : Payload, nonce' : MyNonce(a, b, msg) }SKey); check nonce is nonce'; end (Alice sent msg to Bob); }
Cryptyc is identifying a flaw in this protocol. What Bob doesn't know that a in the message is really Alice. It does know that whomever did send the message would have but their name in it, but it doesn't necessarily have to be Alice. The flaw would be easier to see if we assumed we had an Alice1 and Alice2. Bob would know it was one of them, but not which. Alice begins "Alice sent msg to Bob", but only passes to Bob the effect "a sent msg to b". As a result, Bob must end "a sent msg to b"$ java -jar /home/comp527/cryptyc/cryptyc.jar /home/comp527/cryptyc/example5.cry /home/comp527/cryptyc/example5.cry Type error! At line 32: In server Receiver at Bob (socket : Socket ()): In end (Alice sent msg to Bob); the effect (end (Alice sent msg to Bob)) is unjustified. Effects can be justified either with a matching begin, or with appropriate nonce checks.
The file /home/comp527/cryptyc/example6.cry ends Bob with
end (a sent msg to b);
$ java -jar /home/comp527/cryptyc/cryptyc.jar /home/comp527/cryptyc/example6.cry /home/comp527/cryptyc/example6.cry Type checked OK!
Event 1 Alice begins Alice sent msg to Bob Message 1 Bob --> Alice N Message 2 Alice --> Bob: {Alice,Bob,msg,N}SKey Event 2 Bob ends a sent msg to b Message 3 Alice --> Bob: {Alice,Bob,msg}SKey
This file is located at /home/comp527/cryptyc/example7.cry.type Payload = Private; type Socket = Public; type Host = Public; type Server = Public; type Client = Public; type Word = Public; type Challenge = Public; public Sender: Client; public Receiver: Server; public sent: Word; public to: Word; type MyNonce (a: Host, b: Host, msg : Payload) = Nonce (end (a sent msg to b));These are our two message typestype MyMsg = Struct (a: Host, b: Host, msg : Payload, nonce : MyNonce(a, b, msg)); type MyMsg2 = Struct (a: Host, b: Host, msg: Payload);This is the union type, taking both messages. The secret key encrypts this union type. Note that each type in the union type has a label (msg and msg2).type UseMsg = Union (msg: MyMsg, msg2: MyMsg2); type MyKey = Key (UseMsg); public Alice : Host; public Bob : Host; private SKey: MyKey; client Sender at Alice is { establish Receiver at Bob is (socket : Socket); input socket is (nonce : Challenge); new (msg : Payload); begin (Alice sent msg to Bob); cast nonce is (nonce' : MyNonce (Alice, Bob, msg));We now need to tell Cryptyc what type is being encrypted. The syntax for encrypting a union type is {label(fields,...)}Key.In this case, we are encrypting something of type MyMsg.
output socket is ({msg( Alice, Bob, msg, nonce') }SKey); new (msg: Payload);In this case, we are encrypting something of type MyMsg2.output socket is ({msg2(Alice, Bob, msg)}SKey); } server Receiver at Bob is (socket : Socket) { new (nonce : Challenge); output socket is (nonce); input socket is ({ msg(a: Host, b: Host, msg : Payload, nonce' : MyNonce(a, b, msg)) }SKey); check nonce is nonce'; end (a sent msg to b); input socket is ({msg2(a : Host, b: Host, msg: Payload)}SKey); }
This is the Cryptyc form of the same protocol:
Event 1 Alice begins Alice sent msg to Bob Message 1 Bob --> Alice N Message 2 Alice --> Bob: {Alice,Bob,msg,N,N2}SKey Event 2 Bob ends a sent msg to b Event 3 Bob begins Bob got msg from Alice Message 3 Bob --> Alice: {Alice,Bob,msg,N2}SKey Event 4 Alice ends b got msg from a
This file is located at /home/comp527/cryptyc/example8.cry.load = Private; type Socket = Public; type Host = Public; type Server = Public; type Client = Public; type Word = Public; type Challenge = Public; public Sender: Client; public Receiver: Server; public sent: Word; public to: Word; public got: Word; public from: Word; type MyNonce (a: Host, b: Host, msg : Payload) = Nonce (end (a sent msg to b)); type MyNonceR (a: Host, b: Host, msg : Payload) = Nonce (end (b got msg from a)); type MyMsg = Struct (a: Host, b: Host, msg : Payload, nonce : MyNonce(a, b, msg), nonceR : Challenge); type MyMsgR = Struct (a: Host, b: Host, msg : Payload, nonce : MyNonceR(a, b, msg)); type UMsg = Union (msg: MyMsg, msgR: MyMsgR); type MyKey = Key (UMsg); public Alice : Host; public Bob : Host; private SKey: MyKey; client Sender at Alice is { establish Receiver at Bob is (socket : Socket); input socket is (nonce : Challenge); new (msg : Payload); begin (Alice sent msg to Bob); cast nonce is (nonce' : MyNonce (Alice, Bob, msg)); new (nonceR : Challenge); output socket is ({msg(Alice, Bob, msg, nonce', nonceR)}SKey); input socket is ({msgR(a : Host, b : Host, msg : Payload, nonceR' : MyNonceR(a, b, msg))}SKey); check nonceR is nonceR'; end(b got msg from a); } server Receiver at Bob is (socket : Socket) { new (nonce : Challenge); output socket is (nonce); input socket is ({ msg(a: Host, b: Host, msg : Payload, nonce' : MyNonce(a, b, msg) , nonceR: Challenge)}SKey); check nonce is nonce'; end (a sent msg to b); begin (Bob got msg from Alice); cast nonceR is (nonceR' : MyNonceR(Alice, Bob, msg)); output socket is ({msgR(Alice, Bob, msg, nonceR')}SKey); }
Here we are exchanging two nonces and transferring one effect in each direction in each nonce handshake.
These statements exist:Public Private Key(T) Struct (foo: T1, bar: T2, baz: T3) Union (tag1: T1, tag2: T2, tag3: T3) Nonce (end (M))
You're not limited to 2 entities in a protocol. You can have as many as you want. (Eg, one or more intermediaries). The input and output commands perform pattern matching.establish Receiver at Bob is (socket : Socket); new (msg : Payload); output socket is ({msg}SKey); input socket is ({msg: Payload}SKey); begin (Sender sent msg); end (Sender sent msg); cast nonce is (nonce' : MyNonce (msg)); check nonce is nonce';
Fall 2004 updates:
scrosby@cs.rice.edu, Department of Computer Science, Rice UniversityFall 2003 author:
arudys@rice.edu, Department of Computer Science, Rice University Last modified: Fri Sep 26 15:06:29 CDT 2003