Confirming understanding of security protocol modelled in Scyther - security

In university, I'm currently learning how to use Scyther to model security protocols. Currently I am trying to understand what is happening in an example protocol given to me which is:
# Key Establishment Protocol
#
usertype SessionKey;
protocol ke11(I,R,S)
{
role I
{
var Kir: SessionKey;
send_1(I,S, R);
recv_2(S,I,{Kir}k(I,S));
claim_I1(I,Running,R,Kir);
send_4(I,R,{I}Kir);
recv_5(R,I,{R}Kir);
claim_I3(I,Commit,R,Kir);
claim_I1(I,Secret,Kir);
}
role R
{
var Kir: SessionKey;
recv_3(S,R, (I,{Kir}k(R,S)));
recv_4(I,R,{I}Kir);
claim_R3(R,Running,I,Kir);
send_5(R,I,{R}Kir);
claim_R2(R,Commit,I,Kir);
claim_R1(R,Secret,Kir);
}
role S
{
fresh Kir : SessionKey;
recv_1(I,S, R);
send_2(S,I, {Kir}k(I,S));
send_3(S,R, I,{Kir}k(R,S));
}
}
This has also been given to me:
1. I → S : R
2. S → I : {Kir}k(I,S)
3. S → R : I,{Kir}k(R,S)
4. R → I : {I}Kir
5. I → R : {R}Kir
From my understanding:
I is sending S the value of R
S is sending I the encryption of Kir, which is encrypted using a shared symmetric
key between I and S
S is sending R the encryption of Kir, which is encrypted using a shared symmetric
key between R and S
I is sending R the value of I encrypted using Kir which it got from decrypting the
value in 2 using the shared symmetric key
R is sending I the value of R encrypted using Kir which it got from decrypting the
value in 3 using the shared symmetric key.
I'm not really sure if this is correct so could someone please comment on if my understanding is correct or not. Specifically, I don't really understand what is being sent when I have written that the value of I or R is being sent (I -> S:R).
Any comments would greatly be appreciated.
Many thanks.

Related

Python implementation of ed25519 shared secret agreement

Let's consider the following example. Using Python library tinyec I can write the following code:
def compress(pubKey):
return hex(pubKey.x) + hex(pubKey.y % 2)[2:]
curve = registry.get_curve('brainpoolP256r1')
alicePrivKey = secrets.randbelow(curve.field.n)
alicePubKey = alicePrivKey * curve.g
bobPrivKey = secrets.randbelow(curve.field.n)
bobPubKey = bobPrivKey * curve.g
print("Now exchange the public keys (e.g. through Internet)")
aliceSharedKey = alicePrivKey * bobPubKey
print("Alice shared key:", compress(aliceSharedKey))
bobSharedKey = bobPrivKey * alicePubKey
In this way Alice and Bob are able to derive a shared secret through the Internet. Now, I need to know if I can do the same thing with the ed25519 curve, since I was not able to found any kind of libraries.
So, I am interested in finding a way to do this type of operation safely. How could I do ? What are the best practices for this specific operation?
Thanks

Python Diffie-Hellman exchange cryptography library. Shared key not the same

