Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow shake128/256 to produce outputs of unlimited length #54406

Open
tianxiadys opened this issue Aug 16, 2024 · 6 comments
Open

Allow shake128/256 to produce outputs of unlimited length #54406

tianxiadys opened this issue Aug 16, 2024 · 6 comments
Labels
crypto Issues and PRs related to the crypto subsystem. feature request Issues that request new features to be added to Node.js.

Comments

@tianxiadys
Copy link

What is the problem this feature will solve?

SHAKE128 is essentially a hash algorithm that can output an infinite-length stream. Rather than being just a hash algorithm, it's more like a stream cipher such as RC4. Support for SHAKE128/256 is currently available, but the implementation requires specifying an outputSize in advance, which is fixed and limited. This doesn't meet my needs, so I have to rely on third-party libraries.

What is the feature you are proposing to solve the problem?

Support for SHAKE was first introduced in 2019, but I believe this support is incomplete.

#28757
#28805

What alternatives have you considered?

No response

@tianxiadys tianxiadys added the feature request Issues that request new features to be added to Node.js. label Aug 16, 2024
@github-project-automation github-project-automation bot moved this to Awaiting Triage in Node.js feature requests Aug 16, 2024
@tianxiadys
Copy link
Author

Allowing the digest method to be called multiple times in certain situations could be an option, such as adding a {streamMode: true} option. This approach seems clunky, but it could solve the problem.

@tianxiadys
Copy link
Author

Alternatively, a new method like digestAsStream(length: number) could be added. This method would represent a streaming hash, with the length parameter specifying the desired output length. It could be called an unlimited number of times.

@avivkeller avivkeller added the crypto Issues and PRs related to the crypto subsystem. label Aug 16, 2024
@avivkeller
Copy link
Member

Hi! Could you provide some example code of what it is that you are looking for?

@tniessen
Copy link
Member

I don't think there's much we can do here until EVP_DigestSqueeze() becomes available in the OpenSSL version that we are linking against, which might take a while.

@tianxiadys
Copy link
Author

I'm actually writing a Node.js version of the Xray-core library, which is an encryption proxy tool. Living in mainland China, it's an important tool for me. The library is poorly written; it implements a proxy protocol called VMess. In one part, it uses SHAKE128 to generate an infinite byte stream and XORs this stream with plaintext data to transmit over the network. This is definitely a misuse, as it incorrectly applies a hash algorithm as a stream cipher. However, I'm just replicating the existing protocol and need to find a function that supports this usage.

This is a quote from the original code.

https://github.com/XTLS/Xray-core/blob/main/proxy/vmess/encoding/auth.go

type ShakeSizeParser struct {
	shake  sha3.ShakeHash
	buffer [2]byte
}

func NewShakeSizeParser(nonce []byte) *ShakeSizeParser {
	shake := sha3.NewShake128()
	common.Must2(shake.Write(nonce))
	return &ShakeSizeParser{
		shake: shake,
	}
}

func (*ShakeSizeParser) SizeBytes() int32 {
	return 2
}

func (s *ShakeSizeParser) next() uint16 {
	common.Must2(s.shake.Read(s.buffer[:]))
	return binary.BigEndian.Uint16(s.buffer[:])
}

func (s *ShakeSizeParser) Decode(b []byte) (uint16, error) {
	mask := s.next()
	size := binary.BigEndian.Uint16(b)
	return mask ^ size, nil
}

func (s *ShakeSizeParser) Encode(size uint16, b []byte) []byte {
	mask := s.next()
	binary.BigEndian.PutUint16(b, mask^size)
	return b[:2]
}

func (s *ShakeSizeParser) NextPaddingLen() uint16 {
	return s.next() % 64
}

func (s *ShakeSizeParser) MaxPaddingLen() uint16 {
	return 64
}

@tniessen
Copy link
Member

This is definitely a misuse, as it incorrectly applies a hash algorithm as a stream cipher.

Side note: it's not an approved usage of an XOF, but it's not known to be vulnerable in Keccak's sponge construction either. It even was suggested in the 2011 paper on sponge function but has not been approved so far. In many ways, SHAKE is not a hash function but rather an XOF.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
crypto Issues and PRs related to the crypto subsystem. feature request Issues that request new features to be added to Node.js.
Projects
Status: Awaiting Triage
Development

No branches or pull requests

3 participants