diff --git a/cmd/ncrypt/main.go b/cmd/ncrypt/main.go index 3879790..87ac0e9 100644 --- a/cmd/ncrypt/main.go +++ b/cmd/ncrypt/main.go @@ -152,7 +152,7 @@ func cipherSuites() []byte { case "": return []byte{} // use platform specific cipher case "AES256": - return []byte{sio.AES_256_GCM} + return []byte{sio.AES_GCM} case "C20P1305": return []byte{sio.CHACHA20_POLY1305} } @@ -196,21 +196,21 @@ func parseIOArgs() (*os.File, *os.File) { } func readPassword(src *os.File) []byte { - state, err := term.GetState(int(src.Fd())) + state, err := term.GetState(int(src.Fd())) // #nosec G115 if err != nil { fmt.Fprintln(os.Stderr, "Failed to read password:", err) exit(codeError) } cleanFn = append(cleanFn, func(code int) { - stat, _ := term.GetState(int(src.Fd())) + stat, _ := term.GetState(int(src.Fd())) // #nosec G115 if code == codeCancel && stat != nil && *stat != *state { _, _ = fmt.Fprintln(src, "\nFailed to read password: Interrupted") } - _ = term.Restore(int(src.Fd()), state) + _ = term.Restore(int(src.Fd()), state) // #nosec G115 }) _, _ = fmt.Fprint(src, "Enter password:") - password, err := term.ReadPassword(int(src.Fd())) + password, err := term.ReadPassword(int(src.Fd())) // #nosec G115 if err != nil { fmt.Fprintln(os.Stderr, "Failed to read password:", err) exit(codeError) diff --git a/dare_test.go b/dare_test.go index 3289d34..1e63673 100644 --- a/dare_test.go +++ b/dare_test.go @@ -30,6 +30,44 @@ func isZero(p []byte) bool { return true } +func TestSealOpen(t *testing.T) { + key128, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f") + key256, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + randValue, _ := hex.DecodeString("0001020304050607") + + for _, maxVersion := range []byte{Version10, Version20} { + for _, key := range [][]byte{key128, key256} { + config := Config{ + MinVersion: Version10, + MaxVersion: maxVersion, + CipherSuites: []byte{AES_GCM, CHACHA20_POLY1305}, + Key: key, + Rand: bytes.NewReader(randValue), + PayloadSize: maxPayloadSize, + } + plaintext := make([]byte, maxPayloadSize-1) + ciphertext := make([]byte, maxPayloadSize-1+32) + + enc, err := newAuthEncV10(&config) + if err != nil { + t.Errorf("Failed to create authenticated encryption scheme: %v", err) + } + enc.Seal(ciphertext, plaintext) + + dec, err := newAuthDecV10(&config) + if err != nil { + t.Errorf("Failed to create authenticated decryption scheme: %v", err) + } + if err = dec.Open(plaintext, ciphertext); err != nil { + t.Errorf("Failed to open ciphertext: %v", err) + } + if !isZero(plaintext) { + t.Errorf("Decryption failed") + } + } + } +} + func TestSealV10(t *testing.T) { key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") randValue, _ := hex.DecodeString("0001020304050607") @@ -69,7 +107,7 @@ func TestOpenV10(t *testing.T) { config := Config{ MinVersion: Version10, MaxVersion: Version10, - CipherSuites: []byte{AES_256_GCM, CHACHA20_POLY1305}, + CipherSuites: []byte{AES_GCM, CHACHA20_POLY1305}, Key: key, SequenceNumber: uint32(i), //nolint:gosec // Test loop counter Rand: rand.Reader, @@ -160,7 +198,7 @@ func TestOpenV20(t *testing.T) { config := Config{ MinVersion: Version20, MaxVersion: Version20, - CipherSuites: []byte{AES_256_GCM, CHACHA20_POLY1305}, + CipherSuites: []byte{AES_GCM, CHACHA20_POLY1305}, Key: key, SequenceNumber: uint32(i), //nolint:gosec // Test loop counter Rand: rand.Reader, @@ -188,7 +226,7 @@ func TestOpenV20(t *testing.T) { config := Config{ MinVersion: Version20, MaxVersion: Version20, - CipherSuites: []byte{AES_256_GCM, CHACHA20_POLY1305}, + CipherSuites: []byte{AES_GCM, CHACHA20_POLY1305}, Key: key, SequenceNumber: uint32(i), //nolint:gosec // Test loop counter Rand: rand.Reader, diff --git a/fuzz_test.go b/fuzz_test.go index 6308ac4..a8fc84e 100644 --- a/fuzz_test.go +++ b/fuzz_test.go @@ -39,7 +39,7 @@ func FuzzEncryptDecrypt(f *testing.F) { } // Test both cipher suites - for _, cipher := range []byte{AES_256_GCM, CHACHA20_POLY1305} { + for _, cipher := range []byte{AES_GCM, CHACHA20_POLY1305} { config := Config{ Key: key[:], CipherSuites: []byte{cipher}, diff --git a/sio.go b/sio.go index 8f379dd..8d2915d 100644 --- a/sio.go +++ b/sio.go @@ -38,8 +38,8 @@ const ( ) const ( - // AES_256_GCM specifies the cipher suite AES-GCM with 256 bit keys. - AES_256_GCM byte = iota + // AES_GCM specifies the cipher suite AES-GCM with 128 or 256 bit keys. + AES_GCM byte = iota // CHACHA20_POLY1305 specifies the cipher suite ChaCha20Poly1305 with 256 bit keys. CHACHA20_POLY1305 ) @@ -66,8 +66,6 @@ func detectAESSupport() bool { } const ( - keySize = 32 - headerSize = 16 maxPayloadSize = 1 << 16 tagSize = 16 @@ -77,17 +75,32 @@ const ( maxEncryptedSize = maxDecryptedSize + ((headerSize + tagSize) * 1 << 32) ) -var newAesGcm = func(key []byte) (cipher.AEAD, error) { - aes256, err := aes.NewCipher(key) +var newAES = func(key []byte) (cipher.AEAD, error) { + block, err := aes.NewCipher(key) if err != nil { return nil, err } - return cipher.NewGCM(aes256) + return cipher.NewGCM(block) +} + +var newChaCha20 = func(key []byte) (cipher.AEAD, error) { + // ChaCha20 requires 256 bit keys. When a 128 bit key K128 is + // provided, we expand it to a 256 bit key K256 = K128 | K128. + // This is acceptable for ChaCha20 since it has no other requirements + // on its key apart from being 256 bit long and unknown to resp. + // unpredictable for an adversary. + if len(key) == 128/8 { + k := make([]byte, 0, 256/8) + k = append(k, key...) + k = append(k, key...) + key = k + } + return chacha20poly1305.New(key) } var supportedCiphers = [...]func([]byte) (cipher.AEAD, error){ - AES_256_GCM: newAesGcm, - CHACHA20_POLY1305: chacha20poly1305.New, + AES_GCM: newAES, + CHACHA20_POLY1305: newChaCha20, } var ( @@ -336,9 +349,9 @@ func DecryptWriter(dst io.Writer, config Config) (io.WriteCloser, error) { func defaultCipherSuites() []byte { if supportsAES { - return []byte{AES_256_GCM, CHACHA20_POLY1305} + return []byte{AES_GCM, CHACHA20_POLY1305} } - return []byte{CHACHA20_POLY1305, AES_256_GCM} + return []byte{CHACHA20_POLY1305, AES_GCM} } func setConfigDefaults(config *Config) error { @@ -348,7 +361,7 @@ func setConfigDefaults(config *Config) error { if config.MaxVersion > Version20 { return errors.New("sio: unknown maximum version") } - if len(config.Key) != keySize { + if len(config.Key) != 128/8 && len(config.Key) != 256/8 { return errors.New("sio: invalid key size") } if len(config.CipherSuites) > 2 {