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.
 
 
 
 
 
 

165 lines
6.2 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("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. def copy(self): return Strobe(None,copy_of=self)
  37. def deepcopy(self): return self.copy()
  38. def _runF(self):
  39. if self.initialized:
  40. self.st[self.pos] ^= self.posbegin
  41. self.st[self.pos+1] ^= 0x04
  42. self.st[self.R+1] ^= 0x80
  43. self.st = self.F(self.st)
  44. self.pos = self.posbegin = 0
  45. def _duplex(self, data, cbefore=False, cafter=False, forceF=False):
  46. assert not (cbefore and cafter)
  47. # Copy data, and convert string or int to bytearray
  48. # This converts an integer n to an array of n zeros
  49. data = bytearray(data)
  50. for i in range(len(data)):
  51. if cbefore: data[i] ^= self.st[self.pos]
  52. self.st[self.pos] ^= data[i]
  53. if cafter: data[i] = self.st[self.pos]
  54. self.pos += 1
  55. if self.pos == self.R: self._runF()
  56. if forceF and self.pos != 0: self._runF()
  57. return data
  58. def _beginOp(self, flags):
  59. # Adjust direction information so that sender and receiver agree
  60. if flags & T:
  61. if self.I0 is None: self.I0 = flags & I
  62. flags ^= self.I0
  63. # Update posbegin
  64. oldbegin, self.posbegin = self.posbegin, self.pos+1
  65. self._duplex([oldbegin,flags], forceF = flags&(C|K))
  66. def operate(self, flags, data, more=False, meta_flags=A|M, metadata=None):
  67. """
  68. STROBE main duplexing mode.
  69. Op is a byte which describes the operating mode, per the STROBE paper.
  70. Data is either a string or bytearray of data, or else a length. If it
  71. is given as a length, the data is that many bytes of zeros.
  72. If metadata is not None, first apply the given metadata in the given
  73. meta_op.
  74. STROBE operations are streamable. If more is true, this operation
  75. continues the previous operation. It therefore ignores metadata and
  76. doesn't use the beginOp code from the paper.
  77. Certain operations return data. If an operation returns no data
  78. (for example, AD and KEY don't return any data), it returns the empty
  79. byte array.
  80. The meta-operation might also return data. This is convenient for
  81. explicit framing (meta_op = 0b11010/0b11011) or encrypted explicit
  82. framing (meta_op = 0b11110/0b11111)
  83. If the operation is a MAC verification, this function returns the
  84. empty byte array (plus any metadata returned) on success, and throws
  85. AuthenticationFailed on failure.
  86. """
  87. assert not (flags & (K|1<<6|1<<7)) # Not implemented here
  88. meta_out = bytearray()
  89. if more:
  90. assert flags == self.cur_flags
  91. else:
  92. if metadata is not None:
  93. meta_out = self.operate(meta_flags, metadata)
  94. self._beginOp(flags)
  95. self.cur_flags = flags
  96. if (flags & (I|T) != (I|T)) and (flags & (I|A) != A):
  97. # Operation takes no input
  98. assert isinstance(data,int)
  99. # The actual processing code is just duplex
  100. cafter = (flags & (C|I|T)) == (C|T)
  101. cbefore = (flags & C) and not cafter
  102. processed = self._duplex(data, cbefore, cafter)
  103. # Determine what to do with the output.
  104. if (flags & (I|A)) == (I|A):
  105. # Return data to the application
  106. return meta_out + processed
  107. elif (flags & (I|T)) == T:
  108. # Return data to the transport.
  109. # A fancier implementation might send it directly.
  110. return meta_out + processed
  111. elif (flags & (I|A|T)) == (I|T):
  112. # Check MAC
  113. assert not more
  114. failures = 0
  115. for byte in processed: failures |= byte
  116. if failures != 0: raise AuthenticationFailed()
  117. return meta_out
  118. else:
  119. # Operation has no output data, but maybe output metadata
  120. return meta_out
  121. def ad (self,data, **kw): return self.operate(0b0010,data,**kw)
  122. def key (self,data, **kw): return self.operate(0b0110,data,**kw)
  123. def prf (self,data, **kw): return self.operate(0b0111,data,**kw)
  124. def send_clr(self,data, **kw): return self.operate(0b1010,data,**kw)
  125. def recv_clr(self,data, **kw): return self.operate(0b1011,data,**kw)
  126. def send_enc(self,data, **kw): return self.operate(0b1110,data,**kw)
  127. def recv_enc(self,data, **kw): return self.operate(0b1111,data,**kw)
  128. def send_mac(self,data=16,**kw): return self.operate(0b1100,data,**kw)
  129. def recv_mac(self,data ,**kw): return self.operate(0b1101,data,**kw)
  130. def ratchet (self,data=32,**kw): return self.operate(0b0100,data,**kw)