Conversation
Codecov ReportPatch coverage has no change and project coverage change:
📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more Additional details and impacted files@@ Coverage Diff @@
## main #141 +/- ##
==========================================
- Coverage 60.14% 60.11% -0.04%
==========================================
Files 20 20
Lines 3450 3452 +2
==========================================
Hits 2075 2075
- Misses 1079 1081 +2
Partials 296 296
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
store/dhash/dhash.go
Outdated
| // Nonce is derived from the mulithash (passpharase) so that encrypted payloads | ||
| // for the same multihash can be compared to each other without having to decrypt | ||
| nonce := SHA256(passphrase, nil)[:nonceLen] | ||
| nonce := SHA256(append(noncePrefix, passphrase...), nil)[:nonceLen] |
There was a problem hiding this comment.
AFAIT this means that two different peer will use the same nonce (since it is just a salted hash of the multihash). Assuming this is true:
This is not OK, because then an attacker can use multiple different provider records from multiple different nodes using the same nonce to recover the derived key, which is the multihash. (the actual attack relies on cipher textes being different, here the cipher text should be the peer id)
If this what the spec says, the spec needs to be updated.
You can search: aes gcm duplicated nonce this is a well known attack.
Other symetric encryptions constructions supports using duplicated nonce with minimal issue (attackers can match duplicates without revealing their content, either based on blocks of the stream without feedback, or until the first duplicate is found if there is feedback).
We are not using this here tho, and this is unsafe.
cc @guillaumemichel we had brainstorming together around this, I was maybe not clear at the end but this was assuming the nonce was salted with the cipher text and we don't mind duplicates being matched. not related
There was a problem hiding this comment.
Responding to:
We derive nonse from the passphrase so that we can deterministically produce the same result of
hash(mh) -> EncPeerIDgiven the same multihash / peerID.
You need to salt this by the peer id then:
nonce := SHA256(append(append(binary.AppendUvarint(noncePrefix, uint64(len(payload))), payload...)), passphrase...))There was a problem hiding this comment.
Thanks for pointing this out - will push a fix in shortly
There was a problem hiding this comment.
Btw I can't comment on untouched code, but you should really make:
func SHA256(dest []byte, payloads ...[]byte) []byte {
h := sha256.New()
for _, payload := range payloads {
h.Write(payload)
}
return h.Sum(dest)
}This will allow to rewrite:
nonce := SHA256(append(append(binary.AppendUint64(noncePrefix, uint64(len(payload))), payload...)), passphrase...))into the much more readable:
nonce := SHA256(nil, noncePrefix, binary.Uvarint(uint64(len(payload))), payload, passphrase)Co-authored-by: Andrew Gillis <gammazero@users.noreply.github.com>
| nonce := SHA256(passphrase, nil)[:nonceLen] | ||
| // Nonce is derived from the passphrase concatenated with the payload so that the encrypted payloads | ||
| // for the same multihash can be compared to each other without having to decrypt them, as it's not possible. | ||
| payloadLen := make([]byte, 8) |
There was a problem hiding this comment.
Concerning the payloadLen, the exact format isn't defined yet in ipfs/specs#373.
Would it make more sense to use a constant byte array (e.g make([]byte, 8)) or to use an unsigned varint. As we mostly encrypt peerids, that are quite short, we don't always need a big number for the payload len. A varint allows to use less bytes to describe the payload len, and the maximal payload len can also be very large if needed.
I think that I am more in favor of an unsigned varint format for the payload len, as it gives more agility. WDYT?
There was a problem hiding this comment.
we don't always need a big number for the payload len
These values are hashed over, so payloadLen isn't going to be a part of the encrypted value, i.e. see line 79: nonce := sha256Multiple(nil, noncePrefix, payloadLen, payload, passphrase)[:nonceLen].
Does that make sense?
There was a problem hiding this comment.
Alright, all good! 👍🏻
| nonce := sha256Multiple(nil, noncePrefix, payloadLen, payload, passphrase)[:nonceLen] | ||
|
|
||
| // Create cypher and seal the data | ||
| block, err := aes.NewCipher(derivedKey) |
There was a problem hiding this comment.
The format for Encrypted PeerID/Metadata was recently updated in the DHT Spec. The format should be [encryption_varint, payload_len, nonce, encrypted_payload]. See ipfs/specs#373
The encryption varint for aes-gcm-256 is 0x8040 and the multicodec is 0x2000 (See multiformats/multicodec#314 (comment))
There was a problem hiding this comment.
Great point! As IPNI doesn't have anything stored after EncPeerID the encryption_varint and payload_len can be appended on retrieval.
There was a problem hiding this comment.
Perfect, just wanted to make sure we are on the same page 😄
Co-authored-by: Guillaume Michel - guissou <guillaumemichel@users.noreply.github.com>
Co-authored-by: Guillaume Michel - guissou <guillaumemichel@users.noreply.github.com>
Co-authored-by: Guillaume Michel - guissou <guillaumemichel@users.noreply.github.com>
Co-authored-by: Guillaume Michel - guissou <guillaumemichel@users.noreply.github.com>
Co-authored-by: Guillaume Michel - guissou <guillaumemichel@users.noreply.github.com>
Co-authored-by: Guillaume Michel - guissou <guillaumemichel@users.noreply.github.com>
Co-authored-by: Guillaume Michel - guissou <guillaumemichel@users.noreply.github.com>
Co-authored-by: Guillaume Michel - guissou <guillaumemichel@users.noreply.github.com>
passphrase || payloadinstead of justpassphraseto avoid attacks on gcm with repeated nonces (example).