From 3ba510c20b19fb3bb80d5ab5006119eabf136895 Mon Sep 17 00:00:00 2001 From: webwarrior Date: Wed, 31 May 2023 14:00:36 +0200 Subject: [PATCH 01/10] NOnion: introduced expanded blinded key types Introduced types for public and private expanded blinded keys that wrap byte array representation of those keys. Use those types instead of raw byte arrays. This should improve code clarity and potentially, safety. --- NOnion.Tests/KeyBlindingTests.cs | 2 +- NOnion/Crypto/HiddenServicesCipher.fs | 10 +++++----- NOnion/Directory/TorDirectory.fs | 2 +- NOnion/KeyTypes.fs | 11 +++++++++++ NOnion/NOnion.fsproj | 1 + NOnion/Services/TorServiceClient.fs | 7 ++++--- NOnion/Services/TorServiceHost.fs | 14 +++++++++----- NOnion/Utility/CertificateUtil.fs | 4 ++-- 8 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 NOnion/KeyTypes.fs diff --git a/NOnion.Tests/KeyBlindingTests.cs b/NOnion.Tests/KeyBlindingTests.cs index a5e2d6f0..bade62db 100644 --- a/NOnion.Tests/KeyBlindingTests.cs +++ b/NOnion.Tests/KeyBlindingTests.cs @@ -17,7 +17,7 @@ public class KeyBlindingTests [Test] public void CheckBlindedPublicKey() { - var computed = HiddenServicesCipher.CalculateBlindedPublicKey(blindingFactor, publicKey); + var computed = HiddenServicesCipher.CalculateBlindedPublicKey(blindingFactor, publicKey).ToByteArray(); CollectionAssert.AreEqual(computed, blindedPublicKey); } } diff --git a/NOnion/Crypto/HiddenServicesCipher.fs b/NOnion/Crypto/HiddenServicesCipher.fs index 202c61bb..94c59607 100644 --- a/NOnion/Crypto/HiddenServicesCipher.fs +++ b/NOnion/Crypto/HiddenServicesCipher.fs @@ -88,13 +88,13 @@ module HiddenServicesCipher = Ed25519Clamp blindingFactor match Ed25519.CalculateBlindedPublicKey(publicKey, blindingFactor) with - | true, output -> output + | true, output -> ExpandedBlindedPublicKey output | false, _ -> failwith "can't calculate blinded public key" let CalculateExpandedBlindedPrivateKey (blindingFactor: array) (masterPrivateKey: array) - = + : ExpandedBlindedPrivateKey = let expandedMasterPrivateKey = Array.zeroCreate 64 let hashEngine = Sha512Digest() @@ -112,14 +112,14 @@ module HiddenServicesCipher = "Derive temporary signing key hash input" ) with - | true, output -> output + | true, output -> ExpandedBlindedPrivateKey output | false, _ -> failwith "can't calculate blinded private key" let BuildBlindedPublicKey (periodNumber: uint64, periodLength: uint64) (publicKey: array) - = + : ExpandedBlindedPublicKey = let blindingFactor = CalculateBlindingFactor periodNumber periodLength publicKey @@ -154,7 +154,7 @@ module HiddenServicesCipher = [ "subcredential" |> Encoding.ASCII.GetBytes credential - blindedKey + blindedKey.ToByteArray() ] |> SHA3256 diff --git a/NOnion/Directory/TorDirectory.fs b/NOnion/Directory/TorDirectory.fs index 7e03a77a..b8d3137c 100644 --- a/NOnion/Directory/TorDirectory.fs +++ b/NOnion/Directory/TorDirectory.fs @@ -583,7 +583,7 @@ type TorDirectory = |> Async.StartAsTask member self.GetResponsibleHiddenServiceDirectories - (blindedPublicKey: array) + (ExpandedBlindedPublicKey blindedPublicKey) (sharedRandomValue: string) (periodNumber: uint64) (periodLength: uint64) diff --git a/NOnion/KeyTypes.fs b/NOnion/KeyTypes.fs new file mode 100644 index 00000000..23733201 --- /dev/null +++ b/NOnion/KeyTypes.fs @@ -0,0 +1,11 @@ +namespace NOnion + + +type ExpandedBlindedPrivateKey = ExpandedBlindedPrivateKey of array + +type ExpandedBlindedPublicKey = + | ExpandedBlindedPublicKey of array + + member self.ToByteArray() = + match self with + | ExpandedBlindedPublicKey bytes -> bytes diff --git a/NOnion/NOnion.fsproj b/NOnion/NOnion.fsproj index 50428413..9d4ebae3 100644 --- a/NOnion/NOnion.fsproj +++ b/NOnion/NOnion.fsproj @@ -19,6 +19,7 @@ + diff --git a/NOnion/Services/TorServiceClient.fs b/NOnion/Services/TorServiceClient.fs index 9264dfbc..a0fb37cf 100644 --- a/NOnion/Services/TorServiceClient.fs +++ b/NOnion/Services/TorServiceClient.fs @@ -110,8 +110,9 @@ type TorServiceClient = (sprintf "/tor/hs/%i/%s" Constants.HiddenServices.Version - ((Convert.ToBase64String - blindedPublicKey))) + (Convert.ToBase64String + <| blindedPublicKey.ToByteArray + ())) false return @@ -188,7 +189,7 @@ type TorServiceClient = let secretInput = Array.concat [ - blindedPublicKey + blindedPublicKey.ToByteArray() HiddenServicesCipher.GetSubCredential (periodNum, periodLength) publicKey diff --git a/NOnion/Services/TorServiceHost.fs b/NOnion/Services/TorServiceHost.fs index 7d6c10e0..897579ea 100644 --- a/NOnion/Services/TorServiceHost.fs +++ b/NOnion/Services/TorServiceHost.fs @@ -483,7 +483,7 @@ type TorServiceHost let secretInput = Array.concat [ - blindedPublicKey + blindedPublicKey.ToByteArray() HiddenServicesCipher.GetSubCredential (periodNum, periodLength) (masterPublicKey.GetEncoded()) @@ -529,9 +529,11 @@ type TorServiceHost ((info.AuthKey.Public :?> Ed25519PublicKeyParameters) .GetEncoded()) - (descriptorSigningPublicKey.GetEncoded()) + (descriptorSigningPublicKey.GetEncoded() + |> ExpandedBlindedPublicKey) (descriptorSigningPrivateKey.GetEncoded - ()) + () + |> ExpandedBlindedPrivateKey) Constants.HiddenServices.Descriptor.CertificateLifetime let encKeyBytes = @@ -556,9 +558,11 @@ type TorServiceHost Certificate.CreateNew CertType.IntroPointEncKeySignedByDescriptorSigningKey convertedX25519Key - (descriptorSigningPublicKey.GetEncoded()) + (descriptorSigningPublicKey.GetEncoded() + |> ExpandedBlindedPublicKey) (descriptorSigningPrivateKey.GetEncoded - ()) + () + |> ExpandedBlindedPrivateKey) Constants.HiddenServices.Descriptor.CertificateLifetime { diff --git a/NOnion/Utility/CertificateUtil.fs b/NOnion/Utility/CertificateUtil.fs index b4327b88..81da4480 100644 --- a/NOnion/Utility/CertificateUtil.fs +++ b/NOnion/Utility/CertificateUtil.fs @@ -70,8 +70,8 @@ type Certificate = static member CreateNew certType (certifiedKey: array) - (signingPublicKey: array) - (signingPrivateKey: array) + (ExpandedBlindedPublicKey signingPublicKey) + (ExpandedBlindedPrivateKey signingPrivateKey) (lifetime: TimeSpan) = let unsignedCertificate = From a37f69c609a9fd25c5399f4a1a5f649080c583df Mon Sep 17 00:00:00 2001 From: webwarrior Date: Wed, 31 May 2023 14:34:24 +0200 Subject: [PATCH 02/10] NOnion: changed some types used for keys Introduced NTorOnionKey type that wraps byte array and use it where applicable. Use Ed25519PublicKeyParameters instead of byte array for IntroductionPointInfo.MasterPublicKey. This should improve code clarity and potentially, safety. --- NOnion.Tests/CircuitHelper.cs | 2 +- NOnion.Tests/TorDirectoryTests.cs | 2 +- NOnion/Cells/Relay/RelayIntroduce.fs | 11 +++++++---- NOnion/Directory/HiddenServiceDescriptorDocument.fs | 9 ++++++--- NOnion/Directory/TorDirectory.fs | 2 +- NOnion/KeyTypes.fs | 7 +++++++ NOnion/Network/TorCircuit.fs | 2 +- NOnion/Services/TorServiceHost.fs | 9 ++++----- NOnion/TorHandshakes/NTorHandshake.fs | 2 +- 9 files changed, 29 insertions(+), 17 deletions(-) diff --git a/NOnion.Tests/CircuitHelper.cs b/NOnion.Tests/CircuitHelper.cs index a76de0bb..7207b53a 100644 --- a/NOnion.Tests/CircuitHelper.cs +++ b/NOnion.Tests/CircuitHelper.cs @@ -23,7 +23,7 @@ static private CircuitNodeDetail ConvertToCircuitNodeDetail(ServerDescriptorEntr var fingerprintBytes = Hex.ToByteArray(server.Fingerprint.Value); var nTorOnionKeyBytes = Base64Util.FromString(server.NTorOnionKey.Value); var endpoint = IPEndPoint.Parse($"{server.Address.Value}:{server.OnionRouterPort.Value}"); - return CircuitNodeDetail.NewCreate(endpoint, nTorOnionKeyBytes, fingerprintBytes); + return CircuitNodeDetail.NewCreate(endpoint, NTorOnionKey.NewNTorOnionKey(nTorOnionKeyBytes), fingerprintBytes); } /* It's possible that the router returned by GetRandomFallbackDirectory diff --git a/NOnion.Tests/TorDirectoryTests.cs b/NOnion.Tests/TorDirectoryTests.cs index 0285b663..4dbf0337 100644 --- a/NOnion.Tests/TorDirectoryTests.cs +++ b/NOnion.Tests/TorDirectoryTests.cs @@ -57,7 +57,7 @@ private async Task ReturnRandomRouter() var (endPoint, router) = await directory.GetRouterAsync(RouterType.Normal); Assert.IsTrue(router.IsCreate); Assert.IsFalse(((CircuitNodeDetail.Create)router).IdentityKey.All(x => x == 0)); - Assert.IsFalse(((CircuitNodeDetail.Create)router).NTorOnionKey.All(x => x == 0)); + Assert.IsFalse(((CircuitNodeDetail.Create)router).NTorOnionKey.ToByteArray().All(x => x == 0)); Assert.IsNotNull(((CircuitNodeDetail.Create)router).EndPoint); Assert.That(endPoint, Is.EqualTo(((CircuitNodeDetail.Create)router).EndPoint)); } diff --git a/NOnion/Cells/Relay/RelayIntroduce.fs b/NOnion/Cells/Relay/RelayIntroduce.fs index d45f3ada..fb3c93f4 100644 --- a/NOnion/Cells/Relay/RelayIntroduce.fs +++ b/NOnion/Cells/Relay/RelayIntroduce.fs @@ -10,7 +10,7 @@ type RelayIntroduceInnerData = { RendezvousCookie: array Extensions: List - OnionKey: array + OnionKey: NTorOnionKey RendezvousLinkSpecifiers: List } @@ -41,7 +41,8 @@ type RelayIntroduceInnerData = if onionKeyLength <> Constants.NTorPublicKeyLength then failwith "Invalid onion key length" - let onionKey = reader.ReadBytes Constants.NTorPublicKeyLength + let onionKey = + reader.ReadBytes Constants.NTorPublicKeyLength |> NTorOnionKey let rec readLinkSpecifier (n: byte) (state: List) = if n = 0uy then @@ -62,6 +63,8 @@ type RelayIntroduceInnerData = } member self.ToBytes() = + let onionKeyBytes = self.OnionKey.ToByteArray() + Array.concat [ self.RendezvousCookie @@ -70,10 +73,10 @@ type RelayIntroduceInnerData = |> List.map(fun ext -> ext.ToBytes()) |> Array.concat Array.singleton 1uy - self.OnionKey.Length + onionKeyBytes.Length |> uint16 |> IntegerSerialization.FromUInt16ToBigEndianByteArray - self.OnionKey + onionKeyBytes self.RendezvousLinkSpecifiers.Length |> byte |> Array.singleton self.RendezvousLinkSpecifiers |> List.map(fun link -> link.ToBytes()) diff --git a/NOnion/Directory/HiddenServiceDescriptorDocument.fs b/NOnion/Directory/HiddenServiceDescriptorDocument.fs index c5805595..a2f56629 100644 --- a/NOnion/Directory/HiddenServiceDescriptorDocument.fs +++ b/NOnion/Directory/HiddenServiceDescriptorDocument.fs @@ -8,7 +8,7 @@ open NOnion.Utility type IntroductionPointEntry = { - OnionKey: Option> + OnionKey: Option AuthKey: Option EncKey: Option> EncKeyCert: Option @@ -60,7 +60,10 @@ type IntroductionPointEntry = innerParse { state with OnionKey = - readWord() |> Convert.FromBase64String |> Some + readWord() + |> Convert.FromBase64String + |> NTorOnionKey + |> Some } | "enc-key" -> lines.Dequeue() |> ignore @@ -127,7 +130,7 @@ type IntroductionPointEntry = "HS document (most inner wrapper) is incomplete, missing linkspecifier" match self.OnionKey with - | Some onionKey -> + | Some(NTorOnionKey onionKey) -> appendLine( sprintf "onion-key ntor %s" (Convert.ToBase64String onionKey) ) diff --git a/NOnion/Directory/TorDirectory.fs b/NOnion/Directory/TorDirectory.fs index b8d3137c..970f2eb9 100644 --- a/NOnion/Directory/TorDirectory.fs +++ b/NOnion/Directory/TorDirectory.fs @@ -276,7 +276,7 @@ type TorDirectory = endpoint, CircuitNodeDetail.Create( endpoint, - nTorOnionKeyBytes, + NTorOnionKey nTorOnionKeyBytes, fingerprintBytes ) } diff --git a/NOnion/KeyTypes.fs b/NOnion/KeyTypes.fs index 23733201..dd724f17 100644 --- a/NOnion/KeyTypes.fs +++ b/NOnion/KeyTypes.fs @@ -9,3 +9,10 @@ type ExpandedBlindedPublicKey = member self.ToByteArray() = match self with | ExpandedBlindedPublicKey bytes -> bytes + +type NTorOnionKey = + | NTorOnionKey of array + + member self.ToByteArray() = + match self with + | NTorOnionKey bytes -> bytes diff --git a/NOnion/Network/TorCircuit.fs b/NOnion/Network/TorCircuit.fs index 39edec50..12e49e48 100644 --- a/NOnion/Network/TorCircuit.fs +++ b/NOnion/Network/TorCircuit.fs @@ -25,7 +25,7 @@ type CircuitNodeDetail = | FastCreate | Create of EndPoint: IPEndPoint * - NTorOnionKey: array * + NTorOnionKey: NTorOnionKey * IdentityKey: array member self.GetIdentityKey() = diff --git a/NOnion/Services/TorServiceHost.fs b/NOnion/Services/TorServiceHost.fs index 897579ea..540467ad 100644 --- a/NOnion/Services/TorServiceHost.fs +++ b/NOnion/Services/TorServiceHost.fs @@ -29,8 +29,8 @@ type IntroductionPointInfo = Address: IPEndPoint EncryptionKey: AsymmetricCipherKeyPair AuthKey: AsymmetricCipherKeyPair - MasterPublicKey: array - OnionKey: array + MasterPublicKey: Ed25519PublicKeyParameters + OnionKey: NTorOnionKey Fingerprint: array } @@ -195,7 +195,7 @@ type TorServiceHost (introductionPointDetails.EncryptionKey.Public :?> X25519PublicKeyParameters) periodInfo - introductionPointDetails.MasterPublicKey + (introductionPointDetails.MasterPublicKey.GetEncoded()) use decryptedStream = new MemoryStream(decryptedData) use decryptedReader = new BinaryReader(decryptedStream) @@ -307,8 +307,7 @@ type TorServiceHost EncryptionKey = encKeyPair OnionKey = onionKey Fingerprint = fingerprint - MasterPublicKey = - masterPublicKey.GetEncoded() + MasterPublicKey = masterPublicKey } do! circuit.Create guardNodeDetail |> Async.Ignore diff --git a/NOnion/TorHandshakes/NTorHandshake.fs b/NOnion/TorHandshakes/NTorHandshake.fs index 9f88a43a..595cac4f 100644 --- a/NOnion/TorHandshakes/NTorHandshake.fs +++ b/NOnion/TorHandshakes/NTorHandshake.fs @@ -22,7 +22,7 @@ type NTorHandshake = static member Create (identityDigest: array) - (nTorOnionKey: array) + (NTorOnionKey nTorOnionKey) = let privateKey, publicKey = From 1fc03050f026be6ea95bcd720d17dc1590a71054 Mon Sep 17 00:00:00 2001 From: webwarrior Date: Thu, 1 Jun 2023 11:42:17 +0200 Subject: [PATCH 03/10] NOnion: introduced IdentityKey type Introduced IdentityKey type that wraps byte array and use it where applicable. This should improve code clarity and potentially, safety. --- NOnion.Tests/CircuitHelper.cs | 4 +++- NOnion.Tests/TorDirectoryTests.cs | 2 +- NOnion/Directory/TorDirectory.fs | 2 +- NOnion/KeyTypes.fs | 7 +++++++ NOnion/Network/TorCircuit.fs | 4 ++-- NOnion/Network/TorGuard.fs | 6 +++--- NOnion/Services/TorServiceClient.fs | 4 ++-- NOnion/Services/TorServiceHost.fs | 8 +++++--- NOnion/TorHandshakes/NTorHandshake.fs | 10 +++++----- 9 files changed, 29 insertions(+), 18 deletions(-) diff --git a/NOnion.Tests/CircuitHelper.cs b/NOnion.Tests/CircuitHelper.cs index 7207b53a..8a7cd85d 100644 --- a/NOnion.Tests/CircuitHelper.cs +++ b/NOnion.Tests/CircuitHelper.cs @@ -23,7 +23,9 @@ static private CircuitNodeDetail ConvertToCircuitNodeDetail(ServerDescriptorEntr var fingerprintBytes = Hex.ToByteArray(server.Fingerprint.Value); var nTorOnionKeyBytes = Base64Util.FromString(server.NTorOnionKey.Value); var endpoint = IPEndPoint.Parse($"{server.Address.Value}:{server.OnionRouterPort.Value}"); - return CircuitNodeDetail.NewCreate(endpoint, NTorOnionKey.NewNTorOnionKey(nTorOnionKeyBytes), fingerprintBytes); + return CircuitNodeDetail.NewCreate(endpoint, + NTorOnionKey.NewNTorOnionKey(nTorOnionKeyBytes), + IdentityKey.NewIdentityKey(fingerprintBytes)); } /* It's possible that the router returned by GetRandomFallbackDirectory diff --git a/NOnion.Tests/TorDirectoryTests.cs b/NOnion.Tests/TorDirectoryTests.cs index 4dbf0337..c4256059 100644 --- a/NOnion.Tests/TorDirectoryTests.cs +++ b/NOnion.Tests/TorDirectoryTests.cs @@ -56,7 +56,7 @@ private async Task ReturnRandomRouter() TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), cachePath); var (endPoint, router) = await directory.GetRouterAsync(RouterType.Normal); Assert.IsTrue(router.IsCreate); - Assert.IsFalse(((CircuitNodeDetail.Create)router).IdentityKey.All(x => x == 0)); + Assert.IsFalse(((CircuitNodeDetail.Create)router).IdentityKey.ToByteArray().All(x => x == 0)); Assert.IsFalse(((CircuitNodeDetail.Create)router).NTorOnionKey.ToByteArray().All(x => x == 0)); Assert.IsNotNull(((CircuitNodeDetail.Create)router).EndPoint); Assert.That(endPoint, Is.EqualTo(((CircuitNodeDetail.Create)router).EndPoint)); diff --git a/NOnion/Directory/TorDirectory.fs b/NOnion/Directory/TorDirectory.fs index 970f2eb9..e380356b 100644 --- a/NOnion/Directory/TorDirectory.fs +++ b/NOnion/Directory/TorDirectory.fs @@ -277,7 +277,7 @@ type TorDirectory = CircuitNodeDetail.Create( endpoint, NTorOnionKey nTorOnionKeyBytes, - fingerprintBytes + IdentityKey fingerprintBytes ) } diff --git a/NOnion/KeyTypes.fs b/NOnion/KeyTypes.fs index dd724f17..8d64894d 100644 --- a/NOnion/KeyTypes.fs +++ b/NOnion/KeyTypes.fs @@ -16,3 +16,10 @@ type NTorOnionKey = member self.ToByteArray() = match self with | NTorOnionKey bytes -> bytes + +type IdentityKey = + | IdentityKey of array + + member self.ToByteArray() = + match self with + | IdentityKey bytes -> bytes diff --git a/NOnion/Network/TorCircuit.fs b/NOnion/Network/TorCircuit.fs index 12e49e48..fc50c1ba 100644 --- a/NOnion/Network/TorCircuit.fs +++ b/NOnion/Network/TorCircuit.fs @@ -26,7 +26,7 @@ type CircuitNodeDetail = | Create of EndPoint: IPEndPoint * NTorOnionKey: NTorOnionKey * - IdentityKey: array + IdentityKey: IdentityKey member self.GetIdentityKey() = match self with @@ -694,7 +694,7 @@ and TorCircuit { LinkSpecifier.Type = LinkSpecifierType.LegacyIdentity - Data = identityKey + Data = identityKey.ToByteArray() } ] HandshakeType = HandshakeType.NTor diff --git a/NOnion/Network/TorGuard.fs b/NOnion/Network/TorGuard.fs index 8450be42..a02b0185 100644 --- a/NOnion/Network/TorGuard.fs +++ b/NOnion/Network/TorGuard.fs @@ -49,7 +49,7 @@ type TorGuard ( client: TcpClient, sslStream: SslStream, - fingerprintOpt: Option> + fingerprintOpt: Option ) = let shutdownToken = new CancellationTokenSource() @@ -116,7 +116,7 @@ type TorGuard static member private InnerNewClient (ipEndpoint: IPEndPoint) - (fingerprintOpt: Option>) + (fingerprintOpt: Option) = async { let tcpClient = new TcpClient() @@ -475,7 +475,7 @@ type TorGuard let sha1 = SHA1.Create() let hash = sha1.ComputeHash(getPublicKeyBytes idCertificate) - if hash <> fingerprint then + if hash <> fingerprint.ToByteArray() then raise <| GuardConnectionFailedException("RSA Identity was invalid") | None -> () diff --git a/NOnion/Services/TorServiceClient.fs b/NOnion/Services/TorServiceClient.fs index a0fb37cf..ee94300c 100644 --- a/NOnion/Services/TorServiceClient.fs +++ b/NOnion/Services/TorServiceClient.fs @@ -373,7 +373,7 @@ type TorServiceClient = CircuitNodeDetail.Create( endpointSpecifier, introductionPoint.OnionKey.Value, - identityKey + IdentityKey identityKey ) return @@ -434,7 +434,7 @@ type TorServiceClient = { LinkSpecifier.Type = LinkSpecifierType.LegacyIdentity - Data = identityKey + Data = identityKey.ToByteArray() } ] } diff --git a/NOnion/Services/TorServiceHost.fs b/NOnion/Services/TorServiceHost.fs index 540467ad..e2679e80 100644 --- a/NOnion/Services/TorServiceHost.fs +++ b/NOnion/Services/TorServiceHost.fs @@ -31,7 +31,7 @@ type IntroductionPointInfo = AuthKey: AsymmetricCipherKeyPair MasterPublicKey: Ed25519PublicKeyParameters OnionKey: NTorOnionKey - Fingerprint: array + Fingerprint: IdentityKey } type TorServiceHost @@ -234,7 +234,7 @@ type TorServiceHost |> Seq.tryExactlyOne match linkSpecifierOpt with - | Some linkSpecifier -> linkSpecifier.Data + | Some linkSpecifier -> IdentityKey linkSpecifier.Data | None -> failwith "No rendezvous fingerprint found!" let connectToRendezvousJob = @@ -506,7 +506,9 @@ type TorServiceHost { LinkSpecifier.Type = LinkSpecifierType.LegacyIdentity - Data = info.Fingerprint + Data = + info.Fingerprint.ToByteArray + () } ] diff --git a/NOnion/TorHandshakes/NTorHandshake.fs b/NOnion/TorHandshakes/NTorHandshake.fs index 595cac4f..73c7e2bb 100644 --- a/NOnion/TorHandshakes/NTorHandshake.fs +++ b/NOnion/TorHandshakes/NTorHandshake.fs @@ -15,13 +15,13 @@ type NTorHandshake = { RandomClientPrivateKey: X25519PrivateKeyParameters RandomClientPublicKey: X25519PublicKeyParameters - IdentityDigest: array + IdentityDigest: IdentityKey NTorOnionKey: X25519PublicKeyParameters } static member Create - (identityDigest: array) + (identityDigest: IdentityKey) (NTorOnionKey nTorOnionKey) = @@ -46,7 +46,7 @@ type NTorHandshake = member self.GenerateClientMaterial() = Array.concat [ - self.IdentityDigest + self.IdentityDigest.ToByteArray() self.NTorOnionKey.GetEncoded() self.RandomClientPublicKey.GetEncoded() ] @@ -79,7 +79,7 @@ type NTorHandshake = [ sharedSecretWithY sharedSecretWithB - self.IdentityDigest + self.IdentityDigest.ToByteArray() self.NTorOnionKey.GetEncoded() self.RandomClientPublicKey.GetEncoded() randomServerPublicKey.GetEncoded() @@ -97,7 +97,7 @@ type NTorHandshake = Array.concat [ verify - self.IdentityDigest + self.IdentityDigest.ToByteArray() self.NTorOnionKey.GetEncoded() randomServerPublicKey.GetEncoded() self.RandomClientPublicKey.GetEncoded() From d62958bac9e97fd287253501ce15e3b7d36de4df Mon Sep 17 00:00:00 2001 From: webwarrior Date: Thu, 1 Jun 2023 12:24:51 +0200 Subject: [PATCH 04/10] Services: refactoring Renamed OnionKey to NTorOnionKey in IntroductionPointInfo. --- NOnion/Services/TorServiceHost.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NOnion/Services/TorServiceHost.fs b/NOnion/Services/TorServiceHost.fs index e2679e80..bfcbd55c 100644 --- a/NOnion/Services/TorServiceHost.fs +++ b/NOnion/Services/TorServiceHost.fs @@ -30,7 +30,7 @@ type IntroductionPointInfo = EncryptionKey: AsymmetricCipherKeyPair AuthKey: AsymmetricCipherKeyPair MasterPublicKey: Ed25519PublicKeyParameters - OnionKey: NTorOnionKey + NTorOnionKey: NTorOnionKey Fingerprint: IdentityKey } @@ -305,7 +305,7 @@ type TorServiceHost IntroductionPointInfo.Address = address AuthKey = authKeyPair EncryptionKey = encKeyPair - OnionKey = onionKey + NTorOnionKey = onionKey Fingerprint = fingerprint MasterPublicKey = masterPublicKey } @@ -568,7 +568,7 @@ type TorServiceHost { IntroductionPointEntry.OnionKey = - Some info.OnionKey + Some info.NTorOnionKey AuthKey = Some authKeyCert EncKey = Some encKeyBytes EncKeyCert = Some encKeyCert From 5e22cc44c89d94a29b4aac58c8cf39094e9fef55 Mon Sep 17 00:00:00 2001 From: webwarrior Date: Thu, 1 Jun 2023 12:31:47 +0200 Subject: [PATCH 05/10] NOnion: make NTorOnionKey validate length Make NTorOnionKey type validate key length on creation. --- NOnion.Tests/CircuitHelper.cs | 2 +- NOnion/Directory/HiddenServiceDescriptorDocument.fs | 6 ++++-- NOnion/KeyTypes.fs | 9 +++++---- NOnion/TorHandshakes/NTorHandshake.fs | 5 +++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/NOnion.Tests/CircuitHelper.cs b/NOnion.Tests/CircuitHelper.cs index 8a7cd85d..aee632f4 100644 --- a/NOnion.Tests/CircuitHelper.cs +++ b/NOnion.Tests/CircuitHelper.cs @@ -24,7 +24,7 @@ static private CircuitNodeDetail ConvertToCircuitNodeDetail(ServerDescriptorEntr var nTorOnionKeyBytes = Base64Util.FromString(server.NTorOnionKey.Value); var endpoint = IPEndPoint.Parse($"{server.Address.Value}:{server.OnionRouterPort.Value}"); return CircuitNodeDetail.NewCreate(endpoint, - NTorOnionKey.NewNTorOnionKey(nTorOnionKeyBytes), + new NTorOnionKey(nTorOnionKeyBytes), IdentityKey.NewIdentityKey(fingerprintBytes)); } diff --git a/NOnion/Directory/HiddenServiceDescriptorDocument.fs b/NOnion/Directory/HiddenServiceDescriptorDocument.fs index a2f56629..60da092b 100644 --- a/NOnion/Directory/HiddenServiceDescriptorDocument.fs +++ b/NOnion/Directory/HiddenServiceDescriptorDocument.fs @@ -130,9 +130,11 @@ type IntroductionPointEntry = "HS document (most inner wrapper) is incomplete, missing linkspecifier" match self.OnionKey with - | Some(NTorOnionKey onionKey) -> + | Some nTorOnionKey -> appendLine( - sprintf "onion-key ntor %s" (Convert.ToBase64String onionKey) + sprintf + "onion-key ntor %s" + (Convert.ToBase64String <| nTorOnionKey.ToByteArray()) ) |> ignore | None -> diff --git a/NOnion/KeyTypes.fs b/NOnion/KeyTypes.fs index 8d64894d..d35f7cb1 100644 --- a/NOnion/KeyTypes.fs +++ b/NOnion/KeyTypes.fs @@ -10,12 +10,13 @@ type ExpandedBlindedPublicKey = match self with | ExpandedBlindedPublicKey bytes -> bytes -type NTorOnionKey = - | NTorOnionKey of array +type NTorOnionKey(bytes: array) = + do + if bytes.Length <> Constants.NTorPublicKeyLength then + failwith "Invalid onion key length" member self.ToByteArray() = - match self with - | NTorOnionKey bytes -> bytes + bytes type IdentityKey = | IdentityKey of array diff --git a/NOnion/TorHandshakes/NTorHandshake.fs b/NOnion/TorHandshakes/NTorHandshake.fs index 73c7e2bb..4b78373f 100644 --- a/NOnion/TorHandshakes/NTorHandshake.fs +++ b/NOnion/TorHandshakes/NTorHandshake.fs @@ -22,7 +22,7 @@ type NTorHandshake = static member Create (identityDigest: IdentityKey) - (NTorOnionKey nTorOnionKey) + (nTorOnionKey: NTorOnionKey) = let privateKey, publicKey = @@ -37,7 +37,8 @@ type NTorHandshake = { IdentityDigest = identityDigest - NTorOnionKey = X25519PublicKeyParameters(nTorOnionKey, 0) + NTorOnionKey = + X25519PublicKeyParameters(nTorOnionKey.ToByteArray(), 0) RandomClientPrivateKey = privateKey RandomClientPublicKey = publicKey } From 871bf87b8eff1e91481e276efe610405c6c4b48e Mon Sep 17 00:00:00 2001 From: webwarrior Date: Thu, 1 Jun 2023 12:59:05 +0200 Subject: [PATCH 06/10] NOnion: make IdentityKey validate length Make IdentityKey type validate key length on creation. Improve error message for wrong key length. --- NOnion.Tests/CircuitHelper.cs | 2 +- NOnion/Constants.fs | 3 +++ NOnion/KeyTypes.fs | 17 ++++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/NOnion.Tests/CircuitHelper.cs b/NOnion.Tests/CircuitHelper.cs index aee632f4..e2cfe38e 100644 --- a/NOnion.Tests/CircuitHelper.cs +++ b/NOnion.Tests/CircuitHelper.cs @@ -25,7 +25,7 @@ static private CircuitNodeDetail ConvertToCircuitNodeDetail(ServerDescriptorEntr var endpoint = IPEndPoint.Parse($"{server.Address.Value}:{server.OnionRouterPort.Value}"); return CircuitNodeDetail.NewCreate(endpoint, new NTorOnionKey(nTorOnionKeyBytes), - IdentityKey.NewIdentityKey(fingerprintBytes)); + new IdentityKey(fingerprintBytes)); } /* It's possible that the router returned by GetRandomFallbackDirectory diff --git a/NOnion/Constants.fs b/NOnion/Constants.fs index 17294f9c..6fdb4d74 100644 --- a/NOnion/Constants.fs +++ b/NOnion/Constants.fs @@ -31,6 +31,9 @@ module Constants = [] let KdfLength = 92 + [] + let IdentityKeyLength = 20 + let internal SupportedProtocolVersion: array = [| 3us |] (* diff --git a/NOnion/KeyTypes.fs b/NOnion/KeyTypes.fs index d35f7cb1..bcdc51b1 100644 --- a/NOnion/KeyTypes.fs +++ b/NOnion/KeyTypes.fs @@ -13,14 +13,21 @@ type ExpandedBlindedPublicKey = type NTorOnionKey(bytes: array) = do if bytes.Length <> Constants.NTorPublicKeyLength then - failwith "Invalid onion key length" + failwithf + "Invalid onion key length, expected %d, got %d" + Constants.NTorPublicKeyLength + bytes.Length member self.ToByteArray() = bytes -type IdentityKey = - | IdentityKey of array +type IdentityKey(bytes: array) = + do + if bytes.Length <> Constants.IdentityKeyLength then + failwithf + "Invalid identity key length, expected %d, got %d" + Constants.IdentityKeyLength + bytes.Length member self.ToByteArray() = - match self with - | IdentityKey bytes -> bytes + bytes From ff994c7f2a5badd822552a59602b3308f50a181f Mon Sep 17 00:00:00 2001 From: webwarrior Date: Thu, 1 Jun 2023 13:11:11 +0200 Subject: [PATCH 07/10] NOnion: renamed ExpandedBlindedPublicKey Renamed ExpandedBlindedPublicKey to BlindedPublicKey. --- NOnion/Crypto/HiddenServicesCipher.fs | 4 ++-- NOnion/Directory/TorDirectory.fs | 2 +- NOnion/KeyTypes.fs | 6 +++--- NOnion/Services/TorServiceHost.fs | 4 ++-- NOnion/Utility/CertificateUtil.fs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/NOnion/Crypto/HiddenServicesCipher.fs b/NOnion/Crypto/HiddenServicesCipher.fs index 94c59607..4585f951 100644 --- a/NOnion/Crypto/HiddenServicesCipher.fs +++ b/NOnion/Crypto/HiddenServicesCipher.fs @@ -88,7 +88,7 @@ module HiddenServicesCipher = Ed25519Clamp blindingFactor match Ed25519.CalculateBlindedPublicKey(publicKey, blindingFactor) with - | true, output -> ExpandedBlindedPublicKey output + | true, output -> BlindedPublicKey output | false, _ -> failwith "can't calculate blinded public key" let CalculateExpandedBlindedPrivateKey @@ -119,7 +119,7 @@ module HiddenServicesCipher = let BuildBlindedPublicKey (periodNumber: uint64, periodLength: uint64) (publicKey: array) - : ExpandedBlindedPublicKey = + : BlindedPublicKey = let blindingFactor = CalculateBlindingFactor periodNumber periodLength publicKey diff --git a/NOnion/Directory/TorDirectory.fs b/NOnion/Directory/TorDirectory.fs index e380356b..1431d42d 100644 --- a/NOnion/Directory/TorDirectory.fs +++ b/NOnion/Directory/TorDirectory.fs @@ -583,7 +583,7 @@ type TorDirectory = |> Async.StartAsTask member self.GetResponsibleHiddenServiceDirectories - (ExpandedBlindedPublicKey blindedPublicKey) + (BlindedPublicKey blindedPublicKey) (sharedRandomValue: string) (periodNumber: uint64) (periodLength: uint64) diff --git a/NOnion/KeyTypes.fs b/NOnion/KeyTypes.fs index bcdc51b1..eb24c6b2 100644 --- a/NOnion/KeyTypes.fs +++ b/NOnion/KeyTypes.fs @@ -3,12 +3,12 @@ type ExpandedBlindedPrivateKey = ExpandedBlindedPrivateKey of array -type ExpandedBlindedPublicKey = - | ExpandedBlindedPublicKey of array +type BlindedPublicKey = + | BlindedPublicKey of array member self.ToByteArray() = match self with - | ExpandedBlindedPublicKey bytes -> bytes + | BlindedPublicKey bytes -> bytes type NTorOnionKey(bytes: array) = do diff --git a/NOnion/Services/TorServiceHost.fs b/NOnion/Services/TorServiceHost.fs index bfcbd55c..c06e3e6c 100644 --- a/NOnion/Services/TorServiceHost.fs +++ b/NOnion/Services/TorServiceHost.fs @@ -531,7 +531,7 @@ type TorServiceHost :?> Ed25519PublicKeyParameters) .GetEncoded()) (descriptorSigningPublicKey.GetEncoded() - |> ExpandedBlindedPublicKey) + |> BlindedPublicKey) (descriptorSigningPrivateKey.GetEncoded () |> ExpandedBlindedPrivateKey) @@ -560,7 +560,7 @@ type TorServiceHost CertType.IntroPointEncKeySignedByDescriptorSigningKey convertedX25519Key (descriptorSigningPublicKey.GetEncoded() - |> ExpandedBlindedPublicKey) + |> BlindedPublicKey) (descriptorSigningPrivateKey.GetEncoded () |> ExpandedBlindedPrivateKey) diff --git a/NOnion/Utility/CertificateUtil.fs b/NOnion/Utility/CertificateUtil.fs index 81da4480..3af3205e 100644 --- a/NOnion/Utility/CertificateUtil.fs +++ b/NOnion/Utility/CertificateUtil.fs @@ -70,7 +70,7 @@ type Certificate = static member CreateNew certType (certifiedKey: array) - (ExpandedBlindedPublicKey signingPublicKey) + (BlindedPublicKey signingPublicKey) (ExpandedBlindedPrivateKey signingPrivateKey) (lifetime: TimeSpan) = From 6963b36ca3e3716e9ff49a5f4401c6fc8789407f Mon Sep 17 00:00:00 2001 From: webwarrior Date: Thu, 1 Jun 2023 14:18:42 +0200 Subject: [PATCH 08/10] NOnion: key types refactoring Renamed ExpandedBlindedPrivateKey to Ed25519PrivateKey, which now can be either NormalEd25519PrivateKey or ExpandedEd25519PrivateKey. Implemented length checks for both. Renamed BlindedPublicKey to ED25519PublicKey. --- NOnion/Constants.fs | 6 ++++ NOnion/Crypto/HiddenServicesCipher.fs | 11 +++--- NOnion/Directory/TorDirectory.fs | 2 +- NOnion/KeyTypes.fs | 49 ++++++++++++++++++++++++--- NOnion/Services/TorServiceHost.fs | 10 +++--- NOnion/Utility/CertificateUtil.fs | 16 ++++----- 6 files changed, 70 insertions(+), 24 deletions(-) diff --git a/NOnion/Constants.fs b/NOnion/Constants.fs index 6fdb4d74..e6afeb53 100644 --- a/NOnion/Constants.fs +++ b/NOnion/Constants.fs @@ -34,6 +34,12 @@ module Constants = [] let IdentityKeyLength = 20 + [] + let Ed25519PrivateKeyLength = 32 + + [] + let ExpandedEd25519PrivateKeyLength = 64 + let internal SupportedProtocolVersion: array = [| 3us |] (* diff --git a/NOnion/Crypto/HiddenServicesCipher.fs b/NOnion/Crypto/HiddenServicesCipher.fs index 4585f951..7d70040c 100644 --- a/NOnion/Crypto/HiddenServicesCipher.fs +++ b/NOnion/Crypto/HiddenServicesCipher.fs @@ -88,14 +88,15 @@ module HiddenServicesCipher = Ed25519Clamp blindingFactor match Ed25519.CalculateBlindedPublicKey(publicKey, blindingFactor) with - | true, output -> BlindedPublicKey output + | true, output -> ED25519PublicKey output | false, _ -> failwith "can't calculate blinded public key" let CalculateExpandedBlindedPrivateKey (blindingFactor: array) (masterPrivateKey: array) - : ExpandedBlindedPrivateKey = - let expandedMasterPrivateKey = Array.zeroCreate 64 + : ExpandedEd25519PrivateKey = + let expandedMasterPrivateKey = + Array.zeroCreate Constants.ExpandedEd25519PrivateKeyLength let hashEngine = Sha512Digest() hashEngine.BlockUpdate(masterPrivateKey, 0, masterPrivateKey.Length) @@ -112,14 +113,14 @@ module HiddenServicesCipher = "Derive temporary signing key hash input" ) with - | true, output -> ExpandedBlindedPrivateKey output + | true, output -> ExpandedEd25519PrivateKey output | false, _ -> failwith "can't calculate blinded private key" let BuildBlindedPublicKey (periodNumber: uint64, periodLength: uint64) (publicKey: array) - : BlindedPublicKey = + : ED25519PublicKey = let blindingFactor = CalculateBlindingFactor periodNumber periodLength publicKey diff --git a/NOnion/Directory/TorDirectory.fs b/NOnion/Directory/TorDirectory.fs index 1431d42d..b61f5aab 100644 --- a/NOnion/Directory/TorDirectory.fs +++ b/NOnion/Directory/TorDirectory.fs @@ -583,7 +583,7 @@ type TorDirectory = |> Async.StartAsTask member self.GetResponsibleHiddenServiceDirectories - (BlindedPublicKey blindedPublicKey) + (ED25519PublicKey blindedPublicKey) (sharedRandomValue: string) (periodNumber: uint64) (periodLength: uint64) diff --git a/NOnion/KeyTypes.fs b/NOnion/KeyTypes.fs index eb24c6b2..e4c00beb 100644 --- a/NOnion/KeyTypes.fs +++ b/NOnion/KeyTypes.fs @@ -1,14 +1,55 @@ namespace NOnion -type ExpandedBlindedPrivateKey = ExpandedBlindedPrivateKey of array +type NormalEd25519PrivateKey(bytes: array) = + do + if bytes.Length <> Constants.Ed25519PrivateKeyLength then + failwithf + "Invalid private key (length=%d), %d expected" + bytes.Length + Constants.Ed25519PrivateKeyLength + + member self.ToByteArray() = + bytes + +type ExpandedEd25519PrivateKey(bytes: array) = + do + if bytes.Length <> Constants.ExpandedEd25519PrivateKeyLength then + failwithf + "Invalid private key (length=%d), %d expected" + bytes.Length + Constants.ExpandedEd25519PrivateKeyLength + + member self.ToByteArray() = + bytes + +type Ed25519PrivateKey = + | NormalEd25519 of NormalEd25519PrivateKey + | ExpandedEd25519 of ExpandedEd25519PrivateKey + + static member FromBytes(bytes: array) : Ed25519PrivateKey = + if bytes.Length = Constants.ExpandedEd25519PrivateKeyLength then + ExpandedEd25519 <| ExpandedEd25519PrivateKey bytes + elif bytes.Length = Constants.Ed25519PrivateKeyLength then + NormalEd25519 <| NormalEd25519PrivateKey bytes + else + failwithf + "Invalid private key (length=%d), private key should either be %d (standard ed25519) or %d bytes (expanded ed25519 key)" + bytes.Length + Constants.Ed25519PrivateKeyLength + Constants.ExpandedEd25519PrivateKeyLength + + member self.ToByteArray() = + match self with + | NormalEd25519 key -> key.ToByteArray() + | ExpandedEd25519 key -> key.ToByteArray() -type BlindedPublicKey = - | BlindedPublicKey of array +type ED25519PublicKey = + | ED25519PublicKey of array member self.ToByteArray() = match self with - | BlindedPublicKey bytes -> bytes + | ED25519PublicKey bytes -> bytes type NTorOnionKey(bytes: array) = do diff --git a/NOnion/Services/TorServiceHost.fs b/NOnion/Services/TorServiceHost.fs index c06e3e6c..e54cb9c8 100644 --- a/NOnion/Services/TorServiceHost.fs +++ b/NOnion/Services/TorServiceHost.fs @@ -531,10 +531,10 @@ type TorServiceHost :?> Ed25519PublicKeyParameters) .GetEncoded()) (descriptorSigningPublicKey.GetEncoded() - |> BlindedPublicKey) + |> ED25519PublicKey) (descriptorSigningPrivateKey.GetEncoded () - |> ExpandedBlindedPrivateKey) + |> Ed25519PrivateKey.FromBytes) Constants.HiddenServices.Descriptor.CertificateLifetime let encKeyBytes = @@ -560,10 +560,10 @@ type TorServiceHost CertType.IntroPointEncKeySignedByDescriptorSigningKey convertedX25519Key (descriptorSigningPublicKey.GetEncoded() - |> BlindedPublicKey) + |> ED25519PublicKey) (descriptorSigningPrivateKey.GetEncoded () - |> ExpandedBlindedPrivateKey) + |> Ed25519PrivateKey.FromBytes) Constants.HiddenServices.Descriptor.CertificateLifetime { @@ -690,7 +690,7 @@ type TorServiceHost CertType.ShortTermDescriptorSigningKeyByBlindedPublicKey (descriptorSigningPublicKey.GetEncoded()) blindedPublicKey - blindedPrivateKey + (ExpandedEd25519 blindedPrivateKey) Constants.HiddenServices.Descriptor.CertificateLifetime HiddenServiceFirstLayerDescriptorDocument.CreateNew diff --git a/NOnion/Utility/CertificateUtil.fs b/NOnion/Utility/CertificateUtil.fs index 3af3205e..00d07a9e 100644 --- a/NOnion/Utility/CertificateUtil.fs +++ b/NOnion/Utility/CertificateUtil.fs @@ -70,8 +70,8 @@ type Certificate = static member CreateNew certType (certifiedKey: array) - (BlindedPublicKey signingPublicKey) - (ExpandedBlindedPrivateKey signingPrivateKey) + (ED25519PublicKey signingPublicKey) + (signingPrivateKey: Ed25519PrivateKey) (lifetime: TimeSpan) = let unsignedCertificate = @@ -101,13 +101,14 @@ type Certificate = let unsignedCertificateBytes = unsignedCertificate.ToBytes true let signature = - if signingPrivateKey.Length = 32 then + match signingPrivateKey with + | NormalEd25519 privateKey -> //Standard private key, we can sign with bouncycastle let signer = Ed25519Signer() signer.Init( true, - Ed25519PrivateKeyParameters(signingPrivateKey, 0) + Ed25519PrivateKeyParameters(privateKey.ToByteArray(), 0) ) signer.BlockUpdate( @@ -117,21 +118,18 @@ type Certificate = ) signer.GenerateSignature() - elif signingPrivateKey.Length = 64 then + | ExpandedEd25519 privateKey -> //Expanded private key, we have to sign with Chaos.NaCl let signature = Array.zeroCreate 64 Ed25519.SignWithPrehashedPrivateKey( ArraySegment signature, ArraySegment unsignedCertificateBytes, - ArraySegment signingPrivateKey, + ArraySegment(privateKey.ToByteArray()), ArraySegment signingPublicKey ) signature - else - failwith - "Invalid private key, private key should either be 32 (standard ed25519) or 64 bytes (expanded ed25519 key)" { unsignedCertificate with Signature = signature From 269e6e1fa36ba948fc47d63ee42ae4f3b66b074b Mon Sep 17 00:00:00 2001 From: webwarrior Date: Thu, 1 Jun 2023 14:52:58 +0200 Subject: [PATCH 09/10] NOnion: use BouncyCastle types Use BouncyCastle types for Ed25519PrivateKey.NormalEd25519 and ED25519PublicKey. --- NOnion/Crypto/HiddenServicesCipher.fs | 2 +- NOnion/Directory/TorDirectory.fs | 2 +- NOnion/KeyTypes.fs | 31 +++++++++++---------------- NOnion/Services/TorServiceHost.fs | 16 ++++++-------- NOnion/Utility/CertificateUtil.fs | 10 ++++----- 5 files changed, 27 insertions(+), 34 deletions(-) diff --git a/NOnion/Crypto/HiddenServicesCipher.fs b/NOnion/Crypto/HiddenServicesCipher.fs index 7d70040c..083468b6 100644 --- a/NOnion/Crypto/HiddenServicesCipher.fs +++ b/NOnion/Crypto/HiddenServicesCipher.fs @@ -88,7 +88,7 @@ module HiddenServicesCipher = Ed25519Clamp blindingFactor match Ed25519.CalculateBlindedPublicKey(publicKey, blindingFactor) with - | true, output -> ED25519PublicKey output + | true, output -> ED25519PublicKey.FromBytes output | false, _ -> failwith "can't calculate blinded public key" let CalculateExpandedBlindedPrivateKey diff --git a/NOnion/Directory/TorDirectory.fs b/NOnion/Directory/TorDirectory.fs index b61f5aab..4286762e 100644 --- a/NOnion/Directory/TorDirectory.fs +++ b/NOnion/Directory/TorDirectory.fs @@ -654,7 +654,7 @@ type TorDirectory = Array.concat [ "store-at-idx" |> Encoding.ASCII.GetBytes - blindedPublicKey + blindedPublicKey.GetEncoded() replicaNum |> uint64 |> IntegerSerialization.FromUInt64ToBigEndianByteArray diff --git a/NOnion/KeyTypes.fs b/NOnion/KeyTypes.fs index e4c00beb..f58f50a3 100644 --- a/NOnion/KeyTypes.fs +++ b/NOnion/KeyTypes.fs @@ -1,16 +1,7 @@ namespace NOnion +open Org.BouncyCastle.Crypto.Parameters -type NormalEd25519PrivateKey(bytes: array) = - do - if bytes.Length <> Constants.Ed25519PrivateKeyLength then - failwithf - "Invalid private key (length=%d), %d expected" - bytes.Length - Constants.Ed25519PrivateKeyLength - - member self.ToByteArray() = - bytes type ExpandedEd25519PrivateKey(bytes: array) = do @@ -23,15 +14,16 @@ type ExpandedEd25519PrivateKey(bytes: array) = member self.ToByteArray() = bytes +[] type Ed25519PrivateKey = - | NormalEd25519 of NormalEd25519PrivateKey - | ExpandedEd25519 of ExpandedEd25519PrivateKey + | Normal of Ed25519PrivateKeyParameters + | Expanded of ExpandedEd25519PrivateKey static member FromBytes(bytes: array) : Ed25519PrivateKey = if bytes.Length = Constants.ExpandedEd25519PrivateKeyLength then - ExpandedEd25519 <| ExpandedEd25519PrivateKey bytes + Expanded <| ExpandedEd25519PrivateKey bytes elif bytes.Length = Constants.Ed25519PrivateKeyLength then - NormalEd25519 <| NormalEd25519PrivateKey bytes + Normal <| Ed25519PrivateKeyParameters(bytes, 0) else failwithf "Invalid private key (length=%d), private key should either be %d (standard ed25519) or %d bytes (expanded ed25519 key)" @@ -41,15 +33,18 @@ type Ed25519PrivateKey = member self.ToByteArray() = match self with - | NormalEd25519 key -> key.ToByteArray() - | ExpandedEd25519 key -> key.ToByteArray() + | Normal key -> key.GetEncoded() + | Expanded key -> key.ToByteArray() type ED25519PublicKey = - | ED25519PublicKey of array + | ED25519PublicKey of Ed25519PublicKeyParameters + + static member FromBytes(bytes: array) : ED25519PublicKey = + ED25519PublicKey <| Ed25519PublicKeyParameters(bytes, 0) member self.ToByteArray() = match self with - | ED25519PublicKey bytes -> bytes + | ED25519PublicKey publicKeyParams -> publicKeyParams.GetEncoded() type NTorOnionKey(bytes: array) = do diff --git a/NOnion/Services/TorServiceHost.fs b/NOnion/Services/TorServiceHost.fs index e54cb9c8..52fba205 100644 --- a/NOnion/Services/TorServiceHost.fs +++ b/NOnion/Services/TorServiceHost.fs @@ -530,11 +530,10 @@ type TorServiceHost ((info.AuthKey.Public :?> Ed25519PublicKeyParameters) .GetEncoded()) - (descriptorSigningPublicKey.GetEncoded() + (descriptorSigningPublicKey |> ED25519PublicKey) - (descriptorSigningPrivateKey.GetEncoded - () - |> Ed25519PrivateKey.FromBytes) + (descriptorSigningPrivateKey + |> Ed25519PrivateKey.Normal) Constants.HiddenServices.Descriptor.CertificateLifetime let encKeyBytes = @@ -559,11 +558,10 @@ type TorServiceHost Certificate.CreateNew CertType.IntroPointEncKeySignedByDescriptorSigningKey convertedX25519Key - (descriptorSigningPublicKey.GetEncoded() + (descriptorSigningPublicKey |> ED25519PublicKey) - (descriptorSigningPrivateKey.GetEncoded - () - |> Ed25519PrivateKey.FromBytes) + (descriptorSigningPrivateKey + |> Ed25519PrivateKey.Normal) Constants.HiddenServices.Descriptor.CertificateLifetime { @@ -690,7 +688,7 @@ type TorServiceHost CertType.ShortTermDescriptorSigningKeyByBlindedPublicKey (descriptorSigningPublicKey.GetEncoded()) blindedPublicKey - (ExpandedEd25519 blindedPrivateKey) + (Ed25519PrivateKey.Expanded blindedPrivateKey) Constants.HiddenServices.Descriptor.CertificateLifetime HiddenServiceFirstLayerDescriptorDocument.CreateNew diff --git a/NOnion/Utility/CertificateUtil.fs b/NOnion/Utility/CertificateUtil.fs index 00d07a9e..901665cd 100644 --- a/NOnion/Utility/CertificateUtil.fs +++ b/NOnion/Utility/CertificateUtil.fs @@ -92,7 +92,7 @@ type Certificate = CertificateExtension.Type = CertificateExtensionType.SignedWithEd25519Key Flags = 0uy - Data = signingPublicKey + Data = signingPublicKey.GetEncoded() } ) Signature = Array.empty @@ -102,13 +102,13 @@ type Certificate = let signature = match signingPrivateKey with - | NormalEd25519 privateKey -> + | Ed25519PrivateKey.Normal privateKey -> //Standard private key, we can sign with bouncycastle let signer = Ed25519Signer() signer.Init( true, - Ed25519PrivateKeyParameters(privateKey.ToByteArray(), 0) + Ed25519PrivateKeyParameters(privateKey.GetEncoded(), 0) ) signer.BlockUpdate( @@ -118,7 +118,7 @@ type Certificate = ) signer.GenerateSignature() - | ExpandedEd25519 privateKey -> + | Ed25519PrivateKey.Expanded privateKey -> //Expanded private key, we have to sign with Chaos.NaCl let signature = Array.zeroCreate 64 @@ -126,7 +126,7 @@ type Certificate = ArraySegment signature, ArraySegment unsignedCertificateBytes, ArraySegment(privateKey.ToByteArray()), - ArraySegment signingPublicKey + ArraySegment(signingPublicKey.GetEncoded()) ) signature From 7e325e97b804d03c66134a55a87eb0111cff8a54 Mon Sep 17 00:00:00 2001 From: webwarrior Date: Mon, 5 Jun 2023 14:20:15 +0200 Subject: [PATCH 10/10] NOnion: minor refactoring Renamed IdentityKey type to Fingerprint as technically this is the digest of identity key. --- NOnion.Tests/CircuitHelper.cs | 2 +- NOnion.Tests/TorDirectoryTests.cs | 2 +- NOnion/Constants.fs | 2 +- NOnion/Directory/TorDirectory.fs | 2 +- NOnion/KeyTypes.fs | 9 +++++---- NOnion/Network/TorCircuit.fs | 2 +- NOnion/Network/TorGuard.fs | 4 ++-- NOnion/Services/TorServiceClient.fs | 2 +- NOnion/Services/TorServiceHost.fs | 4 ++-- NOnion/TorHandshakes/NTorHandshake.fs | 4 ++-- 10 files changed, 17 insertions(+), 16 deletions(-) diff --git a/NOnion.Tests/CircuitHelper.cs b/NOnion.Tests/CircuitHelper.cs index e2cfe38e..251eda3f 100644 --- a/NOnion.Tests/CircuitHelper.cs +++ b/NOnion.Tests/CircuitHelper.cs @@ -25,7 +25,7 @@ static private CircuitNodeDetail ConvertToCircuitNodeDetail(ServerDescriptorEntr var endpoint = IPEndPoint.Parse($"{server.Address.Value}:{server.OnionRouterPort.Value}"); return CircuitNodeDetail.NewCreate(endpoint, new NTorOnionKey(nTorOnionKeyBytes), - new IdentityKey(fingerprintBytes)); + new Fingerprint(fingerprintBytes)); } /* It's possible that the router returned by GetRandomFallbackDirectory diff --git a/NOnion.Tests/TorDirectoryTests.cs b/NOnion.Tests/TorDirectoryTests.cs index c4256059..8be3c1ad 100644 --- a/NOnion.Tests/TorDirectoryTests.cs +++ b/NOnion.Tests/TorDirectoryTests.cs @@ -56,7 +56,7 @@ private async Task ReturnRandomRouter() TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), cachePath); var (endPoint, router) = await directory.GetRouterAsync(RouterType.Normal); Assert.IsTrue(router.IsCreate); - Assert.IsFalse(((CircuitNodeDetail.Create)router).IdentityKey.ToByteArray().All(x => x == 0)); + Assert.IsFalse(((CircuitNodeDetail.Create)router).Fingerprint.ToByteArray().All(x => x == 0)); Assert.IsFalse(((CircuitNodeDetail.Create)router).NTorOnionKey.ToByteArray().All(x => x == 0)); Assert.IsNotNull(((CircuitNodeDetail.Create)router).EndPoint); Assert.That(endPoint, Is.EqualTo(((CircuitNodeDetail.Create)router).EndPoint)); diff --git a/NOnion/Constants.fs b/NOnion/Constants.fs index e6afeb53..88ee484b 100644 --- a/NOnion/Constants.fs +++ b/NOnion/Constants.fs @@ -32,7 +32,7 @@ module Constants = let KdfLength = 92 [] - let IdentityKeyLength = 20 + let FingerprintLength = 20 [] let Ed25519PrivateKeyLength = 32 diff --git a/NOnion/Directory/TorDirectory.fs b/NOnion/Directory/TorDirectory.fs index 4286762e..7edd56dd 100644 --- a/NOnion/Directory/TorDirectory.fs +++ b/NOnion/Directory/TorDirectory.fs @@ -277,7 +277,7 @@ type TorDirectory = CircuitNodeDetail.Create( endpoint, NTorOnionKey nTorOnionKeyBytes, - IdentityKey fingerprintBytes + Fingerprint fingerprintBytes ) } diff --git a/NOnion/KeyTypes.fs b/NOnion/KeyTypes.fs index f58f50a3..32244ba0 100644 --- a/NOnion/KeyTypes.fs +++ b/NOnion/KeyTypes.fs @@ -57,12 +57,13 @@ type NTorOnionKey(bytes: array) = member self.ToByteArray() = bytes -type IdentityKey(bytes: array) = +/// Digest of identity key. +type Fingerprint(bytes: array) = do - if bytes.Length <> Constants.IdentityKeyLength then + if bytes.Length <> Constants.FingerprintLength then failwithf - "Invalid identity key length, expected %d, got %d" - Constants.IdentityKeyLength + "Invalid fingerprint (identity key digest) length, expected %d, got %d" + Constants.FingerprintLength bytes.Length member self.ToByteArray() = diff --git a/NOnion/Network/TorCircuit.fs b/NOnion/Network/TorCircuit.fs index fc50c1ba..6e4672d7 100644 --- a/NOnion/Network/TorCircuit.fs +++ b/NOnion/Network/TorCircuit.fs @@ -26,7 +26,7 @@ type CircuitNodeDetail = | Create of EndPoint: IPEndPoint * NTorOnionKey: NTorOnionKey * - IdentityKey: IdentityKey + Fingerprint: Fingerprint member self.GetIdentityKey() = match self with diff --git a/NOnion/Network/TorGuard.fs b/NOnion/Network/TorGuard.fs index a02b0185..0c215daf 100644 --- a/NOnion/Network/TorGuard.fs +++ b/NOnion/Network/TorGuard.fs @@ -49,7 +49,7 @@ type TorGuard ( client: TcpClient, sslStream: SslStream, - fingerprintOpt: Option + fingerprintOpt: Option ) = let shutdownToken = new CancellationTokenSource() @@ -116,7 +116,7 @@ type TorGuard static member private InnerNewClient (ipEndpoint: IPEndPoint) - (fingerprintOpt: Option) + (fingerprintOpt: Option) = async { let tcpClient = new TcpClient() diff --git a/NOnion/Services/TorServiceClient.fs b/NOnion/Services/TorServiceClient.fs index ee94300c..ef626bcf 100644 --- a/NOnion/Services/TorServiceClient.fs +++ b/NOnion/Services/TorServiceClient.fs @@ -373,7 +373,7 @@ type TorServiceClient = CircuitNodeDetail.Create( endpointSpecifier, introductionPoint.OnionKey.Value, - IdentityKey identityKey + Fingerprint identityKey ) return diff --git a/NOnion/Services/TorServiceHost.fs b/NOnion/Services/TorServiceHost.fs index 52fba205..c66b9085 100644 --- a/NOnion/Services/TorServiceHost.fs +++ b/NOnion/Services/TorServiceHost.fs @@ -31,7 +31,7 @@ type IntroductionPointInfo = AuthKey: AsymmetricCipherKeyPair MasterPublicKey: Ed25519PublicKeyParameters NTorOnionKey: NTorOnionKey - Fingerprint: IdentityKey + Fingerprint: Fingerprint } type TorServiceHost @@ -234,7 +234,7 @@ type TorServiceHost |> Seq.tryExactlyOne match linkSpecifierOpt with - | Some linkSpecifier -> IdentityKey linkSpecifier.Data + | Some linkSpecifier -> Fingerprint linkSpecifier.Data | None -> failwith "No rendezvous fingerprint found!" let connectToRendezvousJob = diff --git a/NOnion/TorHandshakes/NTorHandshake.fs b/NOnion/TorHandshakes/NTorHandshake.fs index 4b78373f..501fd322 100644 --- a/NOnion/TorHandshakes/NTorHandshake.fs +++ b/NOnion/TorHandshakes/NTorHandshake.fs @@ -15,13 +15,13 @@ type NTorHandshake = { RandomClientPrivateKey: X25519PrivateKeyParameters RandomClientPublicKey: X25519PublicKeyParameters - IdentityDigest: IdentityKey + IdentityDigest: Fingerprint NTorOnionKey: X25519PublicKeyParameters } static member Create - (identityDigest: IdentityKey) + (identityDigest: Fingerprint) (nTorOnionKey: NTorOnionKey) =