Пример #1
0
        /// <summary>
        ///   Creates a new instance of the <see cref="Secio1Stream"/> class.
        /// </summary>
        /// <param name="stream">
        ///   The source/destination of SECIO packets.
        /// </param>
        /// <param name="cipherName">
        ///   The cipher for the <paramref name="stream"/>, such as AES-256 or AES-128.
        /// </param>
        /// <param name="hashName">
        ///   The hash for the <paramref name="stream"/>, such as SHA256.
        /// </param>
        /// <param name="localKey">
        ///   The keys used by the local endpoint.
        /// </param>
        /// <param name="remoteKey">
        ///   The keys used by the remote endpoint.
        /// </param>
        public Secio1Stream(
            Stream stream,
            string cipherName, string hashName,
            StretchedKey localKey, StretchedKey remoteKey)
        {
            this.stream = stream;

            inHmac = new HMac(DigestUtilities.GetDigest(hashName));
            inHmac.Init(new KeyParameter(localKey.MacKey));

            outHmac = new HMac(DigestUtilities.GetDigest(hashName));
            outHmac.Init(new KeyParameter(remoteKey.MacKey));

            if (cipherName == "AES-256" || cipherName == "AES-512")
            {
                decrypt = new CtrStreamCipher(new AesEngine());
                var p = new ParametersWithIV(new KeyParameter(remoteKey.CipherKey), remoteKey.IV);
                decrypt.Init(false, p);

                encrypt = new CtrStreamCipher(new AesEngine());
                p       = new ParametersWithIV(new KeyParameter(localKey.CipherKey), localKey.IV);
                encrypt.Init(true, p);
            }
            else
            {
                throw new NotSupportedException($"Cipher '{cipherName}' is not supported.");
            }
        }
Пример #2
0
        public void StretchedSecret()
        {
            var cipher = "AES-256";
            var hash   = "SHA256";
            var secret = new byte[]
            {
                195, 191, 209, 165, 209, 201, 127, 122, 136, 111, 31, 66, 111, 68, 38, 155, 216, 204, 46, 181, 200, 188,
                170, 204, 104, 74, 239, 251, 173, 114, 222, 234
            };

            StretchedKey.Generate(cipher, hash, secret, out var k1, out var k2);

            Assert.IsNotNull(k1);
            CollectionAssert.AreEqual(
                new byte[] { 208, 132, 203, 169, 253, 52, 40, 83, 161, 91, 17, 71, 33, 136, 67, 96 }, k1.Iv);
            CollectionAssert.AreEqual(
                new byte[]
            {
                156, 48, 241, 157, 92, 248, 153, 186, 114, 127, 195, 114, 106, 104, 215, 133, 35, 11, 131, 137, 123,
                70, 74, 26, 15, 60, 189, 32, 67, 221, 115, 137
            }, k1.CipherKey);
            CollectionAssert.AreEqual(
                new byte[] { 6, 179, 91, 245, 224, 56, 153, 120, 77, 140, 29, 5, 15, 213, 187, 65, 137, 230, 202, 120 },
                k1.MacKey);

            Assert.IsNotNull(k2);
            CollectionAssert.AreEqual(
                new byte[] { 236, 17, 34, 141, 90, 106, 197, 56, 197, 184, 157, 135, 91, 88, 112, 19 }, k2.Iv);
            CollectionAssert.AreEqual(
                new byte[]
            {
                151, 145, 195, 219, 76, 195, 102, 109, 187, 231, 100, 150, 132, 245, 251, 130, 254, 37, 178, 55,
                227, 34, 114, 39, 238, 34, 2, 193, 107, 130, 32, 87
            }, k2.CipherKey);
            CollectionAssert.AreEqual(
                new byte[] { 3, 229, 77, 212, 241, 217, 23, 113, 220, 126, 38, 255, 18, 117, 108, 205, 198, 89, 1, 236 },
                k2.MacKey);
        }