Following the tutorial listed on the cryptography library documentation I have successfully created a function that demonstrates a Diffie-Hellman exchange. I am now trying to create a proof of concept socket server and socket client.
An undocumented requirement of this application is the sending of the public key to the client. This requires the DHPublicKey object to be serialised and serialised in order for it to be sent over the socket.
By doing this however the shared keys are not the same! I have tried to use different encoding formats (PEM for example) to see if this made a difference. Unfortunatly it has not. I get a different shared key on both sides. Here is an example of my code.
Client
parameters = dh.generate_parameters(generator=2, key_size=1024, backend=default_backend())
client_private_key = parameters.generate_private_key()
client_public_key = client_private_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
# Recv Server Pub key
length = s.recv(2) # Prepend the length of the message
server_public_key = s.recv(int.from_bytes(length, "big"))
print("Got server public key: " + str(server_public_key))
server_public_key = load_der_public_key(server_public_key, default_backend())
# Send Pub key
s.send(len(client_public_key).to_bytes(2, "big") + client_public_key)
print("Generating shared key...")
shared_key = client_private_key.exchange(server_public_key)
print("Our shared key!: " + str(shared_key))
Server
parameters = dh.generate_parameters(generator=2, key_size=1024, backend=default_backend())
server_private_key = parameters.generate_private_key()
server_public_key = server_private_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
newsocket.send(len(server_public_key).to_bytes(2, "big") + server_public_key)
print("Sent server public key: " + str(server_public_key))
length = newsocket.recv(2) # Prepend the length of the message
client_public_key = newsocket.recv(int.from_bytes(length, "big"))
client_public_key = load_der_public_key(client_public_key, default_backend())
# Send the public key to the client
shared_key = server_private_key.exchange(client_public_key)
print("Our shared key is: " + str(shared_key))
As stated, I'm using the Python 3 Library Cryptography and use the following imports
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives.serialization import PublicFormat, Encoding, load_der_public_key
Also, putting the code into one file (Without network serialization) it works! the key is the same! Here is an example of the working code
print("SERVER: Performing DH exchange. DH 2048-bit key size")
parameters = dh.generate_parameters(generator=2, key_size=2048, backend=default_backend()) # Generate a 256-byte key
print("SERVER: Generating server private and public keys")
server_private_key = parameters.generate_private_key()
server_peer_public_key = server_private_key.public_key()
print("CLIENT: Generating private and public keys")
client_private_key = parameters.generate_private_key()
client_peer_public_key = client_private_key.public_key()
print("SERVER: Sending public key to client")
print("CLIENT: Sending public key to server")
server_shared_key = server_private_key.exchange(client_peer_public_key)
client_shared_key = client_private_key.exchange(server_peer_public_key)
print("server key is: " + str(server_shared_key))
print("client key is: " + str(client_shared_key))
What am I doing wrong when serialising or deserialising the key?
Your problem isn’t to do with serializing and deserializing the key, it is because you are generating different DH parameters on the server and the client. They need to be the same for Diffie Hellman to work.
You could generate the parameters on the server and send them to the client, but a better option is to use a set of predefined parameters, for example group 14 defined in RFC 3526.
To do that, change the line
parameters = dh.generate_parameters(generator=2, key_size=1024, backend=default_backend())
in both client and server to:
p = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF
g = 2
params_numbers = dh.DHParameterNumbers(p,g)
parameters = params_numbers.parameters(default_backend())
Now both client and server will be using the same set of parameters and the key agreement will work. It will also be much faster, parameter generation is a costly process.
Your code in a single file works because you only generate one set of parameters that is used by both sides of the exchange.

Stuck in API XAxiDma_BdRingFromHw, why doesn't the S2MM Block descriptor's Completed bit Set?

