Class: Ciri::P2P::RLPX::FrameIO
- Inherits:
-
Object
- Object
- Ciri::P2P::RLPX::FrameIO
- Extended by:
- Forwardable
- Defined in:
- lib/ciri/p2p/rlpx/frame_io.rb
Defined Under Namespace
Classes: Error, InvalidError, OverflowError
Constant Summary collapse
- MAX_MESSAGE_SIZE =
max message size, took 3 byte to store message size, equal to uint24 max size
(1 << 24) - 1
Instance Attribute Summary collapse
-
#snappy ⇒ Object
Returns the value of attribute snappy.
Instance Method Summary collapse
-
#initialize(io, secrets) ⇒ FrameIO
constructor
A new instance of FrameIO.
- #read_msg ⇒ Object
- #send_data(code, data) ⇒ Object
- #write_msg(msg) ⇒ Object
Constructor Details
#initialize(io, secrets) ⇒ FrameIO
Returns a new instance of FrameIO.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/ciri/p2p/rlpx/frame_io.rb', line 57 def initialize(io, secrets) @io = io @secrets = secrets @snappy = false # snappy compress mac_aes_version = secrets.mac.size * 8 @mac = OpenSSL::Cipher.new("AES#{mac_aes_version}") @mac.encrypt @mac.key = secrets.mac # init encrypt/decrypt aes_version = secrets.aes.size * 8 @encrypt = OpenSSL::Cipher::AES.new(aes_version, :CTR) @decrypt = OpenSSL::Cipher::AES.new(aes_version, :CTR) zero_iv = "\x00".b * @encrypt.iv_len @encrypt.iv = zero_iv @encrypt.key = secrets.aes @decrypt.iv = zero_iv @decrypt.key = secrets.aes end |
Instance Attribute Details
#snappy ⇒ Object
Returns the value of attribute snappy.
55 56 57 |
# File 'lib/ciri/p2p/rlpx/frame_io.rb', line 55 def snappy @snappy end |
Instance Method Details
#read_msg ⇒ Object
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/ciri/p2p/rlpx/frame_io.rb', line 125 def read_msg # verify header mac head_buf = read(32) verify_mac = update_mac(@secrets.ingress_mac, head_buf[0...16]) unless Ciri::Utils.secret_compare(verify_mac, head_buf[16...32]) raise InvalidError.new('bad header mac') end # decrypt header head_buf[0...16] = @decrypt.update(head_buf[0...16]) + @decrypt.final # read frame frame_size = read_frame_size head_buf # frame size should padded to n*16 bytes need_padding = frame_size % 16 padded_frame_size = need_padding > 0 ? frame_size + (16 - need_padding) : frame_size frame_buf = read(padded_frame_size) # verify frame mac @secrets.ingress_mac.update(frame_buf) frame_digest = @secrets.ingress_mac.digest verify_mac = update_mac(@secrets.ingress_mac, frame_digest) # clear head_buf 16...32 bytes(header mac), since we will not need it frame_mac = head_buf[16...32] = read(16) unless Ciri::Utils.secret_compare(verify_mac, frame_mac) raise InvalidError.new('bad frame mac') end # decrypt frame frame_content = @decrypt.update(frame_buf) + @decrypt.final frame_content = frame_content[0...frame_size] msg_code = RLP.decode_with_type frame_content[0], Integer msg = Message.new(code: msg_code, size: frame_content.size - 1, payload: frame_content[1..-1]) # snappy decompress if enable if snappy msg.payload = Snappy.inflate(msg.payload) msg.size = msg.payload.size end msg end |
#send_data(code, data) ⇒ Object
78 79 80 81 |
# File 'lib/ciri/p2p/rlpx/frame_io.rb', line 78 def send_data(code, data) msg = Message.new(code: code, size: data.size, payload: data) write_msg(msg) end |
#write_msg(msg) ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/ciri/p2p/rlpx/frame_io.rb', line 83 def write_msg(msg) pkg_type = RLP.encode_with_type msg.code, Integer, zero: "\x00" # use snappy compress if enable if snappy if msg.size > MAX_MESSAGE_SIZE raise OverflowError.new("Message size is overflow, msg size: #{msg.size}") end msg.payload = Snappy.deflate(msg.payload) msg.size = msg.payload.size end # write header head_buf = "\x00".b * 32 frame_size = pkg_type.size + msg.size if frame_size > MAX_MESSAGE_SIZE raise OverflowError.new("Message size is overflow, frame size: #{frame_size}") end write_frame_size(head_buf, frame_size) # Can't find related RFC or RLPX Spec, below code is copy from geth # write zero header, but I can't find spec or explanations of 'zero header' head_buf[3..5] = [0xC2, 0x80, 0x80].pack('c*') # encrypt first half head_buf[0...16] = @encrypt.update(head_buf[0...16]) + @encrypt.final # write header mac head_buf[16...32] = update_mac(@secrets.egress_mac, head_buf[0...16]) @io.write head_buf # write encrypt frame write_frame(pkg_type) write_frame(msg.payload) # pad to n*16 bytes if (need_padding = frame_size % 16) > 0 write_frame("\x00".b * (16 - need_padding)) end finish_write_frame # because we use Async::IO::Stream as IO object, we must invoke flush to make sure data is send flush end |