Closed terrylao closed 3 years ago
Hi, as you indicated the algorithm used is " ECDH key exchange with curve Curve25519 and hash SHA-256 (unaccelerated)". trying to re-implement this without having access to the source code implementation of that algorithm or specification is like taking a stab in the dark. like for example, we don't know what the hash is used for, what data is hashed etc. I suggest you spend sometime trying to read up how openssh implemented theirs then we can put the pieces together using CryptoLib4Pascal.
MY doubt is why your code and putty's code generate different shared key, with same input. so, i think, this should re-implement.
it seems you still don't understand what I am trying to explain. Putty does not use the default Curve25519 standard, it uses a variant that uses SHA-256 for it's internal operation. these are totally 2 different algorithms in a sense.
i found the algorithm of what they said https://git.libssh.org/users/aris/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt
4.3 Shared secret generation
The shared secret, k, is defined in SSH specifications to be a big integer. This number is calculated using the following procedure:
X is the 32 bytes point obtained by the scalar multiplication of the other
side's public key and the local private key scalar.
The whole 32 bytes of the number X are then converted into a big integer k.
This conversion follows the network byte order. This step differs from
RFC5656.
According to the link you provided, it is clearly indicated that their implementation differs from the defined standard (RFC5656). If you want to produce the same result as theirs, I have attached a code below that works as per their standard. However, do note that this is not the Official X25519 spec.
function ReverseArray(const origArray: TBytes): TBytes;
var
i, h: integer;
begin
h := High(origArray);
SetLength(Result, Length(origArray));
for i := 0 to h do
Result[i] := origArray[h - i];
end;
procedure testCurve25519;
var
s: string;
bigint: TBigInteger;
priv, remotepub, mypub, encryptkey, tmp: TBytes;
n: TCryptoLibUInt32Array;
var
kpGen: IAsymmetricCipherKeyPairGenerator;
kpA, kpB: IAsymmetricCipherKeyPair;
agreeA, agreeB: IX25519Agreement;
secretA, secretB: TBytes;
FRandom: ISecureRandom;
privateKey: IX25519PrivateKeyParameters;
publicKey, kpBpublicKey: IX25519PublicKeyParameters;
begin
FRandom := TSecureRandom.Create();
kpGen := TX25519KeyPairGenerator.Create() as IX25519KeyPairGenerator;
kpGen.Init(TX25519KeyGenerationParameters.Create(FRandom)
as IX25519KeyGenerationParameters);
// if (not AreEqual(secretA, secretB)) then
// begin
// Fail('X25519 agreement failed');
// end;
s := 'B81895A577E387CBD485E41D40FDF838EA087CA0F80895E00B2CBC29ECDAD57A';
priv := THex.Decode(s);
privateKey := TX25519PrivateKeyParameters.Create(priv, 0);
publicKey := privateKey.GeneratePublicKey();
kpA := TAsymmetricCipherKeyPair.Create(publicKey, privateKey);
tmp := publicKey.GetEncoded();
s := THex.Encode(tmp);
writeln(Format('my pub from priv: %s', [s]));
s := '00D1F0E49F1FC6F6C69658A504DFD37EF4E7736BE32E4E119531F28091004C5E';
remotepub := THex.Decode(s);
kpBpublicKey := TX25519PublicKeyParameters.Create(remotepub, 0);
agreeA := TX25519Agreement.Create();
agreeA.Init(kpA.Private);
System.SetLength(secretA, agreeA.AgreementSize);
agreeA.CalculateAgreement(kpBpublicKey, secretA, 0);
bigint := TBigInteger.Create(secretA);
writeln(Format('secretA: %s', [THex.Encode(ReverseArray(bigint.ToByteArray()))]));
end;
please reopen if the above solution does not resolve your issue.
Describe the bug i got this information from PUTTY
dumped information :
Event Log: Doing ECDH key exchange with curve Curve25519 and hash SHA-256 (unaccelerated)
Event Log: Doing ECDH my PUB key :
length 32
423EBB48BE9425B867005C4F269C369CCA11D514C30A7276C8E3193DC2FAD355
Event Log: my Private Key:
length of int 32
B81895A577E387CBD485E41D40FDF838EA087CA0F80895E00B2CBC29ECDAD57A
Event Log: get from packget: hostkeydata :
length 149
000000077373682D72736100000001230000008100C7576DF22171A80402819B89F3D7A9F3AFC4685F048614351ACBB1446DD2D273BA910D0C4DC7CF2583AA676DDA653A9C17DD6F5DA010BEC5C8E25B9117694276D82E078922A785B86E90D78DDFB383158381E8A3F99B97018F095EDC374F2EACE724222C97629CD19E6A35F25018D425A23205228171C08E8183F3A43E1DFABD
Event Log: Doing ECDH get my PUB key :
length 32
423EBB48BE9425B867005C4F269C369CCA11D514C30A7276C8E3193DC2FAD355
Event Log: get from packget: remote pub key :
length 32
00D1F0E49F1FC6F6C69658A504DFD37EF4E7736BE32E4E119531F28091004C5E
Event Log: get from packget: remote pub key to mpint
length of int 32
00D1F0E49F1FC6F6C69658A504DFD37EF4E7736BE32E4E119531F28091004C5E
Event Log: calculated K:
length of int 32
5C83053209F541B4DD1135D14690312E2C3396EAA8ABC48068854E3CBCDAFC07
the shared secret should be : 5C83053209F541B4DD1135D14690312E2C3396EAA8ABC48068854E3CBCDAFC07
To Reproduce but my test code below do not get the same shared secret, do i miss something?
procedure testCurve25519; var s:string; priv,remotepub,mypub,encryptkey,tmp:TBytes; n:TCryptoLibUInt32Array; var kpGen: IAsymmetricCipherKeyPairGenerator; kpA, kpB: IAsymmetricCipherKeyPair; agreeA, agreeB: IX25519Agreement; secretA, secretB: TBytes; FRandom: ISecureRandom; privateKey: IX25519PrivateKeyParameters; publicKey,kpBpublicKey: IX25519PublicKeyParameters; begin FRandom := TSecureRandom.Create(); kpGen := TX25519KeyPairGenerator.Create() as IX25519KeyPairGenerator; kpGen.Init(TX25519KeyGenerationParameters.Create(FRandom) as IX25519KeyGenerationParameters); //if (not AreEqual(secretA, secretB)) then //begin // Fail('X25519 agreement failed'); //end; s:='B81895A577E387CBD485E41D40FDF838EA087CA0F80895E00B2CBC29ECDAD57A'; priv := THex.Decode(s); privateKey := TX25519PrivateKeyParameters.Create(priv,0); publicKey := privateKey.GeneratePublicKey(); kpA := TAsymmetricCipherKeyPair.Create(publicKey, privateKey); tmp:=publicKey.GetEncoded(); s:=THex.Encode(tmp); writeln(StdOut,'my pub from priv:',s); s:='00D1F0E49F1FC6F6C69658A504DFD37EF4E7736BE32E4E119531F28091004C5E'; remotepub := THex.Decode(s); kpBpublicKey:=TX25519PublicKeyParameters.Create(remotepub,0); agreeA := TX25519Agreement.Create(); agreeA.Init(kpA.Private); System.SetLength(secretA, agreeA.AgreementSize); agreeA.CalculateAgreement(kpBpublicKey, secretA, 0); s:=THex.Encode(secretA); writeln(StdOut,'secretA:',s); System.SetLength(encryptkey, TX25519.PointSize); System.SetLength(mypub, TX25519.PointSize); writeln(StdOut,'test private to public'); TX25519.ScalarMultBase(priv, 0, mypub, 0); s:=THex.Encode(mypub); writeln(StdOut,'my pub:',s); TX25519.ScalarMult(priv, 0,remotepub , 0, encryptkey, 0); s:=THex.Encode(encryptkey); writeln(StdOut,'shared secret:',s);
end; OUTPUT:
my pub from priv:423EBB48BE9425B867005C4F269C369CCA11D514C30A7276C8E3193DC2FAD355
secretA:07FCDABC3C4E856880C4ABA8EA96332C2E319046D13511DDB441F5093205835C
test private to public
my pub:423EBB48BE9425B867005C4F269C369CCA11D514C30A7276C8E3193DC2FAD355
shared secret:07FCDABC3C4E856880C4ABA8EA96332C2E319046D13511DDB441F5093205835C
Expected behavior Shared secret should same as putty? I am making an SSH client.
Environment (please complete the following information):