Пример #3
0
#pragma warning disable VSTHRD103
        /// <inheritdoc />
        public async Task <Stream> EncryptAsync(PeerConnection connection, CancellationToken cancel = default(CancellationToken))
        {
            var stream    = connection.Stream;
            var localPeer = connection.LocalPeer;

            connection.RemotePeer = connection.RemotePeer ?? new Peer();
            var remotePeer = connection.RemotePeer;

            // =============================================================================
            // step 1. Propose -- propose cipher suite + send pubkey + nonce
            var rng        = new SecureRandom();
            var localNonce = new byte[16];

            rng.NextBytes(localNonce);
            var localProposal = new Secio1Propose
            {
                Nonce     = localNonce,
                Exchanges = "P-256,P-384,P-521",
                Ciphers   = "AES-256,AES-128",
                Hashes    = "SHA256,SHA512",
                PublicKey = Convert.FromBase64String(localPeer.PublicKey)
            };

            ProtoBuf.Serializer.SerializeWithLengthPrefix(stream, localProposal, PrefixStyle.Fixed32BigEndian);
            await stream.FlushAsync().ConfigureAwait(false);

            // =============================================================================
            // step 1.1 Identify -- get identity from their key
            var remoteProposal = ProtoBuf.Serializer.DeserializeWithLengthPrefix <Secio1Propose>(stream, PrefixStyle.Fixed32BigEndian);
            var ridAlg         = (remoteProposal.PublicKey.Length <= 48) ? "identity" : "sha2-256";
            var remoteId       = MultiHash.ComputeHash(remoteProposal.PublicKey, ridAlg);

            if (remotePeer.Id == null)
            {
                remotePeer.Id = remoteId;
            }
            else if (remoteId != remotePeer.Id)
            {
                throw new Exception($"Expected peer '{remotePeer.Id}', got '{remoteId}'");
            }

            // =============================================================================
            // step 1.2 Selection -- select/agree on best encryption parameters
            // to determine order, use cmp(H(remote_pubkey||local_rand), H(local_pubkey||remote_rand)).
            //   oh1 := hashSha256(append(proposeIn.GetPubkey(), nonceOut...))
            //   oh2 := hashSha256(append(myPubKeyBytes, proposeIn.GetRand()...))
            //   order := bytes.Compare(oh1, oh2)
            byte[] oh1;
            byte[] oh2;
            using (var hasher = MultiHash.GetHashAlgorithm("sha2-256"))
                using (var ms = new MemoryStream())
                {
                    ms.Write(remoteProposal.PublicKey, 0, remoteProposal.PublicKey.Length);
                    ms.Write(localProposal.Nonce, 0, localProposal.Nonce.Length);
                    ms.Position = 0;
                    oh1         = hasher.ComputeHash(ms);
                }
            using (var hasher = MultiHash.GetHashAlgorithm("sha2-256"))
                using (var ms = new MemoryStream())
                {
                    ms.Write(localProposal.PublicKey, 0, localProposal.PublicKey.Length);
                    ms.Write(remoteProposal.Nonce, 0, remoteProposal.Nonce.Length);
                    ms.Position = 0;
                    oh2         = hasher.ComputeHash(ms);
                }
            int order = 0;

            for (int i = 0; order == 0 && i < oh1.Length; ++i)
            {
                order = oh1[i].CompareTo(oh2[i]);
            }
            if (order == 0)
            {
                throw new Exception("Same keys and nonces; talking to self");
            }
            var curveName = SelectBest(order, localProposal.Exchanges, remoteProposal.Exchanges);

            if (curveName == null)
            {
                throw new Exception("Cannot agree on a key exchange.");
            }

            var cipherName = SelectBest(order, localProposal.Ciphers, remoteProposal.Ciphers);

            if (cipherName == null)
            {
                throw new Exception("Cannot agree on a chipher.");
            }

            var hashName = SelectBest(order, localProposal.Hashes, remoteProposal.Hashes);

            if (hashName == null)
            {
                throw new Exception("Cannot agree on a hash.");
            }

            // =============================================================================
            // step 2. Exchange -- exchange (signed) ephemeral keys. verify signatures.

            // Generate EphemeralPubKey
            var localEphemeralKey       = EphermalKey.Generate(curveName);
            var localEphemeralPublicKey = localEphemeralKey.PublicKeyBytes();

            // Send Exchange packet
            var localExchange = new Secio1Exchange();

            using (var ms = new MemoryStream())
            {
                ProtoBuf.Serializer.Serialize(ms, localProposal);
                ProtoBuf.Serializer.Serialize(ms, remoteProposal);
                ms.Write(localEphemeralPublicKey, 0, localEphemeralPublicKey.Length);
                localExchange.Signature = connection.LocalPeerKey.Sign(ms.ToArray());
            }
            localExchange.EPublicKey = localEphemeralPublicKey;
            ProtoBuf.Serializer.SerializeWithLengthPrefix(stream, localExchange, PrefixStyle.Fixed32BigEndian);
            await stream.FlushAsync(cancel).ConfigureAwait(false);

            // Receive their Exchange packet.  If nothing, then most likely the
            // remote has closed the connection because it does not like us.
            var remoteExchange = ProtoBuf.Serializer.DeserializeWithLengthPrefix <Secio1Exchange>(stream, PrefixStyle.Fixed32BigEndian);

            if (remoteExchange == null)
            {
                throw new Exception("Remote refuses the SECIO exchange.");
            }

            // =============================================================================
            // step 2.1. Verify -- verify their exchange packet is good.
            var remotePeerKey = Key.CreatePublicKeyFromIpfs(remoteProposal.PublicKey);

            using (var ms = new MemoryStream())
            {
                ProtoBuf.Serializer.Serialize(ms, remoteProposal);
                ProtoBuf.Serializer.Serialize(ms, localProposal);
                ms.Write(remoteExchange.EPublicKey, 0, remoteExchange.EPublicKey.Length);
                remotePeerKey.Verify(ms.ToArray(), remoteExchange.Signature);
            }
            var remoteEphemeralKey = EphermalKey.CreatePublicKeyFromIpfs(curveName, remoteExchange.EPublicKey);

            // =============================================================================
            // step 2.2. Keys -- generate keys for mac + encryption
            var sharedSecret = localEphemeralKey.GenerateSharedSecret(remoteEphemeralKey);

            StretchedKey.Generate(cipherName, hashName, sharedSecret, out StretchedKey k1, out StretchedKey k2);
            if (order < 0)
            {
                StretchedKey tmp = k1;
                k1 = k2;
                k2 = tmp;
            }

            // =============================================================================
            // step 2.3. MAC + Cipher -- prepare MAC + cipher
            var secureStream = new Secio1Stream(stream, cipherName, hashName, k1, k2);

            // =============================================================================
            // step 3. Finish -- send expected message to verify encryption works (send local nonce)

            // Send thier nonce,
            await secureStream.WriteAsync(remoteProposal.Nonce, 0, remoteProposal.Nonce.Length, cancel).ConfigureAwait(false);

            await secureStream.FlushAsync(cancel).ConfigureAwait(false);

            // Receive our nonce.
            var verification = new byte[localNonce.Length];
            await secureStream.ReadExactAsync(verification, 0, verification.Length, cancel);

            if (!localNonce.SequenceEqual(verification))
            {
                throw new Exception($"SECIO verification message failure.");
            }

            log.Debug($"Secure session with {remotePeer}");

            // Fill in the remote peer
            remotePeer.PublicKey = Convert.ToBase64String(remoteProposal.PublicKey);

            // Set secure task done
            connection.Stream = secureStream;
            connection.SecurityEstablished.SetResult(true);
            return(secureStream);
        }