RustCrypto / block-modes

Collection of generic block mode algorithms written in pure Rust
64 stars 13 forks source link

Implement belt-ctr #39

Closed makavity closed 1 year ago

makavity commented 1 year ago

Hello! Just implemented belt-ctr. Have questions about :)

  1. Am I need to implement BE Flavor?
  2. Should I implement seek and pos methods?

Thanks!

Closes #35

newpavlov commented 1 year ago

Am I need to implement BE Flavor?

You do not need flavors, since the standard fixes width and byte order.

Should I implement seek and pos methods?

Yes, it's worth to have them.

makavity commented 1 year ago

Hm, okay. So, should I also use cipher as generic?

newpavlov commented 1 year ago

Yes, it would be preferable. But you can make BeltBlock the default cipher. It can look something like this:

struct BeltCtr<C = BeltBlock>
where C: BlockEncrypt + BlockSizeUser<BlockSize = U16>
{ ... }
makavity commented 1 year ago

I don't know, should I completely remove nonce and use CtrNonce128 as struct type? Also, which way would be better to implement seek, if I use ctr like s variable from standard?

newpavlov commented 1 year ago

should I completely remove nonce and use CtrNonce128 as struct type?

Yes, simply use u128 for counter and initial s fields inside BeltCtr.

which way would be better to implement seek, if I use ctr like s variable from standard?

Implement StreamCipherSeekCore<Counter = u128>. By storing belt-block(S, K) + 1 in the initial s field (we count blocks from 0, the standard from 1), implementation of the methods should be trivial.

makavity commented 1 year ago

Is this fine? Or I should implement pos as additional param? Also, for StreamSeekCore, I need to impl StreamCipherCore, in which is process_with_backend, I don't use it. Can I left unreachable for this? :)

newpavlov commented 1 year ago

No, you should not implement StreamCipher directly. It's a byte level interface, which requires buffering of keystream blocks. Your current implementation will lose keystream bytes when fed messages with length not multiple of the block size.

You should implement StreamCipherCore and StreamSeekCore for BeltCtrCore. Then you can introduce pub type BeltCtr<C> = StreamCipherCoreWrapper<BeltCtrCore<C>>;. The wrapper will handle buffering and implementation of StreamCipher and StreamCipherSeek for the resulting type.

newpavlov commented 1 year ago

I have implemented the required traits. Our trait hierarchy is... a bit non-trivial, so it can be hard to write a proper implementation if you are not familiar with it. Take a look if you are interested and feel free to ask any questions about it.

makavity commented 1 year ago

That looks awesome, thank you for great example! I have a question about tests, how to get blob format for tests?

newpavlov commented 1 year ago

It's done using this utility in the blobby crate. For encoding you feed it a text file containing hex strings. Each line is an entry, stream ciphers require 4 entries per test vector (key, IV, plaintext, ciphertext).

makavity commented 1 year ago

Can you also tell me about remaining blocks and why it is implemented like this?

newpavlov commented 1 year ago

After generating u128::MAX blocks the keystream wraps around, which should not happen. Of course, it's impossible to generate so much data in practice, but with the seeking capability user may erroneously set keystream position near the end. So we calculate how much blocks were "generated" (the used variable), and then subtract it from the total number of blocks. This value is then used in the wrapper to return an error on potential keystream wrapping.