// 4.1. DH functions /// <summary> /// Create a X25519 static keyPair out of a private key. /// </summary> /// <param name="privateKey">Private key, if null - generates a random key pair</param> /// <returns>X25519 key pair</returns> public static KeyPair GenerateKeyPair(byte[] privateKey = null) { var keyPair = new KeyPair() { PublicKey = new byte[Asymmetric.DhLen], PrivateKey = new byte[Asymmetric.DhLen] }; if (privateKey == null) { var random = new RNGCryptoServiceProvider(); #if !DEBUG_DETERMINISTIC random.GetBytes(keyPair.PrivateKey, 0, keyPair.PrivateKey.Length); #endif } else { if (privateKey.Length != Asymmetric.DhLen) { throw new Exception($"disco: expecting {Asymmetric.DhLen} byte key array"); } privateKey.CopyTo(keyPair.PrivateKey, 0); } keyPair.PublicKey = ScalarMult.Base(keyPair.PrivateKey); return(keyPair); }
/// <summary> /// Load Dico key pair from file /// </summary> /// <param name="fileName"></param> /// <returns>Disco key pair</returns> public static KeyPair LoadDiscoKeyPair(string fileName) { var hex = File.ReadAllText(fileName); if (hex.Length != 128) { throw new Exception("Disco: Disco key pair file is not correctly formated"); } var keyPair = new KeyPair(); keyPair.ImportPublicKey(hex.Substring(0, 64)); keyPair.ImportPrivateKey(hex.Substring(64, 64)); return(keyPair); }
/// <summary> /// Disco peer initialization /// </summary> /// <param name="handshakeType">Noise handshake pattern</param> /// <param name="initiator">This party initiates connection</param> /// <param name="prologue">Prologue string, some data prior to handshake</param> /// <param name="s">local static key</param> /// <param name="e">local ephemeral key</param> /// <param name="rs">remote static key</param> /// <param name="re">remote ephemeral key</param> /// <returns>Initialized Disco handshake state</returns> public static HandshakeState InitializeDisco( NoiseHandshakeType handshakeType, bool initiator, byte[] prologue, KeyPair s, KeyPair e, KeyPair rs, KeyPair re) { var handshakePattern = HandshakePattern.GetPattern(handshakeType); var handshakeState = new HandshakeState { SymmetricState = new SymmetricState($"Noise_{handshakePattern.Name}_25519_STROBEv1.0.2"), Initiator = initiator, ShouldWrite = initiator }; try { if (prologue != null) { handshakeState.SymmetricState.MixHash(prologue); } if (s != null) { handshakeState.S = s; } if (e != null) { throw new NotSupportedException("disco: fallback patterns are not implemented"); } if (rs != null) { handshakeState.Rs = rs; } if (re != null) { throw new NotSupportedException("disco: fallback patterns are not implemented"); } //Calls MixHash() once for each public key listed in the pre-messages from handshake_pattern, //with the specified public key as input (see Section 7 for an explanation of pre-messages). //If both initiator and responder have pre-messages, the initiator's public keys are hashed first. // initiator pre-message pattern foreach (var token in handshakePattern.PreMessagePatterns[0]) { if (token == Tokens.TokenS) { if (initiator) { if (s == null) { throw new Exception("disco: the static key of the client should be set"); } handshakeState.SymmetricState.MixHash(s.PublicKey); } else { if (rs == null) { throw new Exception("disco: the remote static key of the server should be set"); } handshakeState.SymmetricState.MixHash(rs.PublicKey); } } else { throw new Exception("disco: token of pre-message not supported"); } } // responder pre-message pattern foreach (var token in handshakePattern.PreMessagePatterns[1]) { if (token == Tokens.TokenS) { if (initiator) { if (rs == null) { throw new Exception("disco: the remote static key of the client should be set"); } handshakeState.SymmetricState.MixHash(rs.PublicKey); } else { if (s == null) { throw new Exception("disco: the static key of the server should be set"); } handshakeState.SymmetricState.MixHash(s.PublicKey); } } else { throw new NotSupportedException("disco: token of pre - message not supported"); } } handshakeState.MessagePatterns = handshakePattern.MessagePatterns; return(handshakeState); } catch (Exception) { handshakeState.Dispose(); throw; } }
/// <summary> /// Perform DH on public key /// </summary> /// <param name="keyPair">Containing private key</param> /// <param name="publicKey">Remote party's public key</param> /// <returns>DH result</returns> public static byte[] Dh(KeyPair keyPair, byte[] publicKey) { return(ScalarMult.Mult(keyPair.PrivateKey, publicKey)); }