Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce separate types for different keys #72

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
4 changes: 3 additions & 1 deletion NOnion.Tests/CircuitHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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, nTorOnionKeyBytes, fingerprintBytes);
return CircuitNodeDetail.NewCreate(endpoint,
new NTorOnionKey(nTorOnionKeyBytes),
new Fingerprint(fingerprintBytes));
}

/* It's possible that the router returned by GetRandomFallbackDirectory
Expand Down
2 changes: 1 addition & 1 deletion NOnion.Tests/KeyBlindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
4 changes: 2 additions & 2 deletions NOnion.Tests/TorDirectoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ 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).NTorOnionKey.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));
}
Expand Down
11 changes: 7 additions & 4 deletions NOnion/Cells/Relay/RelayIntroduce.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type RelayIntroduceInnerData =
{
RendezvousCookie: array<byte>
Extensions: List<RelayIntroExtension>
OnionKey: array<byte>
OnionKey: NTorOnionKey
RendezvousLinkSpecifiers: List<LinkSpecifier>
}

Expand Down Expand Up @@ -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<LinkSpecifier>) =
if n = 0uy then
Expand All @@ -62,6 +63,8 @@ type RelayIntroduceInnerData =
}

member self.ToBytes() =
let onionKeyBytes = self.OnionKey.ToByteArray()

Array.concat
[
self.RendezvousCookie
Expand All @@ -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())
Expand Down
9 changes: 9 additions & 0 deletions NOnion/Constants.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ module Constants =
[<Literal>]
let KdfLength = 92

[<Literal>]
let FingerprintLength = 20

[<Literal>]
let Ed25519PrivateKeyLength = 32

[<Literal>]
let ExpandedEd25519PrivateKeyLength = 64

let internal SupportedProtocolVersion: array<uint16> = [| 3us |]

(*
Expand Down
13 changes: 7 additions & 6 deletions NOnion/Crypto/HiddenServicesCipher.fs
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,15 @@ module HiddenServicesCipher =
Ed25519Clamp blindingFactor

match Ed25519.CalculateBlindedPublicKey(publicKey, blindingFactor) with
| true, output -> output
| true, output -> ED25519PublicKey.FromBytes output
| false, _ -> failwith "can't calculate blinded public key"

let CalculateExpandedBlindedPrivateKey
(blindingFactor: array<byte>)
(masterPrivateKey: array<byte>)
=
let expandedMasterPrivateKey = Array.zeroCreate 64
: ExpandedEd25519PrivateKey =
let expandedMasterPrivateKey =
Array.zeroCreate Constants.ExpandedEd25519PrivateKeyLength

let hashEngine = Sha512Digest()
hashEngine.BlockUpdate(masterPrivateKey, 0, masterPrivateKey.Length)
Expand All @@ -112,14 +113,14 @@ module HiddenServicesCipher =
"Derive temporary signing key hash input"
)
with
| true, output -> output
| true, output -> ExpandedEd25519PrivateKey output
| false, _ -> failwith "can't calculate blinded private key"


let BuildBlindedPublicKey
(periodNumber: uint64, periodLength: uint64)
(publicKey: array<byte>)
=
: ED25519PublicKey =
let blindingFactor =
CalculateBlindingFactor periodNumber periodLength publicKey

Expand Down Expand Up @@ -154,7 +155,7 @@ module HiddenServicesCipher =
[
"subcredential" |> Encoding.ASCII.GetBytes
credential
blindedKey
blindedKey.ToByteArray()
]
|> SHA3256

Expand Down
13 changes: 9 additions & 4 deletions NOnion/Directory/HiddenServiceDescriptorDocument.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ open NOnion.Utility

type IntroductionPointEntry =
{
OnionKey: Option<array<byte>>
OnionKey: Option<NTorOnionKey>
AuthKey: Option<Certificate>
EncKey: Option<array<byte>>
EncKeyCert: Option<Certificate>
Expand Down Expand Up @@ -60,7 +60,10 @@ type IntroductionPointEntry =
innerParse
{ state with
OnionKey =
readWord() |> Convert.FromBase64String |> Some
readWord()
|> Convert.FromBase64String
|> NTorOnionKey
|> Some
}
| "enc-key" ->
lines.Dequeue() |> ignore<string>
Expand Down Expand Up @@ -127,9 +130,11 @@ type IntroductionPointEntry =
"HS document (most inner wrapper) is incomplete, missing linkspecifier"

match self.OnionKey with
| Some onionKey ->
| Some nTorOnionKey ->
appendLine(
sprintf "onion-key ntor %s" (Convert.ToBase64String onionKey)
sprintf
"onion-key ntor %s"
(Convert.ToBase64String <| nTorOnionKey.ToByteArray())
)
|> ignore<StringBuilder>
| None ->
Expand Down
8 changes: 4 additions & 4 deletions NOnion/Directory/TorDirectory.fs
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,8 @@ type TorDirectory =
endpoint,
CircuitNodeDetail.Create(
endpoint,
nTorOnionKeyBytes,
fingerprintBytes
NTorOnionKey nTorOnionKeyBytes,
Fingerprint fingerprintBytes
)
}

Expand Down Expand Up @@ -583,7 +583,7 @@ type TorDirectory =
|> Async.StartAsTask

member self.GetResponsibleHiddenServiceDirectories
(blindedPublicKey: array<byte>)
(ED25519PublicKey blindedPublicKey)
(sharedRandomValue: string)
(periodNumber: uint64)
(periodLength: uint64)
Expand Down Expand Up @@ -654,7 +654,7 @@ type TorDirectory =
Array.concat
[
"store-at-idx" |> Encoding.ASCII.GetBytes
blindedPublicKey
blindedPublicKey.GetEncoded()
replicaNum
|> uint64
|> IntegerSerialization.FromUInt64ToBigEndianByteArray
Expand Down
70 changes: 70 additions & 0 deletions NOnion/KeyTypes.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
namespace NOnion

open Org.BouncyCastle.Crypto.Parameters


type ExpandedEd25519PrivateKey(bytes: array<byte>) =
do
if bytes.Length <> Constants.ExpandedEd25519PrivateKeyLength then
failwithf
"Invalid private key (length=%d), %d expected"
bytes.Length
Constants.ExpandedEd25519PrivateKeyLength

member self.ToByteArray() =
bytes

[<RequireQualifiedAccess>]
type Ed25519PrivateKey =
| Normal of Ed25519PrivateKeyParameters
| Expanded of ExpandedEd25519PrivateKey

static member FromBytes(bytes: array<byte>) : Ed25519PrivateKey =
if bytes.Length = Constants.ExpandedEd25519PrivateKeyLength then
Expanded <| ExpandedEd25519PrivateKey bytes
elif bytes.Length = Constants.Ed25519PrivateKeyLength then
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)"
bytes.Length
Constants.Ed25519PrivateKeyLength
Constants.ExpandedEd25519PrivateKeyLength

member self.ToByteArray() =
match self with
| Normal key -> key.GetEncoded()
| Expanded key -> key.ToByteArray()

type ED25519PublicKey =
| ED25519PublicKey of Ed25519PublicKeyParameters

static member FromBytes(bytes: array<byte>) : ED25519PublicKey =
ED25519PublicKey <| Ed25519PublicKeyParameters(bytes, 0)

member self.ToByteArray() =
match self with
| ED25519PublicKey publicKeyParams -> publicKeyParams.GetEncoded()

type NTorOnionKey(bytes: array<byte>) =
do
if bytes.Length <> Constants.NTorPublicKeyLength then
failwithf
"Invalid onion key length, expected %d, got %d"
Constants.NTorPublicKeyLength
bytes.Length

member self.ToByteArray() =
bytes

/// Digest of identity key.
type Fingerprint(bytes: array<byte>) =
do
if bytes.Length <> Constants.FingerprintLength then
failwithf
"Invalid fingerprint (identity key digest) length, expected %d, got %d"
Constants.FingerprintLength
bytes.Length

member self.ToByteArray() =
bytes
1 change: 1 addition & 0 deletions NOnion/NOnion.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<Compile Include="RelayIntroduceStatus.fs" />
<Compile Include="Exceptions.fs" />
<Compile Include="HandshakeType.fs" />
<Compile Include="KeyTypes.fs" />
<Compile Include="Utility\EmbeddedResourceUtility.fs" />
<Compile Include="Utility\PemUtility.fs" />
<Compile Include="Utility\FSharpUtil.fs" />
Expand Down
6 changes: 3 additions & 3 deletions NOnion/Network/TorCircuit.fs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ type CircuitNodeDetail =
| FastCreate
| Create of
EndPoint: IPEndPoint *
NTorOnionKey: array<byte> *
IdentityKey: array<byte>
NTorOnionKey: NTorOnionKey *
Fingerprint: Fingerprint

member self.GetIdentityKey() =
match self with
Expand Down Expand Up @@ -694,7 +694,7 @@ and TorCircuit
{
LinkSpecifier.Type =
LinkSpecifierType.LegacyIdentity
Data = identityKey
Data = identityKey.ToByteArray()
}
]
HandshakeType = HandshakeType.NTor
Expand Down
6 changes: 3 additions & 3 deletions NOnion/Network/TorGuard.fs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type TorGuard
(
client: TcpClient,
sslStream: SslStream,
fingerprintOpt: Option<array<byte>>
fingerprintOpt: Option<Fingerprint>
) =
let shutdownToken = new CancellationTokenSource()

Expand Down Expand Up @@ -116,7 +116,7 @@ type TorGuard

static member private InnerNewClient
(ipEndpoint: IPEndPoint)
(fingerprintOpt: Option<array<byte>>)
(fingerprintOpt: Option<Fingerprint>)
=
async {
let tcpClient = new TcpClient()
Expand Down Expand Up @@ -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 -> ()
Expand Down
11 changes: 6 additions & 5 deletions NOnion/Services/TorServiceClient.fs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ type TorServiceClient =
(sprintf
"/tor/hs/%i/%s"
Constants.HiddenServices.Version
((Convert.ToBase64String
blindedPublicKey)))
(Convert.ToBase64String
<| blindedPublicKey.ToByteArray
()))
false

return
Expand Down Expand Up @@ -188,7 +189,7 @@ type TorServiceClient =
let secretInput =
Array.concat
[
blindedPublicKey
blindedPublicKey.ToByteArray()
HiddenServicesCipher.GetSubCredential
(periodNum, periodLength)
publicKey
Expand Down Expand Up @@ -372,7 +373,7 @@ type TorServiceClient =
CircuitNodeDetail.Create(
endpointSpecifier,
introductionPoint.OnionKey.Value,
identityKey
Fingerprint identityKey
)

return
Expand Down Expand Up @@ -433,7 +434,7 @@ type TorServiceClient =
{
LinkSpecifier.Type =
LinkSpecifierType.LegacyIdentity
Data = identityKey
Data = identityKey.ToByteArray()
}
]
}
Expand Down
Loading