示例#1
0
        /// <summary>
        /// Recovers the ephemeral key used to sign the transformed nonce in the authentication data.
        /// Throws an exception if the recovered key is invalid, or could not be recovered.
        /// </summary>
        /// <param name="receiverPrivateKey">The private key of the receiver used to generate the shared secret.</param>
        /// <returns>Returns the remote ephemeral public key for the keypair which signed this authentication data.</returns>
        public virtual (EthereumEcdsa remoteEphemeralPublicKey, uint?chainId) RecoverDataFromSignature(EthereumEcdsa receiverPrivateKey)
        {
            // Create an EC provider with the given public key.
            EthereumEcdsa publicKey = EthereumEcdsa.Create(PublicKey, EthereumEcdsaKeyType.Public);

            // Generate the shared secret using ECDH between our local private key and this remote public key
            byte[] ecdhKey = receiverPrivateKey.ComputeECDHKey(publicKey);

            // Obtain our transformed nonce data.
            byte[] transformedNonceData = GetTransformedNonce(ecdhKey);

            // We want our signature in r,s,v format.
            BigInteger ecdsa_r = BigIntegerConverter.GetBigInteger(R, false, 32);
            BigInteger ecdsa_s = BigIntegerConverter.GetBigInteger(S, false, 32);

            (byte recoveryId, uint?chainId) = EthereumEcdsa.GetRecoveryAndChainIDFromV(V);

            // Recover the public key from the data provided.
            EthereumEcdsa remoteEphemeralPublickey = EthereumEcdsa.Recover(transformedNonceData, recoveryId, ecdsa_r, ecdsa_s);

            // Verify the key is valid

            // Return the ephemeral key
            return(remoteEphemeralPublickey, chainId);
        }
示例#2
0
        public virtual void Sign(EthereumEcdsa localPrivateKey, EthereumEcdsa ephemeralPrivateKey, EthereumEcdsa receiverPublicKey, uint?chainID = null)
        {
            // Generate the shared secret using ECDH between our local private key and this remote public key
            byte[] ecdhKey = localPrivateKey.ComputeECDHKey(receiverPublicKey);

            // If our nonce is null, generate a new one
            Nonce = Nonce ?? RLPxSession.GenerateNonce();

            // Verify the nonce is the correct length.
            if (Nonce.Length != RLPxSession.NONCE_SIZE)
            {
                // Throw an exception if an invalid nonce was provided.
                throw new ArgumentException($"Invalid size nonce provided for RLPx session when signing auth message. Should be {RLPxSession.NONCE_SIZE} bytes but was {Nonce?.Length}.");
            }

            // Obtain our transformed nonce data.
            byte[] transformedNonceData = GetTransformedNonce(ecdhKey);

            // Sign the transformed data.
            var signature = ephemeralPrivateKey.SignData(transformedNonceData);

            // We want our signature in r,s,v format.
            R = BigIntegerConverter.GetBytes(signature.r, 32);
            S = BigIntegerConverter.GetBytes(signature.s, 32);
            V = EthereumEcdsa.GetVFromRecoveryID(chainID, signature.RecoveryID);

            // Set our local public key and the public key hash.
            PublicKey = localPrivateKey.ToPublicKeyArray(false, true);
        }
示例#3
0
        public static byte[] Encrypt(EthereumEcdsa remotePublicKey, byte[] data, byte[] sharedMacData = null)
        {
            // If we have no shared mac data, we set it as a blank array
            sharedMacData = sharedMacData ?? Array.Empty <byte>();

            // Generate a random private key
            EthereumEcdsa senderPrivateKey = EthereumEcdsa.Generate(new SystemRandomAccountDerivation());

            // Generate the elliptic curve diffie hellman ("ECDH") shared key
            byte[] ecdhKey = senderPrivateKey.ComputeECDHKey(remotePublicKey);

            // Perform NIST SP 800-56 Concatenation Key Derivation Function ("KDF")
            Memory <byte> keyData = DeriveKeyKDF(ecdhKey, 32);

            // Split the AES encryption key and MAC from the derived key data.
            var aesKey = keyData.Slice(0, 16).ToArray();

            byte[] hmacSha256Key = keyData.Slice(16, 16).ToArray();
            hmacSha256Key = _sha256.ComputeHash(hmacSha256Key);

            // We generate a counter for our aes-128-ctr operation.
            byte[] counter = new byte[AesCtr.BLOCK_SIZE];
            _randomNumberGenerator.GetBytes(counter);

            // Encrypt the data accordingly.
            byte[] encryptedData = AesCtr.Encrypt(aesKey, data, counter);

            // Obtain the sender's public key to compile our message.
            byte[] localPublicKey = senderPrivateKey.ToPublicKeyArray(false, true);

            // We'll want to put this data into the message in the following order (where || is concatenation):
            // ECIES_HEADER_BYTE (1 byte) || sender's public key (64 bytes) || counter (16 bytes) || encrypted data (arbitrary length) || tag (32 bytes)
            // This gives us a total size of 113 + data.Length
            byte[] result = new byte[ECIES_ADDITIONAL_OVERHEAD + encryptedData.Length];

            // Define a pointer and copy in our data as suggested.
            int offset = 0;

            result[offset++] = ECIES_HEADER_BYTE;
            Array.Copy(localPublicKey, 0, result, offset, localPublicKey.Length);
            offset += localPublicKey.Length;
            Array.Copy(counter, 0, result, offset, counter.Length);
            offset += counter.Length;
            Array.Copy(encryptedData, 0, result, offset, encryptedData.Length);
            offset += encryptedData.Length;

            // We still have to copy the tag, which is a HMACSHA256 of our counter + encrypted data + shared mac.

            // We copy the data into a buffer for this hash computation since counter + encrypted data are already aligned.
            byte[] tagPreimage = new byte[counter.Length + encryptedData.Length + sharedMacData.Length];
            Array.Copy(result, 65, tagPreimage, 0, counter.Length + encryptedData.Length);
            Array.Copy(sharedMacData, 0, tagPreimage, counter.Length + encryptedData.Length, sharedMacData.Length);

            // Obtain a HMACSHA256 provider
            HMACSHA256 hmacSha256 = new HMACSHA256(hmacSha256Key);

            // Compute a hash of our counter + encrypted data + shared mac data.
            byte[] tag = hmacSha256.ComputeHash(tagPreimage);

            // Copy the tag into our result buffer.
            Array.Copy(tag, 0, result, offset, tag.Length);
            offset += tag.Length;

            // Return the resulting data.
            return(result);
        }
