/// <summary> /// Encrypts and packs the packet for sending down the wire. /// </summary> /// <returns>The packet.</returns> /// <param name="sk">Local (sender) secret key.</param> /// <param name="pk">Remote (recipent) public key</param> public byte[] EncryptPacket(byte[] sk, byte[] pk) { byte[] header = this.PackHeader(); //seq + ack + id + payload MemoryStream rpcRaw = new MemoryStream(); if (this.HasRPC) { if (this.RPCs.Count > 0) { rpcRaw.WriteByte((byte)RPCType.RPCStart); foreach (IHasSerializationTag rpc in this.RPCs) { byte[] curRpcBytes = rpc.Serialize(); rpcRaw.WriteByte(rpc.SerializationTag); rpcRaw.Write(curRpcBytes, 0, curRpcBytes.Length); } rpcRaw.WriteByte((byte)RPCType.RPCEnd); } } rpcRaw.Close(); byte[] rpcBytes = rpcRaw.ToArray(); int totalLength = 4 + 4 + 4; if (rpcBytes.Length > 0) { totalLength += rpcBytes.Length; } totalLength++; totalLength += this.Payload.Length; byte[] encryptedPart = new byte[totalLength]; uint index = 0; PackingHelpers.PackUint32(Seq, encryptedPart, ref index); PackingHelpers.PackUint32(Ack, encryptedPart, ref index); PackingHelpers.PackUint32(CID, encryptedPart, ref index); if (rpcBytes.Length > 0) { Array.Copy(rpcBytes, 0, encryptedPart, index, rpcBytes.Length); index += (uint)rpcBytes.Length; } //start message flag. encryptedPart[index] = START_PAYLOAD_FLAG; ++index; Array.Copy(this.Payload, 0, encryptedPart, index, this.Payload.Length); byte[] ciphered = PublicKeyBox.Create(encryptedPart, this.Nonce, sk, pk); this.CipherText = ciphered; byte[] ret = new byte[header.Length + ciphered.Length]; Array.Copy(header, ret, header.Length); Array.Copy(ciphered, 0, ret, header.Length, ciphered.Length); this.rawBytes = ret; return(ret); }
/// <summary> /// Packs the header. The total GenericPacket size is the size of the GenericPacket /// including the data that is going to be packed. The returning byte /// array will be of the size totalPacketSize + header overhead /// </summary> /// <returns>The header.</returns> /// <param name="totalPacketSize">Total GenericPacket size.</param> public byte[] PackHeader() { UInt32 packetOverhead = MIN_PACKET_HEADER_SIZE; if (this.hasEPK) { packetOverhead += 32; } if (this.hasPuzzle) { packetOverhead += 148; } byte[] data = new byte[packetOverhead]; //constant start of message part UInt32 curIndex = 0; UInt64 tmpTID = this.TID; if (this.hasEPK) { tmpTID |= Common.PUBLIC_KEY_FLAG; } if (this.hasPuzzle) { tmpTID |= Common.PUZZLE_FLAG; } PackingHelpers.PackUint64(tmpTID, data, ref curIndex); //curIndex += 1; //pack the nonce Array.Copy(this.Nonce, 0, data, curIndex, 24); curIndex += 24; //TODO: Add flags to the GenericPacket header if (this.hasEPK) { //this.TID = this.TID |= PUBLIC_KEY_FLAG; Array.Copy(this.EuphemeralPublicKey, 0, data, curIndex, this.EuphemeralPublicKey.Length); curIndex += (UInt16)this.EuphemeralPublicKey.Length; } if (this.hasPuzzle) { //this.TID = this.TID |= PUZZLE_FLAG; Array.Copy(this.PuzzleOrSolution, 0, data, curIndex, this.PuzzleOrSolution.Length); curIndex += (UInt32)this.PuzzleOrSolution.Length; } /*if (this.RPCs.Count > 0) * { * byte[] bytes = rpcRaw.ToArray(); * Array.Copy(bytes, 0, data, curIndex, bytes.Length); * }*/ return(data); }
/// <summary> /// Unpacks the header. /// </summary> /// <returns>The header.</returns> /// <param name="data">Data.</param> /// <param name="serverKey">Server key.</param> /// <param name="refOffset">Reference offset.</param> public static GenericPacket UnpackPacket(byte[] data) { UInt64 tidWithFlags, tid; tidWithFlags = tid = 0; byte[] nonce = new byte[24]; //todo: clean up the GenericPacket classes to allow us to do this without UInt32 seq, ack, cid; seq = ack = cid = 0; byte[] epk = null, puzzle = null; bool hasPK, hasPuzzle; uint curIndex = 0; PackingHelpers.UnpackUint64(ref tidWithFlags, data, ref curIndex); //curIndex += 1; tid = tidWithFlags & ~Common.TID_FLAGS; hasPK = (tidWithFlags & Common.PUBLIC_KEY_FLAG) == Common.PUBLIC_KEY_FLAG; hasPuzzle = (tidWithFlags & Common.PUZZLE_FLAG) == Common.PUZZLE_FLAG; //PackingHelpers.UnpackUint64 (ref nonce, data, ref curIndex); Array.Copy(data, curIndex, nonce, 0, nonce.Length); curIndex += (uint)(nonce.Length); if (hasPK) { epk = new byte[EPK_SIZE]; Array.Copy(data, curIndex, epk, 0, EPK_SIZE); curIndex += (EPK_SIZE + 1); } if (hasPuzzle) { puzzle = new byte[PUZZLE_SOLUTIONS_SIZE]; Array.Copy(data, curIndex, puzzle, 0, PUZZLE_SOLUTIONS_SIZE); curIndex += PUZZLE_SOLUTIONS_SIZE + 1; } //The remainder of the GenericPacket is encrypted. If there is an E_PK present //then this is likely a abstractTunnel open GenericPacket. Unlock it with the server key if (hasPK && data.Length > curIndex) { //byte[] cipherPart = new byte[data.Length - curIndex]; //byte[] decrypted = Sodium.PublicKeyBox.OpenDetached (cipherPart, nonce, epk, secretKey); //copy in the checksum //byte[] rawMessage = new byte[decrypted.Length - CHECKSUM_SIZE]; //Array.Copy (decrypted, rawMessage, 0, rawMessage.Length); //here we have to create an OpenPacket //todo: is this strictly nessecary? byte[] cipherPart = new byte[data.Length - curIndex]; Array.Copy(data, curIndex, cipherPart, 0, cipherPart.Length); if (!hasPuzzle) { OpenTunnelPacket packet = new OpenTunnelPacket(tid, seq, cid, ack, epk, cipherPart); packet.Nonce = nonce; return(packet); } return(new OpenTunnelPacket(tid, seq, cid, ack, epk, cipherPart, puzzle)); } else { EncryptedPacket packet = new EncryptedPacket(tid, cid, ack); packet.Seq = seq; packet.Nonce = nonce; byte[] cipherPart = new byte[data.Length - curIndex]; Array.Copy(data, curIndex, cipherPart, 0, cipherPart.Length); packet.CipherText = cipherPart; return(packet); } }
/// <summary> /// Unpacks the header from the local binary representation /// </summary> public virtual uint UnpackHeader() { UInt64 tidWithFlags, tid; tidWithFlags = tid = 0; byte[] nonce = new byte[24]; //todo: clean up the GenericPacket classes to allow us to do this without UInt32 seq, ack; seq = ack = 0; byte[] epk = null, puzzle = null; bool hasPK, hasPuzzle; uint curIndex = 0; byte[] data = this.rawBytes; PackingHelpers.UnpackUint64(ref tidWithFlags, data, ref curIndex); //curIndex += 1; tid = tidWithFlags & ~Common.TID_FLAGS; hasPK = (tidWithFlags & Common.PUBLIC_KEY_FLAG) != 0; hasPuzzle = (tidWithFlags & Common.PUZZLE_FLAG) != 0; this.TID = tid; this.Seq = seq; this.Ack = ack; this.Nonce = nonce; //PackingHelpers.UnpackUint64 (ref nonce, data, ref curIndex); Array.Copy(data, curIndex, nonce, 0, nonce.Length); curIndex += (uint)(nonce.Length); if (hasPK) { epk = new byte[EPK_SIZE]; Array.Copy(data, curIndex, epk, 0, EPK_SIZE); curIndex += EPK_SIZE; this.EuphemeralPublicKey = epk; } if (hasPuzzle) { puzzle = new byte[PUZZLE_SOLUTIONS_SIZE]; Array.Copy(data, curIndex, puzzle, 0, PUZZLE_SOLUTIONS_SIZE); curIndex += PUZZLE_SOLUTIONS_SIZE; this.PuzzleOrSolution = puzzle; } //The remainder of the GenericPacket is encrypted. If there is an E_PK present //then this is likely a abstractTunnel open GenericPacket. Unlock it with the server key /*if(hasPK && data.Length > curIndex){ * //byte[] cipherPart = new byte[data.Length - curIndex]; * //byte[] decrypted = Sodium.PublicKeyBox.OpenDetached (cipherPart, nonce, epk, secretKey); * //copy in the checksum * //byte[] rawMessage = new byte[decrypted.Length - CHECKSUM_SIZE]; * //Array.Copy (decrypted, rawMessage, 0, rawMessage.Length); * //here we have to create an OpenPacket * byte[] cipherPart = new byte[data.Length - curIndex]; * Array.Copy (data, curIndex, cipherPart, 0, cipherPart.Length); * if (!hasPuzzle) { * OpenTunnelPacket GenericPacket = new OpenTunnelPacket (tid, seq, id, ack, epk, cipherPart); * GenericPacket.Nonce = nonce; * * } * }else{*/ //EncryptedPacket GenericPacket = new EncryptedPacket(tid, id, ack); //GenericPacket.Seq = seq; //GenericPacket.Nonce = nonce; //GenericPacket.CipherText = cipherPart; return(curIndex); //} }
public bool DecryptPacket(byte[] sk, byte[] pk) { byte[] decrypt; try { decrypt = Sodium.PublicKeyBox.Open(this.CipherText, this.Nonce, sk, pk); } catch (CryptographicException ex) { Debug.Fail(ex.StackTrace); return(false); } catch (Exception ex) { Debug.Fail(ex.StackTrace); return(false); } MemoryStream ms = new MemoryStream(decrypt); UInt32 seq = 0; UInt32 ack = 0; UInt32 cid = 0; PackingHelpers.UnpackUint32(ref seq, ms); PackingHelpers.UnpackUint32(ref ack, ms); PackingHelpers.UnpackUint32(ref cid, ms); this.Seq = seq; this.Ack = ack; this.CID = cid; int flag = ms.ReadByte(); Debug.Assert((flag == (int)RPCType.RPCStart || flag == (int)START_PAYLOAD_FLAG)); if (flag == (int)RPCType.RPCStart) { //deserialize the RPC this.RPCs = new ArrayList <IHasSerializationTag>(); byte serializationTag; while ((serializationTag = (byte)ms.ReadByte()) != (byte)RPCType.RPCEnd) { IHasSerializationTag rpc = null; switch ((RPCType)serializationTag) { case RPCType.AnonymousPipe: rpc = RPCBase <CreateAnonymousPipe> .Unpack(ms); break; case RPCType.AuthenticatedPipe: rpc = RPCBase <CreateAuthenticatedPipe> .Unpack(ms); break; case RPCType.ClosePipe: rpc = RPCBase <ClosePipeRPC> .Unpack(ms); break; case RPCType.AckPipe: rpc = RPCBase <AckPipe> .Unpack(ms); break; case RPCType.RefusePipe: rpc = RPCBase <RefusePipe> .Unpack(ms); break; case RPCType.RequestCertificate: rpc = RPCBase <RequestCertificate> .Unpack(ms); break; case RPCType.GiveCertificate: rpc = RPCBase <GiveCertificate> .Unpack(ms); break; case RPCType.Ok: rpc = RPCBase <OkRPC> .Unpack(ms); break; case RPCType.NextTID: rpc = RPCBase <NextTID> .Unpack(ms); break; case RPCType.RekeyNow: rpc = RPCBase <RekeyNow> .Unpack(ms); break; case RPCType.PosePuzzle: rpc = RPCBase <PosePuzzle> .Unpack(ms); break; case RPCType.PuzzleSolution: //RPCBase<>.Unpack(ms); //major todo break; case RPCType.WindowResize: rpc = RPCBase <ResizeWindow> .Unpack(ms); break; default: //todo we should error out but simply drop packets throw new ArgumentOutOfRangeException(); } this.RPCs.Add(rpc); } flag = ms.ReadByte(); } if (flag == START_PAYLOAD_FLAG) { int index = (int)ms.Position; byte[] payload = new byte[decrypt.Length - index]; Array.Copy(decrypt, index, payload, 0, payload.Length); this.Payload = payload; } else { this.Payload = new byte[0]; } return(true); }