public void Lesson1() { Key key = new Key(); //new private key PubKey pubKey = key.PubKey; Console.WriteLine("Public Key: {0}", pubKey); KeyId hash = pubKey.Hash; Console.WriteLine("Hashed Public Key: {0}", hash); BitcoinAddress address = pubKey.GetAddress(Network.Main); Console.WriteLine("Address: {0}", address); Script scriptPubKeyFromAddress = address.ScriptPubKey; Console.WriteLine("ScriptPubKey from address: {0}", scriptPubKeyFromAddress); Script scriptPubKeyFromHash = hash.ScriptPubKey; Console.WriteLine("ScriptPubKey from hash: {0}", scriptPubKeyFromHash); }
public BlockchainAnchorRecorder(Uri url, Key publishingAddress, Network network, long satoshiFees) { this.url = url; this.publishingAddress = publishingAddress; this.network = network; this.satoshiFees = satoshiFees; }
static void Main(string[] args) { byte[] brain = System.Text.Encoding.UTF8.GetBytes("Lykkex1"); byte[] p = Hashes.SHA256(brain); string hexp = Encoders.Hex.EncodeData(p); Console.WriteLine(hexp); Console.WriteLine(Encoders.Base58.EncodeData(p)); String baddr = "1NDKhcEiha89vUPuxeg5zBdg38kMh9H1JQ"; byte[] byteArray = Encoders.Base58.DecodeData(baddr); string hex = Encoders.Hex.EncodeData(byteArray); // Console.Write(hex); BitcoinAddress addr = BitcoinAddress.Create(baddr); Key privateKey = new Key(); BitcoinAddress addr2 = privateKey.PubKey.GetAddress(Network.Main); string priv = "z4tiDi9WNB933EaS4wvnGL8UByT5jEJxZRnkF6zNAWGNAyrwvgWV6pNneRbh3WNQ9qFszWhVkp3eV7Sm3SANUBh"; byte[] arr = Encoders.Base58.DecodeData(priv); //Console.Write(Encoders.Hex.EncodeData(arr)); //BitcoinSecret secret = new BitcoinSecret("z4tiDi9WNB933EaS4wvnGL8UByT5jEJxZRnkF6zNAWGNAyrwvgWV6pNneRbh3WNQ9qFszWhVkp3eV7Sm3SANUBh", Network.Main); //string sign = secret.PrivateKey.SignMessage("I am owner"); //Console.Write("hash {0} {0}", addr.Hash, sign); Console.ReadLine(); }
public static BitcoinAddress GetBitcoinAddress() { var walletPrivateKey = ConfigurationManager.AppSettings[METACO_ENV_WALLET_PRIVATE_KEY_HEX_NAME]; var key = new Key(Encoders.Hex.DecodeData(walletPrivateKey)); return key.PubKey.GetAddress(Network.TestNet); }
public TedchainClient(NBitcoin.Key tedChainKey, string assetName, Uri tedChainUri, Network network) { this.tedChainKey = tedChainKey; this.assetName = assetName; this.tedChainUri = tedChainUri; this.network = network; }
private static string GenerateWif(Key key, string password, Network network) { var vch = key.ToBytes(); //Compute the Bitcoin address (ASCII), var addressBytes = Encoders.ASCII.DecodeData(key.PubKey.GetAddress(network).ToWif()); // and take the first four bytes of SHA256(SHA256()) of it. Let's call this "addresshash". var addresshash = Hashes.Hash256(addressBytes).ToBytes().SafeSubarray(0, 4); var derived = SCrypt.BitcoinComputeDerivedKey(Encoding.UTF8.GetBytes(password), addresshash); var encrypted = EncryptKey(vch, derived); var version = network.GetVersionBytes(Base58Type.ENCRYPTED_SECRET_KEY_NO_EC); byte flagByte = 0; flagByte |= 0x0C0; flagByte |= (key.IsCompressed ? (byte)0x20 : (byte)0x00); var bytes = version .Concat(new[] { flagByte }) .Concat(addresshash) .Concat(encrypted).ToArray(); return Encoders.Base58Check.EncodeData(bytes); }
public OpenchainClient(NBitcoin.Key openChainKey, string assetName, Uri openChainUri, Network network) { this.openChainKey = openChainKey; this.assetName = assetName; this.openChainUri = openChainUri; this.network = network; }
public static StealthPayment[] GetPayments(Transaction transaction, PubKey[] spendKeys, BitField bitField, Key scan) { List<StealthPayment> result = new List<StealthPayment>(); for(int i = 0 ; i < transaction.Outputs.Count ; i++) { var metadata = StealthMetadata.TryParse(transaction.Outputs[i].ScriptPubKey); if(metadata != null && bitField.Match(metadata.BitField)) { var payment = new StealthPayment(transaction.Outputs[i + 1].ScriptPubKey, metadata); if(scan != null && spendKeys != null) { if(payment.StealthKeys.Length != spendKeys.Length) continue; var expectedStealth = spendKeys.Select(s => s.UncoverReceiver(scan, metadata.EphemKey)).ToList(); foreach(var stealth in payment.StealthKeys) { var match = expectedStealth.FirstOrDefault(expected => expected.ID == stealth.ID); if(match != null) expectedStealth.Remove(match); } if(expectedStealth.Count != 0) continue; } result.Add(payment); } } return result.ToArray(); }
private void SetMaster(byte[] seed) { var hashMAC = Hashes.HMACSHA512(hashkey, seed); key = new Key(hashMAC.SafeSubarray(0, 32)); Buffer.BlockCopy(hashMAC, 32, vchChainCode, 0, 32); }
private static byte[] ToBytes(Key key) { var keyBytes = key.ToBytes(); if(!key.IsCompressed) return keyBytes; else return keyBytes.Concat(new byte[] { 0x01 }).ToArray(); }
public static Credentials Create(string copayerName, Network network) { var newCopayerKey = new NBitcoin.ExtKey(); var walletKey = new NBitcoin.Key(); var copayerXPrivKey = newCopayerKey.ToString(network); return(Credentials.FromExtendedPrivateKey(copayerXPrivKey, walletKey, copayerName, network)); }
public StealthMetadata(Key ephemKey, uint nonce) { var data = new MemoryStream(); data.WriteByte(6); var b = Utils.ToBytes(nonce, true); data.Write(b, 0, b.Length); data.Write(ephemKey.PubKey.Compress().ToBytes(), 0, 33); Fill(this, new Script(OpcodeType.OP_RETURN, Op.GetPushOp(data.ToArray()))); }
public BitcoinClient(Uri url, Key receivingKey, Key storageKey, Network network, ILogger logger) { this.url = url; this.ReceivingKey = receivingKey; this.storageKey = storageKey; this.logger = logger; this.Network = network; this.ReceivingAddress = ReceivingKey.PubKey.GetAddress(Network).ToString(); }
public static StealthMetadata CreateMetadata(Key ephemKey, BitField bitField = null) { for(uint nonce = 0 ; nonce < uint.MaxValue ; nonce++) { var metadata = new StealthMetadata(ephemKey, nonce); if(bitField == null || bitField.Match(metadata.BitField)) return metadata; } throw new ArgumentException("No nonce can satisfy the given bitfield, use another ephemKey"); }
public CustomWebApplicationFactory( NBitcoin.Key agent_key, int port, string[] public_keys, BitIndex.Client.IBitIndexApi bitIndexApi) { this.agent_key_ = agent_key; this.port_ = port; this.public_keys_ = public_keys; this.bitIndexApi_ = bitIndexApi; }
public ExtKey(ExtPubKey extPubKey, Key privateKey) { if(extPubKey == null) throw new ArgumentNullException("extPubKey"); if(privateKey == null) throw new ArgumentNullException("privateKey"); this.nChild = extPubKey.nChild; this.nDepth = extPubKey.nDepth; this.vchChainCode = extPubKey.vchChainCode; this.vchFingerprint = extPubKey.vchFingerprint; this.key = privateKey; }
private static JObject CreateExchangeMatch(JObject firstRequestJson, string firstRequestSignature, JObject secondRequestJson, string secondRequestSignature, Key exchangeKey) { dynamic exchangeMatch = new JObject(); exchangeMatch.Match = new JObject(); exchangeMatch.Match.FirstRequest = firstRequestJson; exchangeMatch.Match.FirstRequestSignature = firstRequestSignature; exchangeMatch.Match.SecondRequest = secondRequestJson; exchangeMatch.Match.SecondRequestSignature = secondRequestSignature; exchangeMatch.MatchSignature = exchangeKey.SignMessage(exchangeMatch.Match.ToString(Formatting.None)); return exchangeMatch; }
public NodeContext(IRandomNumberGenerator randomNumberGenerator) { _randomNumberGenerator = randomNumberGenerator; byte[] prv = new byte[32]; _randomNumberGenerator.GetBytes(prv.AsSpan()); var k = new NBitcoin.Key(prv); //TODO Dan // random data PrivateKey = k.ToBytes(); LocalPubKey = k.PubKey.ToHex(); }
public void Lesson1() { Key key = new Key(); //generates a new private key. PubKey pubKey = key.PubKey; //gets the matching public key. Console.WriteLine("Public Key: {0}", pubKey); KeyId hash = pubKey.Hash; //gets a hash of the public key. Console.WriteLine("Hashed public key: {0}", hash); BitcoinAddress address = pubKey.GetAddress(Network.Main); //retrieves the bitcoin address. Console.WriteLine("Address: {0}", address); Script scriptPubKeyFromAddress = address.ScriptPubKey; Console.WriteLine("ScriptPubKey from address: {0}", scriptPubKeyFromAddress); Script scriptPubKeyFromHash = hash.ScriptPubKey; Console.WriteLine("ScriptPubKey from hash: {0}", scriptPubKeyFromHash); }
public override Key GetKey(string password) { var derived = SCrypt.BitcoinComputeDerivedKey(password, AddressHash); var bitcoinprivkey = DecryptKey(Encrypted, derived); var key = new Key(bitcoinprivkey, fCompressedIn: IsCompressed); var addressBytes = Encoders.ASCII.DecodeData(key.PubKey.GetAddress(Network).ToString()); var salt = Hashes.Hash256(addressBytes).ToBytes().SafeSubarray(0, 4); if(!Utils.ArrayEqual(salt, AddressHash)) throw new SecurityException("Invalid password (or invalid Network)"); return key; }
public StealthPayment(int sigCount, PubKey[] spendPubKeys, Key privateKey, PubKey publicKey, StealthMetadata metadata) { Metadata = metadata; if(sigCount == 1 && spendPubKeys.Length == 1) { var template = new PayToPubkeyHashTemplate(); SpendableScript = template.GenerateScriptPubKey(spendPubKeys[0].Uncover(privateKey, publicKey).ID); } else { var template = new PayToMultiSigTemplate(); SpendableScript = template.GenerateScriptPubKey(sigCount, spendPubKeys.Select(p => p.Uncover(privateKey, publicKey)).ToArray()); } ParseSpendable(); }
public static string GetHexSignedTransaction(TransactionToSign txToSign) { var walletPrivateKey = ConfigurationManager.AppSettings[METACO_ENV_WALLET_PRIVATE_KEY_HEX_NAME]; var key = new Key(Encoders.Hex.DecodeData(walletPrivateKey)); var scriptPubKey = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(key.PubKey); var tx = new Transaction(Encoders.Hex.DecodeData(txToSign.Raw)); foreach(var inputsToSign in txToSign.InputsToSign) { var sigHash = tx.GetSignatureHash(scriptPubKey, inputsToSign.Index); var sig = key.Sign(sigHash); var txSign = new TransactionSignature(sig, SigHash.All); var inputScript = PayToPubkeyHashTemplate.Instance.GenerateScriptSig(txSign, key.PubKey); tx.Inputs[inputsToSign.Index].ScriptSig = inputScript; Assert.True(Script.VerifyScript(scriptPubKey, tx, inputsToSign.Index)); } return tx.ToHex(); }
private void SetupProvisioningWindow(PKType pKType) { CardPkType = pKType; CardGeneratedKey = new NBitcoin.Key(); switch (pKType) { case PKType.BTC: Network = NBitcoin.Network.Main; break; case PKType.BTC_TestNet: Network = NBitcoin.Network.TestNet; break; case PKType.CUSTOM: CardGeneratedKey = null; Network = null; break; } if (CardGeneratedKey != null) { PublicKeyTextBox.Text = CardGeneratedKey.PubKey.GetAddress(Network).ToString(); PrivateKeyTextBox.Text = CardGeneratedKey.GetBitcoinSecret(Network).ToWif(); //HelperText.Text = "When setting up a Bitcoin address, a private key is automatically generated for you. You can replace the private key data with a seed phrase mnemonic or your own WIF address."; } else { PublicKeyTextBox.Text = String.Empty; PrivateKeyTextBox.Text = String.Empty; // HelperText.Text = "Public data is not encrypted and optional. Any data may be used in the Private Key textbox, as long as it is under 96 bytes."; } }
public Key Uncover(Key scan, PubKey ephem) { var curve = ECKey.Secp256k1; var priv = new BigInteger(1, PubKey.GetStealthSharedSecret(scan, ephem)) .Add(new BigInteger(1, this.ToBytes())) .Mod(curve.N) .ToByteArrayUnsigned(); if(priv.Length < 32) priv = new byte[32 - priv.Length].Concat(priv).ToArray(); var key = new Key(priv, fCompressedIn: this.IsCompressed); return key; }
public PubKey UncoverReceiver(Key scan, PubKey ephem) { return(Uncover(scan, ephem)); }
public BitcoinSecret(Key key, Network network) : base(ToBytes(key), network) { }
public void SignAll(Key key) { for(int i = 0 ; i < Inputs.Count ; i++) { if(Inputs[i].ScriptSig == null) Inputs[i].ScriptSig = new PayToPubkeyHashTemplate().GenerateScriptPubKey(key.PubKey); } for(int i = 0 ; i < Inputs.Count ; i++) { var hash = Inputs[i].ScriptSig.SignatureHash(this, i, SigHash.All); var sig = key.Sign(hash); Inputs[i].ScriptSig = new PayToPubkeyHashTemplate().GenerateScriptSig(new TransactionSignature(sig, SigHash.All), key.PubKey); } }
public BitcoinSecret CreateBitcoinSecret(Key key) { return(new BitcoinSecret(key, this)); }
public PubKey[] Uncover(PubKey[] spendPubKeys, Key scanKey) { var pubKeys = new PubKey[spendPubKeys.Length]; for(int i = 0 ; i < pubKeys.Length ; i++) { pubKeys[i] = spendPubKeys[i].UncoverReceiver(scanKey, StealthMetadata.EphemKey); } return pubKeys; }
/// <summary> /// Scan the Transaction for StealthCoin given address and scan key /// </summary> /// <param name="tx">The transaction to scan</param> /// <param name="address">The stealth address</param> /// <param name="scan">The scan private key</param> /// <returns></returns> public static StealthCoin Find(Transaction tx, BitcoinStealthAddress address, Key scan) { var payment = address.GetPayments(tx, scan).FirstOrDefault(); if(payment == null) return null; var txId = tx.GetHash(); var txout = tx.Outputs.First(o => o.ScriptPubKey == payment.ScriptPubKey); return new StealthCoin(new OutPoint(txId, tx.Outputs.IndexOf(txout)), txout, payment.Redeem, payment.Metadata, address); }
public TransactionSignature Sign(Key key) { return(Sign(key, SigHash.All)); }
public PubKey UncoverSender(Key ephem, PubKey scan) { return Uncover(ephem, scan); }
internal static byte[] GetStealthSharedSecret(Key priv, PubKey pub) { var curve = ECKey.CreateCurve(); var pubec = curve.Curve.DecodePoint(pub.ToBytes()); var p = pubec.Multiply(new BigInteger(1, priv.ToBytes())); var pBytes = new PubKey(p.GetEncoded()).Compress().ToBytes(); var hash = Hashes.SHA256(pBytes); return hash; }
private void ProvisionCardButton_Click(object sender, RoutedEventArgs e) { CreateKeyErrorLabel.Content = ""; CreatingKeyLabel.Content = ""; if (passphrase.Password.Length == 0 || passphraseConfirmation.Password.Length == 0) { CreateKeyErrorLabel.Content = "A passphrase was not entered."; return; } if (passphrase.Password != passphraseConfirmation.Password) { CreateKeyErrorLabel.Content = "Passphrases entered did not match."; return; } if (PrivateKeyTextBox.Text.Length == 0) { CreateKeyErrorLabel.Content = "No private key data to store."; return; } if (CardPkType == PKType.BTC || CardPkType == PKType.BTC_TestNet) { // Check if we are importing a user's wif or mnemonic, update public key field // to allow the user to confirm if (CardGeneratedKey.GetBitcoinSecret(Network).ToWif() != PrivateKeyTextBox.Text) { try { NBitcoin.Key importedKey = null; int spacesDetected = PrivateKeyTextBox.Text.Count(a => a == ' '); // Check for mnemonic if (spacesDetected >= 11) { NBitcoin.Mnemonic mnemonic = new NBitcoin.Mnemonic(PrivateKeyTextBox.Text); // Force bitcoin and first address importedKey = mnemonic.DeriveExtKey().Derive(KeyPath.Parse("m/44'/0'/0'/0/0")).PrivateKey; } else { // Check for wif importedKey = NBitcoin.Key.Parse(PrivateKeyTextBox.Text); } // Replace CardGeneratedKey with imported key. Only valid for bitcoin addresses. CardGeneratedKey = importedKey; PublicKeyTextBox.Text = importedKey.PubKey.GetAddress(Network).ToString(); PrivateKeyTextBox.Text = importedKey.GetBitcoinSecret(Network).ToWif(); CreatingKeyLabel.Content = "Key data imported, ready to setup your bChip."; return; } catch (Exception ex) { Logger.Log(ex); CreateKeyErrorLabel.Content = "Failed to automatically parse private key data."; return; } } } byte[] privateKeyToEncrypt = null; byte[] publicKeyData = null; if (CardGeneratedKey != null) { // Users can optionally remove the public key portion, so we'll skip saving it. if (PublicKeyTextBox.Text.Length > 0) { publicKeyData = CardGeneratedKey.PubKey.ToBytes(); } byte[] privateKeyBytes = CardGeneratedKey.GetWif(Network.Main).ToBytes(); // Always seem to get an extra bit at the end... if (privateKeyBytes.Length == 33 && privateKeyBytes[32] == 0x1) { privateKeyToEncrypt = privateKeyBytes.Take(32).ToArray(); } else { privateKeyToEncrypt = privateKeyBytes; } } else { if (PublicKeyTextBox.Text.Length > 0) { publicKeyData = CryptographicBuffer.ConvertStringToBinary(PublicKeyTextBox.Text, BinaryStringEncoding.Utf8).ToArray(); } privateKeyToEncrypt = CryptographicBuffer.ConvertStringToBinary(PrivateKeyTextBox.Text, BinaryStringEncoding.Utf8).ToArray(); } if (privateKeyToEncrypt.Length > BChipMemoryLayout_BCHIP.PRIVATEKEY_MAX_DATA) { CreateKeyErrorLabel.Content = $"Private key was {PrivateKeyTextBox.Text.Length} bytes, {Math.Abs(PrivateKeyTextBox.Text.Length - BChipMemoryLayout_BCHIP.PRIVATEKEY_MAX_DATA)} bytes over the limit."; return; } if (publicKeyData != null) { if (publicKeyData.Length > BChipMemoryLayout_BCHIP.PUBKEY_MAX_DATA) { CreateKeyErrorLabel.Content = $"Public key was {PublicKeyTextBox.Text.Length} bytes, {Math.Abs(PublicKeyTextBox.Text.Length - BChipMemoryLayout_BCHIP.PUBKEY_MAX_DATA)} bytes over the limit."; return; } } string friendlyName = FriendlyNameTextBox.Text; ProvisionNewKeysViewGrid.Dispatcher.BeginInvoke(new Action(() => { ProvisionNewKeysViewGrid.IsEnabled = false; })); Task.Run(new Action(() => { try { UpdateTextLabel(CreatingKeyLabel, "Setting up bChip! Do not remove card."); // Encrypt data BChipMemoryLayout_BCHIP bchip = LoadedBChips.SmartCardData as BChipMemoryLayout_BCHIP; bchip.EncryptPrivateKeyData(CardPkType, passphrase.Password, privateKeyToEncrypt, publicKeyData); bchip.SetFriendlyName(friendlyName); using (var context = _contextFactory.Establish(SCardScope.System)) { using (var isoReader = new IsoReader(context, LoadedBChips.ReaderName, SCardShareMode.Shared, SCardProtocol.Any)) { if (LoadedBChips.AdpuInterface.RequiresInit) { Response initResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.InitCard()); if (initResponse.SW1 != 0x90) { Logger.Log("Error initializing smart card reader"); } } var unlockResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.UnblockCard()); Logger.Log($"ADPU response from pin request: {unlockResponse.StatusWord:X}"); if (unlockResponse.SW1 != 0x90) { UpdateTextLabel(CreatingKeyLabel, ""); UpdateTextLabel(CreateKeyErrorLabel, "Could not be unlock bchip for writing."); return; } var writeResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.WriteCard(bchip)); Logger.Log($"ADPU response from write card data: {unlockResponse.StatusWord:X}"); if (writeResponse.StatusWord != 0x00009000) { UpdateTextLabel(CreatingKeyLabel, ""); UpdateTextLabel(CreateKeyErrorLabel, "Failure writing data to the bchip."); return; } } } UpdateTextLabel(CreatingKeyLabel, string.Empty); ClearTextBox(FriendlyNameTextBox); // Done? clear loaded card, reload card LoadedBChips = null; ChangePageUi(PageToShow.NoCard, null); ScanAndLoadConnectedCards(); } catch (Exception ex) { Logger.Log(ex); } finally { ProvisionNewKeysViewGrid.Dispatcher.BeginInvoke(new Action(() => { ProvisionNewKeysViewGrid.IsEnabled = true; })); } })); }
public PubKey UncoverSender(Key ephem, PubKey scan) { return(Uncover(ephem, scan)); }
public void AddKey(Key key) { _Keys.Add(key); }
/// <summary> /// Scan the Transaction for StealthCoin given address and scan key /// </summary> /// <param name="tx">The transaction to scan</param> /// <param name="address">The stealth address</param> /// <param name="scan">The scan private key</param> /// <returns></returns> public static StealthCoin Find(Transaction tx, BitcoinStealthAddress address, Key scan) { var payment = address.GetPayments(tx, scan).FirstOrDefault(); if (payment == null) { return(null); } var txId = tx.GetHash(); var txout = tx.Outputs.First(o => o.ScriptPubKey == payment.ScriptPubKey); return(new StealthCoin(new OutPoint(txId, tx.Outputs.IndexOf(txout)), txout, payment.Redeem, payment.Metadata, address)); }
public BitcoinEncryptedSecretNoEC(Key key, string password, Network network) : base(GenerateWif(key, password, network), network) { }
public static BitcoinEncryptedSecretNoEC Generate(Key key, string password, Network network) { return new BitcoinEncryptedSecretNoEC(key, password, network); }
public override Key GetKey(string password) { var encrypted = PartialEncrypted.ToArray(); //Derive passfactor using scrypt with ownerentropy and the user's passphrase and use it to recompute passpoint byte[] passfactor = CalculatePassFactor(password, LotSequence, OwnerEntropy); var passpoint = CalculatePassPoint(passfactor); var derived = SCrypt.BitcoinComputeDerivedKey2(passpoint, this.AddressHash.Concat(this.OwnerEntropy).ToArray()); //Decrypt encryptedpart1 to yield the remainder of seedb. var seedb = DecryptSeed(encrypted, derived); var factorb = Hashes.Hash256(seedb).ToBytes(); var curve = ECKey.CreateCurve(); //Multiply passfactor by factorb mod N to yield the private key associated with generatedaddress. var keyNum = new BigInteger(1, passfactor).Multiply(new BigInteger(1, factorb)).Mod(curve.N); var keyBytes = keyNum.ToByteArrayUnsigned(); if(keyBytes.Length < 32) keyBytes = new byte[32 - keyBytes.Length].Concat(keyBytes).ToArray(); var key = new Key(keyBytes, fCompressedIn: IsCompressed); var generatedaddress = key.PubKey.GetAddress(Network); var addresshash = HashAddress(generatedaddress); if(!Utils.ArrayEqual(addresshash, AddressHash)) throw new SecurityException("Invalid password (or invalid Network)"); return key; }
public Key[] Uncover(Key[] spendKeys, Key scanKey) { var keys = new Key[spendKeys.Length]; for(int i = 0 ; i < keys.Length ; i++) { keys[i] = spendKeys[i].Uncover(scanKey, StealthMetadata.EphemKey); } return keys; }