I am working on Zynq 7z030 and i am trying to receive data on the DDR from the PL side. I am using the AXI DMA SG poll code provided as example by xilinx on SDK. (xaxidma_example_sg_poll.c)
After Configuring DMA -> Setting up the RX channel -> Starting DMA -> I enter the API CheckDmaResult.
Here I call XAxiDma_BdRingFromHw API.
while ((ProcessedBdCount = XAxiDma_BdRingFromHw(RxRingPtr,
XAXIDMA_ALL_BDS,
&BdPtr)) == 0) {
}
This API calls Xil_DCacheInvalidateRange which returns and then the Block descriptor status remains always 0. Thus resulting in forever looping of the XAxiDma_BdRingFromHw. The complete bit never sets.
This happens eventhough I see the TREADY of S2MM go high and receive data in ILA(integrated logic analyser on FPGA end/PL end)
main
....
Status1 = CheckDmaResult(&AxiDma);
.....
-> static int CheckDmaResult(XAxiDma * AxiDmaInstPtr)
....
while ((ProcessedBdCount =
XAxiDma_BdRingFromHw(RxRingPtr,
XAXIDMA_ALL_BDS,
&BdPtr)) == 0) {
}
....
-> XAxiDma_BdRingFromHw(XAxiDma_BdRing * RingPtr, int BdLimit,
XAxiDma_Bd ** BdSetPtr)
....
while (BdCount < BdLimit) {
/* Read the status */
XAXIDMA_CACHE_INVALIDATE(CurBdPtr);
BdSts = XAxiDma_BdRead(CurBdPtr, XAXIDMA_BD_STS_OFFSET);
BdCr = XAxiDma_BdRead(CurBdPtr, XAXIDMA_BD_CTRL_LEN_OFFSET);
/* If the hardware still hasn't processed this BD then we are
* done
*/
if (!(BdSts & XAXIDMA_BD_STS_COMPLETE_MASK)) {
break;
}
.....
could someone please suggest possible reasons or directions i should consider to solve this problem.. any and every suggestion would be a great help.
Thanks in advance!
The problem was with the board (ESD damage).
The DDR started receiving data as soon as the board was changed and the following were observed
further in debug config settings the following needed to be ticked on Under Target Setup
Reset entire system
Program FPGA
Under Application tab
Download Application
Stop at 'main'
by Specifying the correct corresponding .elf file in 'Application ' field

Encrypt in Coldfusion, Decrypt in Node.js

I'm encrypting a string in ColdFusion
enc_string = '7001010000006aaaaaabbbbbb';
uid = encrypt(enc_string,'WTq8zYcZfaWVvMncigHqwQ==','AES','Hex');
// secret key for tests only
Result:
DAEB003D7C9DBDB042C63ED214E85854EAB92A5C1EC555765B565CD8723F9655
Later I want to decrypt that string in Node (just an example)
uid='DAEB003D7C9DBDB042C63ED214E85854EAB92A5C1EC555765B565CD8723F9655'
decipher = crypto.createDecipher('aes-192-ecb', 'WTq8zYcZfaWVvMncigHqwQ==')
decipher.setAutoPadding(false);
dec = decipher.update(uid, 'hex', 'utf8')
dec += decipher.final('utf8')
I have tried few ciphers but with no luck. I would like not to modify the ColdFusion code to make it work, but if there is no other chance I will do that. I want to send some ciphered data with GET from one site to another. Any advice?
EDIT: I tried all AES, DES, with IV, without IV, with & without padding. Tried also base64. Also with no luck.
ColdFusion Encryption with IV
enc_string = '7001010000006aaaaaabbbbbb';
myKey = Tobase64("abcdefghijkl1234");
myIV = charsetDecode("abcdefghijkl9876", "utf-8");
uid=encrypt(enc_string,myKey,'AES/CBC/PKCS5Padding','hex',myIV);
Encrypted uid value is:
614981D0BC6F19A3022FD92CD6EDD3B289214E80D74823C3279E90EBCEF75D90
Now we take it to node:
var Crypto = require('crypto');
var key = new Buffer('abcdefghijkl1234');
var iv = new Buffer('abcdefghijkl9876');
var encrypted = new Buffer('614981D0BC6F19A3022FD92CD6EDD3B289214E80D74823C3279E90EBCEF75D90', 'hex');
var decipher = Crypto.createDecipheriv('aes-128-cbc', key, iv);
var decrypted = decipher.update(encrypted);
var clearText = Buffer.concat([decrypted, decipher.final()]).toString();
console.log(clearText);
Result is:
7001010000006aaaaaabbbbbb
what was expected.
Origin of the problem
Originally in Coldfusion i was using key generated by:
GenerateSecretKey(algorithm [,keysize]);
which generated base64 key which was required by encrypt method. And there was no 'secret' from which was generated.
In Node Crypto method createDecipheriv gets Buffer as params. Buffers requires secret, not keys. I'm not sure why it doesn't work without IV.
What need to be changed in Coldfusion
Don't use GenerateSecretKey if you want to decrypt in other language than CF
Use Tobase64(secret) to generate key
Use IV and generate it using charsetDecode(ivSecret, "utf-8")
Algorithm: AES/CBC/PKCS5Padding
For AES/ECB look #Leigh answer
In Node every input is Buffer.
I think that this short tutorial can help also people who have same issue in other languages like cf->php or cf->python.
A few clarifications and corrections to the accepted answer
Short answer:
Use "crytographically random" keys produced by GenerateSecretKey() rather than creating one with Tobase64(secret).
Although technically ECB mode works (see below), CBC mode is preferred as the more secure method. For CBC, see my full example: Encrypt in ColdFusion, Decrypt in Node.js
Longer Answer:
Don't use GenerateSecretKey if you want to decrypt in other language
No, it is perfectly fine to use the generated value with the encryption functions in other languages - as long as they follow the specifications. That does not mean the values can be used in any language exactly "as is". It may need tweaking to conform with language X or Y's implementation. (For example, a function in language X may expect the key to be a hexadecimal string, instead of base64. So you may need to convert the key value first). That did not quite happen in the original code, which is why the decryption did not work.
GenerateSecretKey() produces a cryptographically random key for the specified algorithm. (While CF generates base64 encoded key strings, it could just as easily be hex encoded. The binary value of the key is what matters.) The generated key is suitable for use with any language that implements the same encryption algorithms and key sizes. However, as I mentioned in the earlier comments, symmetric encryption only works if everything matches. You must use the same key, same algorithm, same iv, etcetera for both encrypting AND decrypting. In the original code, both the "key" and "algorithm" values were different. That is why the decryption failed.
The original code used crypto.createCipher(algorithm, password). Per the API, "password" is used to derive the cipher key. In other words, the Node.js code was using a totally different key than in the CF code. Also, Node.js was configured to use a 192 bit key, whereas the CF code was using a 128 bit key.
To answer your original question, yes - you can use ECB mode (though it is strongly discouraged). However, it requires modifying the CF code to derive the same password Node.js will be using. (The other direction is not possible as it involves one-way hashing.)
To derive the "password" in CF, decode the secret key string into binary and generate an md5 hash. Then decode the hash into binary and re-encode it as base64 to make the encrypt() function happy.
CF:
plainText = "7001010000006aaaaaabbbbbb";
secretKey = "WTq8zYcZfaWVvMncigHqwQ==";
keyHash = hash(binaryDecode(secretKey, "base64"), "md5");
nodeJSPassword = binaryEncode(binaryDecode(keyHash, "hex"), "base64");
encryptedText = encrypt(plainText, nodeJSPassword, "AES/ECB/PKCS5Padding", "Hex");
writeOutput(encryptedText);
Result:
C43E1179C15CD962373A6E28486D6F4ADB12FBB6731EF99C9212474E18D51C70
On the Node.js side, modify the code to use a 128 bit key, not 192. Also, password strings are first decoded into binary. When creating the cipher object, you need to indicate the input string is base64 encoded, to ensure it is interpreted properly.
Node.js
var password = 'WTq8zYcZfaWVvMncigHqwQ==';
var passwordBinary = new Buffer(password, "base64");
var encrypted = 'C43E1179C15CD962373A6E28486D6F4ADB12FBB6731EF99C9212474E18D51C70'
var crypto = require('crypto');
var decipher = crypto.createDecipher('aes-128-ecb', passwordBinary );
var decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
Result:
7001010000006aaaaaabbbbbb
Having said that, Using ECB mode is NOT recommended. The preferred method is CBC mode (with a random iv), which generates less predictable output and hence is more secure.
than CF Use Tobase64(secret) to generate key
Along those same lines, while you can technically use arbitrary strings, ie "abcdefghijkl1234", to generate a key - DON'T. A very important part of strong encryption is using secret keys that are "truly random and contain sufficient entropy". So do not just do it yourself. Use a proven function or library, like GenerateSecretKey(), which was specifically designed for the task.
The ciphers you are using to encrypt and decrypt are not equal.
For Node to decrypt your result to the expected string, you should first make sure that encrypting the initial string in Node gives you the same encrypted result.
Consider the following, which runs through all known (to me) AES ciphers in Node, and tries to get the same encrypted result that you get from Coldfusion:
var crypto = require('crypto');
var key = 'WTq8zYcZfaWVvMncigHqwQ==';
var algorithm;
var ciphers = [
'aes-128-cbc',
'aes-128-cbc-hmac-sha1',
'aes-128-cfb',
'aes-128-cfb1',
'aes-128-cfb8',
'aes-128-ctr',
'aes-128-ecb',
'aes-128-gcm',
'aes-128-ofb',
'aes-128-xts',
'aes-192-cbc',
'aes-192-cfb',
'aes-192-cfb1',
'aes-192-cfb8',
'aes-192-ctr',
'aes-192-ecb',
'aes-192-gcm',
'aes-192-ofb',
'aes-256-cbc',
'aes-256-cbc-hmac-sha1',
'aes-256-cfb',
'aes-256-cfb1',
'aes-256-cfb8',
'aes-256-ctr',
'aes-256-ecb',
'aes-256-gcm',
'aes-256-ofb',
'aes-256-xts',
'aes128',
'aes192',
'aes256'
]
function encrypt(text){
var cipher = crypto.createCipher(algorithm, key);
var crypted = cipher.update(text,'utf8','hex');
crypted += cipher.final('hex');
return crypted;
}
for (var i = 0; i < ciphers.length; i++) {
algorithm = ciphers[i];
console.log(encrypt("7001010000006aaaaaabbbbbb"));
}
If you run this you will get the following output:
ab1e8ddd6be53040fcfdf07578704ed9831c4e962eddd36899fc3819b51d6ade
ab1e8ddd6be53040fcfdf07578704ed9831c4e962eddd36899fc3819b51d6ade
ff19a0b91dad25671632581655f53139ac1f5554383951e255
e4756965c26df5b2e7e2e5291f5a2b1bc835b523ae7e39da0d
ff93cfff713798bcf94ff60fb61a6d9d4ae0a7ad6672e77a22
ff19a0b91dad25671632581655f5313940ed1d69d874cf04d7
70ef98bda47bd95e64221c144c4fdec1e5ad1422ca9f4589653214577adf9d9a
918559eaab9a983f91160dbdb2f093f55b0a2bc011fbe1b309
ff19a0b91dad25671632581655f53139cb62004d669030b400
2c4e36eb6b08107bbdf9c79c2f93160211128977181fee45ab
37fed7d50a56f42fa26805a69c38b12b519e59116702a9f0d15a437791600b3a
01f4d909c587684862ea9e27598f5d5c489028a223cc79be1a
0c482981e6aefa068b0c0429ba1e46894c39d7e7f27d114651
01c9d7545c3bfe8594ebf5aef182f5d4930db0555708057785
01f4d909c587684862ea9e27598f5d5c7aa4939a9008ea18c4
6fb304a32b676bc3ec39575e73752ad71255f7615a94ed93f78e6d367281ee41
7494a477258946d781cb53c9b37622248e0ba84a48c577c9df
01f4d909c587684862ea9e27598f5d5c889a935648f5f7061f
ea16ecf9ad13756f9bd8ad3fcff2a9e06778647d763f88e679dde519e7155cd6
ea16ecf9ad13756f9bd8ad3fcff2a9e06778647d763f88e679dde519e7155cd6
d0688b6632962acf7905ede7e4f9bd7b2d557e3b828a855208
c0119ab62e5c7a3d932042648291f7cd97c30c9b42c9fa1779
d0f72742cc0415a74e201fcc649f90cf9506eac14e24fd96a9
d0688b6632962acf7905ede7e4f9bd7b5e4921830c30ae8223
d6cd01243405e8741e4010698ab2943526f741cfdb2696b5a6d4e7c14479eccf
2592fb4b19fd100c691598c4bdb82188b6e9d6a6b308d0d627
d0688b6632962acf7905ede7e4f9bd7bf375251be38e1d1e08
d9ae0f940e7c40dcb3a620a5e2a1341819632124af5014bf2f
ab1e8ddd6be53040fcfdf07578704ed9831c4e962eddd36899fc3819b51d6ade
37fed7d50a56f42fa26805a69c38b12b519e59116702a9f0d15a437791600b3a
ea16ecf9ad13756f9bd8ad3fcff2a9e06778647d763f88e679dde519e7155cd6
The encrypted result you have from Coldfusion is not present in the above output.
So, using the AES ciphers available in Node, the encrypted result is always different from your encrypted result from Coldfusion. If the encrypted result is always different, you cannot decrypt it to the same value.
The Coldfusion Encryption Docs are not very helpful at describing exactly which algorithm is used when simply specifying "AES". I would strongly recommend specifying a precise algorithm to use, including which key size to use, and choose one that has a corresponding algorithm in Node.

How do you authenticate aws s3 authentication version 4 requests, in node.js?

I'm following this guide: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
And I'm having a difficulty at the last step, where you use the signing key to create the signature.
This step of the first GET example:
signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20130524"),"us-east-1"),"s3"),"aws4_request")
does not have the resulting signing key, so I don't know if it's right. Instead of the signature
f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41 I am getting f03131e53fcdcd3605054f5ead58370d14a672add94bda5da0a69d65d03e7edc.
Can someone tell me what the signing key for the example is? I think it is the step that I'm missing.
I can post my 253 lines of express.js code upon request. The step before this, where I get the string to sign (7344ae5b7ee6c3e7e6b0fe0640412a37625d1fbfff95c48bbb2dc43964946972) is correct.
The correct signing key of the example is dbb893acc010964918f1fd433add87c70e8b0db6be30c1fbeafefa5ec6ba8378.
It is important (but only noted on a different page of the docs) that you use the raw buffers for calculating the signing key, and that you pass a raw buffer to the HMAC function that calculates the final signature.
Or, as pseudocode, given that the third parameter of the hmac(key, msg, format) function allows you whether you want to get a hexadecimal string or a raw byte array:
Buffer dateKey = hmac(('AWS4' + secretAccessKey).toBuffer(), date, 'buffer');
Buffer dateRegionKey = hmac(dateKey, region, 'buffer');
Buffer dateRegionServiceKey = hmac2(dateRegionKey, 's3', 'buffer');
Buffer signingKey = hmac2(dateRegionServiceKey, 'aws4_request', 'buffer');
print("SigningKey\n" + signingKey.toHex());
String signature = hmac(signingKey, stringToSign, 'hex');
print("Signature\n" + signature);

Resources