Implement a secure ICS protocol targeting LoRa Node151 microcontroller for controlling irrigation.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

172 lines
6.4 KiB

  1. """
  2. An example implementation of STROBE. Doesn't include the key tree.
  3. Copyright (c) Mike Hamburg, Cryptography Research, 2016.
  4. I will need to contact legal to get a license for this; in the mean time it is
  5. for example purposes only.
  6. """
  7. from __future__ import absolute_import
  8. from Strobe.Keccak import KeccakF
  9. class AuthenticationFailed(Exception):
  10. """Thrown when a MAC fails."""
  11. pass
  12. I,A,C,T,M,K = 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5
  13. class Strobe(object):
  14. def __init__(self, proto, F = KeccakF(1600), security = 128, copy_of=None, doInit=True):
  15. if copy_of is None:
  16. self.pos = self.posbegin = 0
  17. self.I0 = None
  18. self.F = F
  19. self.R = F.nbytes - security//4
  20. # Domain separation doesn't use Strobe padding
  21. self.initialized = False
  22. self.st = bytearray(F.nbytes)
  23. domain = bytearray([1,self.R,1,0,1,12*8]) \
  24. + bytearray(b"STROBEv1.0.2")
  25. if doInit: self._duplex(domain, forceF=True)
  26. # cSHAKE separation is done.
  27. # Turn on Strobe padding and do per-proto separation
  28. self.R -= 2
  29. self.initialized = True
  30. if doInit: self.operate(A|M, proto)
  31. else:
  32. self.R,self.pos,self.posbegin,self.I0,self.F = \
  33. (copy_of.R,copy_of.pos,copy_of.posbegin,copy_of.I0,
  34. copy_of.F.copy())
  35. self.st = bytearray(copy_of.st)
  36. self.initialized = True
  37. def copy(self): return Strobe(None,copy_of=self)
  38. def deepcopy(self): return self.copy()
  39. def set_state_from(self, obj):
  40. self.R,self.pos,self.posbegin,self.I0,self.F = \
  41. (obj.R,obj.pos,obj.posbegin,obj.I0,
  42. obj.F.copy())
  43. self.st = bytearray(obj.st)
  44. self.initialized = True
  45. def _runF(self):
  46. if self.initialized:
  47. self.st[self.pos] ^= self.posbegin
  48. self.st[self.pos+1] ^= 0x04
  49. self.st[self.R+1] ^= 0x80
  50. self.st = self.F(self.st)
  51. self.pos = self.posbegin = 0
  52. def _duplex(self, data, cbefore=False, cafter=False, forceF=False):
  53. assert not (cbefore and cafter)
  54. # Copy data, and convert string or int to bytearray
  55. # This converts an integer n to an array of n zeros
  56. data = bytearray(data)
  57. for i in range(len(data)):
  58. if cbefore: data[i] ^= self.st[self.pos]
  59. self.st[self.pos] ^= data[i]
  60. if cafter: data[i] = self.st[self.pos]
  61. self.pos += 1
  62. if self.pos == self.R: self._runF()
  63. if forceF and self.pos != 0: self._runF()
  64. return data
  65. def _beginOp(self, flags):
  66. # Adjust direction information so that sender and receiver agree
  67. if flags & T:
  68. if self.I0 is None: self.I0 = flags & I
  69. flags ^= self.I0
  70. # Update posbegin
  71. oldbegin, self.posbegin = self.posbegin, self.pos+1
  72. self._duplex([oldbegin,flags], forceF = flags&(C|K))
  73. def operate(self, flags, data, more=False, meta_flags=A|M, metadata=None):
  74. """
  75. STROBE main duplexing mode.
  76. Op is a byte which describes the operating mode, per the STROBE paper.
  77. Data is either a string or bytearray of data, or else a length. If it
  78. is given as a length, the data is that many bytes of zeros.
  79. If metadata is not None, first apply the given metadata in the given
  80. meta_op.
  81. STROBE operations are streamable. If more is true, this operation
  82. continues the previous operation. It therefore ignores metadata and
  83. doesn't use the beginOp code from the paper.
  84. Certain operations return data. If an operation returns no data
  85. (for example, AD and KEY don't return any data), it returns the empty
  86. byte array.
  87. The meta-operation might also return data. This is convenient for
  88. explicit framing (meta_op = 0b11010/0b11011) or encrypted explicit
  89. framing (meta_op = 0b11110/0b11111)
  90. If the operation is a MAC verification, this function returns the
  91. empty byte array (plus any metadata returned) on success, and throws
  92. AuthenticationFailed on failure.
  93. """
  94. assert not (flags & (K|1<<6|1<<7)) # Not implemented here
  95. meta_out = bytearray()
  96. if more:
  97. assert flags == self.cur_flags
  98. else:
  99. if metadata is not None:
  100. meta_out = self.operate(meta_flags, metadata)
  101. self._beginOp(flags)
  102. self.cur_flags = flags
  103. if (flags & (I|T) != (I|T)) and (flags & (I|A) != A):
  104. # Operation takes no input
  105. assert isinstance(data,int)
  106. # The actual processing code is just duplex
  107. cafter = (flags & (C|I|T)) == (C|T)
  108. cbefore = (flags & C) and not cafter
  109. processed = self._duplex(data, cbefore, cafter)
  110. # Determine what to do with the output.
  111. if (flags & (I|A)) == (I|A):
  112. # Return data to the application
  113. return meta_out + processed
  114. elif (flags & (I|T)) == T:
  115. # Return data to the transport.
  116. # A fancier implementation might send it directly.
  117. return meta_out + processed
  118. elif (flags & (I|A|T)) == (I|T):
  119. # Check MAC
  120. assert not more
  121. failures = 0
  122. for byte in processed: failures |= byte
  123. if failures != 0: raise AuthenticationFailed()
  124. return meta_out
  125. else:
  126. # Operation has no output data, but maybe output metadata
  127. return meta_out
  128. def ad (self,data, **kw): return self.operate(0b0010,data,**kw)
  129. def key (self,data, **kw): return self.operate(0b0110,data,**kw)
  130. def prf (self,data, **kw): return self.operate(0b0111,data,**kw)
  131. def send_clr(self,data, **kw): return self.operate(0b1010,data,**kw)
  132. def recv_clr(self,data, **kw): return self.operate(0b1011,data,**kw)
  133. def send_enc(self,data, **kw): return self.operate(0b1110,data,**kw)
  134. def recv_enc(self,data, **kw): return self.operate(0b1111,data,**kw)
  135. def send_mac(self,data=16,**kw): return self.operate(0b1100,data,**kw)
  136. def recv_mac(self,data ,**kw): return self.operate(0b1101,data,**kw)
  137. def ratchet (self,data=32,**kw): return self.operate(0b0100,data,**kw)