示例#4
0
        public static byte[] Decrypt(EthereumEcdsa localPrivateKey, Memory <byte> message, Memory <byte> sharedMacData)
        {
            // Verify our provided key type
            if (localPrivateKey.KeyType != EthereumEcdsaKeyType.Private)
            {
                throw new ArgumentException("ECIES could not decrypt data because the provided key was not a private key.");
            }

            // Verify the size of our message (layout specified in Encrypt() describes this value)
            if (message.Length <= ECIES_ADDITIONAL_OVERHEAD)
            {
                throw new ArgumentException("ECIES could not decrypt data because the provided data did not contain enough information.");
            }

            // Verify the first byte of our data
            int offset = 0;

            if (message.Span[offset++] != ECIES_HEADER_BYTE)
            {
                throw new ArgumentException("ECIES could not decrypt data because the provided data had an invalid header.");
            }

            // Extract the sender's public key from the data.
            Memory <byte> remotePublicKeyData = message.Slice(offset, 64);
            EthereumEcdsa remotePublicKey     = EthereumEcdsa.Create(remotePublicKeyData, EthereumEcdsaKeyType.Public);

            offset += remotePublicKeyData.Length;
            Memory <byte> counter = message.Slice(offset, AesCtr.BLOCK_SIZE);

            offset += counter.Length;
            Memory <byte> encryptedData = message.Slice(offset, message.Length - offset - 32);

            offset += encryptedData.Length;
            byte[] tag = message.Slice(offset, message.Length - offset).ToArray();
            offset += tag.Length;

            // Generate the elliptic curve diffie hellman ("ECDH") shared key
            byte[] ecdhKey = localPrivateKey.ComputeECDHKey(remotePublicKey);

            // Perform NIST SP 800-56 Concatenation Key Derivation Function ("KDF")
            Memory <byte> keyData = DeriveKeyKDF(ecdhKey, 32);

            // Split the AES encryption key and MAC from the derived key data.
            var aesKey = keyData.Slice(0, 16).ToArray();

            byte[] hmacSha256Key = keyData.Slice(16, 16).ToArray();
            hmacSha256Key = _sha256.ComputeHash(hmacSha256Key);

            // Next we'll want to verify our tag (HMACSHA256 hash of counter + encrypted data + shared mac data).

            // We copy the data into a buffer for this hash computation since counter + encrypted data are already aligned.
            byte[]        tagPreimage       = new byte[counter.Length + encryptedData.Length + sharedMacData.Length];
            Memory <byte> tagPreimageMemory = tagPreimage;

            counter.CopyTo(tagPreimageMemory);
            encryptedData.CopyTo(tagPreimageMemory.Slice(counter.Length, encryptedData.Length));
            sharedMacData.CopyTo(tagPreimageMemory.Slice(counter.Length + encryptedData.Length));

            // Obtain a HMACSHA256 provider
            HMACSHA256 hmacSha256 = new HMACSHA256(hmacSha256Key);

            // Compute a hash of our counter + encrypted data + shared mac data.
            byte[] validTag = hmacSha256.ComputeHash(tagPreimage);

            // Verify our tag is valid
            if (!tag.SequenceEqual(validTag))
            {
                throw new ArgumentException("ECIES could not decrypt data because the hash/tag on the counter/encrypted data/shared mac data was invalid.");
            }

            // Decrypt the data accordingly.
            byte[] decryptedData = AesCtr.Decrypt(aesKey, encryptedData.ToArray(), counter.ToArray());

            // Return the decrypted data.
            return(decryptedData);
        }