Skip to content
This repository was archived by the owner on Feb 8, 2023. It is now read-only.
This repository was archived by the owner on Feb 8, 2023. It is now read-only.

Shared Secret constructions for Private Networks #177

Open
@jbenet

Description

@jbenet

Caveats:

  • this is a very basic encryption to segregate libp2p nodes into separate networks, using a network wide shared secret
  • this is not meant to be highly secure, as the underlying connection will run its own online encryption handshake, with authenticated encryption, ephemeral keys, forward secrecy, and so on.
  • this wrapper is merely to prevent nodes from connecting to each other when they are in different private networks.
  • Thus the threat model is:
    • we do not need authentication (bit flipping is ok, as it will be detected in the underlying cipher)
    • we do not need to protect against replay (replay will be detected/discarded in underlying cipher)
    • we use a network-wide shared secret and re-key when revoking access to a single node
    • we must protect against secret extraction (do not exchange the secret)
    • we must protect against ciphertext collection to derive the key/shared secret (ie derive per-session keys, use random IVs/nonces)
  • WARNING: i have not studied these carefully. this is notes. these may be broken. need review.

Construction 1:

  • 0-RTT
  • basic shard secret encryption
  • individual session keys
  • not safe to replay (fine because internal stream will encrypt with an online key echange, and a session key)
  • not authenticated, can flip bits (fine because internal stream will encrypt with AEAD)
  • no MAC overhead
|| = concat

SS = <shared secret>
conn = <plaintext connection>

N = randomNonce(32) # 256 bits
LK = LocalPublicKey() # use only if remote has it available already. (wouldn't allow connecting to unknown nodes to identify them via secio)
SK = sha2-256(SS || LK || N)  # session key
SC = AESCTR(SK) or CHACHA(SK) # stream cipher

conn.Write(N)
for {
  data := getOutgoingData()
  SC.XORKeyStream(data, data)
  conn.Write(data)
}

Though really:

for {  # AES has a 64GB key limit
  N = randomNonce(32) # 256 bits
  SK = sha2-256(SS || LK || N) # session key

  conn.Write(N)
  SC = AESCTR(SK) or CHACHA(SK)
  SW = cipher.StreamWriter{S: SC, W: conn} # XORs using given cipher

  written := 0
  for written < 63GB { # AES must be re-keyed after 64GB.
    data = getOutgoingData()
    n, _ = SW.Write(data)
    written += n
  }
}

Construction 2:

  • 1-RTT
  • basic shared secret encryption
  • individual session keys
  • safe to replay
  • not authenticated, can flip bits (fine because internal stream will encrypt with AEAD)
  • no MAC overhead
SS = <shared secret>
conn = <plaintext connection>

N1, N2 := make([]byte, 32) # 256 bits
N1 = randomNonce(32)
conn.Write(N1)
conn.Read(N2)
N = sortAndConcat(N1, N2) # to disregard order.

LK = localPublicKey()
RK = remotePublicKey() # only available if we know it beforehand. this is pre-secio
PKs = sortAndConcat(LK, RK) # to disregard order.
SK = sha2-256(SS || PKs || N)        # session key
SC = AESCTR(SK) or CHACHA(SK) # stream cipher

for {
  data := getOutgoingData()
  SC.XORKeyStream(data, data)
  conn.Write(data)
}

Construction 3:

  • 1-RTT
  • AEAD (AES-GCM or ChaCha/Poly1305)
  • individual session keys
  • safe to replay
  • authenticated
  • another MAC overhead (oof!)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions