Class: Sodium::Buffer

Inherits:
Object
  • Object
show all
Defined in:
lib/sodium/buffer.rb

Defined Under Namespace

Classes: ZeroingDelegator

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bytes) ⇒ Buffer

Returns a new instance of Buffer.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/sodium/buffer.rb', line 50

def initialize(bytes)
  # allocate memory for the incoming bytes and copy them in to our
  # newly-owned memory
  bytes   = bytes.to_str # unwrap ZeroingDelegator if present
  pointer = Sodium::FFI::LibC.calloc(1, bytes.bytesize)
  pointer.write_string(bytes)

  # use the ZeroingDelegator finalizer to wipe the memory from our
  # pointer, and attach our own finalizer to free() the memory
  ZeroingDelegator._finalize! self, pointer, bytes.bytesize,
    &self.class._finalizer(pointer, bytes.bytesize)

  # zero out the bytes passed to us, since we can't control their
  # lifecycle
  ZeroingDelegator._mwipe!(bytes, bytes.bytesize)

  # WARNING: The following section is critical. Edit with caution!
  #
  # We create a new pointer to the bytes allocated earlier and set a
  # hidden instance variable pointing at ourself. We do the latter
  # so that there is a cyclic dependency between the buffer and the
  # pointer; if either is still live in the current scope, it is
  # enough to prevent the other from being collected. We create the
  # new pointer since the previous pointer is pointed to by our
  # finalizer; if we didn't, its existence in the finalizer proc
  # would keep us from being garbage collected because of its
  # pointer to us!
  @bytesize = bytes.bytesize
  @bytes    = FFI::Pointer.new(pointer.address)
  @bytes.instance_variable_set(:@_sodium_buffer, self)

  @bytes.freeze
  self  .freeze
end

Class Method Details

.empty(size) ⇒ Object



13
14
15
# File 'lib/sodium/buffer.rb', line 13

def self.empty(size)
  self.new("\0" * size).tap {|buffer| yield buffer if block_given? }
end

.key(size) ⇒ Object



5
6
7
# File 'lib/sodium/buffer.rb', line 5

def self.key(size)
  Sodium::Random.bytes(size)
end

.ljust(string, size) ⇒ Object



17
18
19
20
21
22
23
# File 'lib/sodium/buffer.rb', line 17

def self.ljust(string, size)
  size = (size > string.bytesize) ? size : string.bytesize

  self.empty(size) do |buffer|
    buffer[0, string.bytesize] = string
  end
end

.lpad(string, size, &block) ⇒ Object



33
34
35
# File 'lib/sodium/buffer.rb', line 33

def self.lpad(string, size, &block)
  self.rjust(string, string.bytesize + size, &block)
end

.new(bytes, size = bytes.bytesize) ⇒ Object



41
42
43
44
45
46
47
48
# File 'lib/sodium/buffer.rb', line 41

def self.new(bytes, size = bytes.bytesize)
  raise Sodium::LengthError, "buffer must be exactly #{size} bytes long" unless
    bytes.bytesize == size

  bytes.kind_of?(self) ?
    bytes              :
    super(bytes)
end

.nonce(size) ⇒ Object



9
10
11
# File 'lib/sodium/buffer.rb', line 9

def self.nonce(size)
  Sodium::Random.bytes(size)
end

.rjust(string, size) ⇒ Object



25
26
27
28
29
30
31
# File 'lib/sodium/buffer.rb', line 25

def self.rjust(string, size)
  size = (size > string.bytesize) ? size : string.bytesize

  self.empty(size) do |buffer|
    buffer[size - string.bytesize, string.bytesize] = string
  end
end

.rpad(string, size, &block) ⇒ Object



37
38
39
# File 'lib/sodium/buffer.rb', line 37

def self.rpad(string, size, &block)
  self.ljust(string, string.bytesize + size, &block)
end

Instance Method Details

#+(bytes) ⇒ Object



98
99
100
101
102
103
# File 'lib/sodium/buffer.rb', line 98

def +(bytes)
  Sodium::Buffer.empty(self.bytesize + bytes.bytesize) do |buffer|
    buffer[0,             self .bytesize] = self
    buffer[self.bytesize, bytes.bytesize] = bytes
  end
end

#==(bytes) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/sodium/buffer.rb', line 85

def ==(bytes)
  bytes = Sodium::Buffer.new(bytes)

  return false unless
    self.bytesize == bytes.bytesize

  Sodium::FFI::Crypto.sodium_memcmp(
    self .to_ptr,
    bytes.to_ptr,
    bytes.bytesize
  ) == 0
end

#[](offset, size) ⇒ Object Also known as: byteslice



141
142
143
144
145
# File 'lib/sodium/buffer.rb', line 141

def [](offset, size)
  self.class.new(
    @bytes.get_bytes(offset, size)
  )
end

#[]=(offset, size, bytes) ⇒ Object

Raises:

  • (ArgumentError)


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/sodium/buffer.rb', line 121

def []=(offset, size, bytes)
  raise ArgumentError, %{must only assign to existing bytes in the buffer} unless
    self.bytesize >= offset + size

  raise ArgumentError, %{must reassign only a fixed number of bytes} unless
    size == bytes.bytesize

  # ensure the original bytes get cleared
  bytes = Sodium::Buffer.new(bytes)

  Sodium::FFI::Memory.sodium_memput(
    self .to_ptr,
    bytes.to_ptr,
    offset,
    size
  )

  true
end

#^(bytes) ⇒ Object

Raises:

  • (ArgumentError)


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/sodium/buffer.rb', line 105

def ^(bytes)
  bytes = Sodium::Buffer.new(bytes)

  raise ArgumentError, %{must only XOR strings of equal length} unless
    self.bytesize == bytes.bytesize

  Sodium::Buffer.empty(self.bytesize) do |buffer|
    Sodium::FFI::Memory.sodium_memxor(
      buffer.to_ptr,
      self  .to_ptr,
      bytes .to_ptr,
      bytes .bytesize
    )
  end
end

#bytesizeObject



149
150
151
# File 'lib/sodium/buffer.rb', line 149

def bytesize
  @bytesize
end

#inspectObject



161
162
163
164
165
# File 'lib/sodium/buffer.rb', line 161

def inspect
  # this appears to be equivalent to the default Object#inspect,
  # albeit without instance variables
  "#<%s:0x%x>" % [ self.class.name, self.__id__ * 2 ]
end

#ldrop(size) ⇒ Object



157
158
159
# File 'lib/sodium/buffer.rb', line 157

def ldrop(size)
  self[size, self.bytesize - size]
end

#rdrop(size) ⇒ Object



153
154
155
# File 'lib/sodium/buffer.rb', line 153

def rdrop(size)
  self[0, self.bytesize - size]
end

#to_ptrObject



180
181
182
# File 'lib/sodium/buffer.rb', line 180

def to_ptr
  @bytes
end

#to_sObject



167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/sodium/buffer.rb', line 167

def to_s
  # Pretend to return a string, but really return a Delegator that
  # wipes the string's memory when it gets garbage collected.
  #
  # Since any calls to the methods of the String inside the
  # delegator by necessity have the delegator's `method_missing` in
  # their backtrace, there can never be a situation where there is a
  # live pointer to the string itself but not one to the delegator.
  ZeroingDelegator.new(
    @bytes.read_bytes(@bytesize)
  )
end