...

Source file src/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted_test.go

Documentation: github.com/ProtonMail/go-crypto/openpgp/packet

     1  // Copyright (C) 2018 ProtonTech AG
     2  
     3  package packet
     4  
     5  import (
     6  	"bytes"
     7  	"crypto/rand"
     8  	"encoding/hex"
     9  	"io"
    10  	mathrand "math/rand"
    11  	"testing"
    12  
    13  	"github.com/ProtonMail/go-crypto/openpgp/errors"
    14  )
    15  
    16  // Note: This implementation does not produce packets with chunk sizes over
    17  // 1<<27, but can parse packets with chunk size octets up to 56 and decrypt
    18  // them within limits of the running system. See RFC4880bis, sec 5.16.
    19  var maxChunkSizeExp = 62
    20  
    21  const maxPlaintextLength = 1 << 18
    22  
    23  func TestAeadRFCParse(t *testing.T) {
    24  	for _, sample := range samplesAeadEncryptedDataPacket {
    25  		key, _ := hex.DecodeString(sample.cek)
    26  		packetBytes, _ := hex.DecodeString(sample.full)
    27  		packetReader := bytes.NewBuffer(packetBytes)
    28  		packet := new(AEADEncrypted)
    29  		ptype, _, contentsReader, err := readHeader(packetReader)
    30  		if ptype != packetTypeAEADEncrypted || err != nil {
    31  			t.Error("Error reading packet header")
    32  		}
    33  		if err = packet.parse(contentsReader); err != nil {
    34  			t.Error(err)
    35  		}
    36  		// decrypted plaintext can be read from 'rc'
    37  		rc, err := packet.decrypt(key)
    38  		if err != nil {
    39  			t.Error(err)
    40  		}
    41  		got, err := readDecryptedStream(rc)
    42  		if err != nil {
    43  			t.Error(err)
    44  		}
    45  
    46  		want, _ := hex.DecodeString(sample.plaintext)
    47  		if !bytes.Equal(got, want) {
    48  			t.Errorf("Error opening:\ngot\n%s\nwant\n%s", got, want)
    49  		}
    50  	}
    51  }
    52  
    53  // Test if it is possible to stream an empty plaintext correctly. For
    54  // compatibility with OpenPGPjs, if the stream has no contents, it has two
    55  // authentication tags: One for the empty chunk, and the final auth. tag. This
    56  // test also checks if it cannot decrypt a corrupt stream of empty plaintext.
    57  func TestAeadEmptyStream(t *testing.T) {
    58  	key := randomKey(16)
    59  	config := randomConfig()
    60  	raw, _, err := randomStream(key, 0, config)
    61  	if err != nil {
    62  		t.Error(err)
    63  	}
    64  	// Packet is ready.
    65  	corruptBytes := make([]byte, len(raw.Bytes()))
    66  	copy(corruptBytes, raw.Bytes())
    67  	for bytes.Equal(corruptBytes, raw.Bytes()) {
    68  		corruptBytes[mathrand.Intn(len(corruptBytes)-5)+5] = byte(mathrand.Intn(256))
    69  	}
    70  	corrupt := bytes.NewBuffer(corruptBytes)
    71  
    72  	// Decrypt correct stream
    73  	packet := new(AEADEncrypted)
    74  	ptype, _, contentsReader, err := readHeader(raw)
    75  	if ptype != packetTypeAEADEncrypted || err != nil {
    76  		t.Error("Error reading packet header")
    77  	}
    78  	if err = packet.parse(contentsReader); err != nil {
    79  		t.Error(err)
    80  	}
    81  	// decrypted plaintext can be read from 'rc'
    82  	rc, err := packet.decrypt(key)
    83  
    84  	_, err = readDecryptedStream(rc)
    85  	if err != nil {
    86  		t.Error(err)
    87  	}
    88  
    89  	// Decrypt corrupt stream
    90  	packet = new(AEADEncrypted)
    91  	ptype, _, contentsReader, err = readHeader(corrupt)
    92  	if ptype != packetTypeAEADEncrypted || err != nil {
    93  		t.Error("Error reading packet header")
    94  	}
    95  	if err = packet.parse(contentsReader); err != nil {
    96  		t.Error(err)
    97  	}
    98  	// decrypted plaintext can be read from 'rc'
    99  	rc, err = packet.decrypt(key)
   100  
   101  	_, err = readDecryptedStream(rc)
   102  	if err == nil {
   103  		t.Errorf("No error raised when reading corrupt stream with empty plaintext")
   104  	}
   105  }
   106  
   107  // Tests if encrypt/decrypt functions are callable and correct with a nil config
   108  func TestAeadNilConfigStream(t *testing.T) {
   109  	// Sample key
   110  	key := randomKey(16)
   111  	randomLength := mathrand.Intn(maxPlaintextLength) + 1
   112  	raw, plain, err := randomStream(key, randomLength, nil)
   113  	if err != nil {
   114  		t.Error(err)
   115  	}
   116  	// Packet is ready in 'raw'
   117  
   118  	packet := new(AEADEncrypted)
   119  
   120  	ptype, _, contentsReader, err := readHeader(raw)
   121  	if ptype != packetTypeAEADEncrypted || err != nil {
   122  		t.Error("Error reading packet header")
   123  	}
   124  
   125  	if err = packet.parse(contentsReader); err != nil {
   126  		t.Error(err)
   127  	}
   128  	// decrypted plaintext can be read from 'rc'
   129  	rc, err := packet.decrypt(key)
   130  
   131  	got, err := readDecryptedStream(rc)
   132  	if err != nil {
   133  		t.Error(err)
   134  	}
   135  
   136  	want := plain
   137  	if !bytes.Equal(got, want) {
   138  		t.Errorf("Error encrypting/decrypting random stream with nil config")
   139  	}
   140  }
   141  
   142  // Encrypts and decrypts a random stream, checking correctness and integrity
   143  func TestAeadStreamRandomizeSlow(t *testing.T) {
   144  	key := randomKey(16)
   145  	config := randomConfig()
   146  	randomLength := mathrand.Intn(maxPlaintextLength) + 1
   147  	raw, plain, err := randomStream(key, randomLength, config)
   148  	if err != nil {
   149  		t.Error(err)
   150  	}
   151  	// Packet is ready in 'raw'
   152  
   153  	packet := new(AEADEncrypted)
   154  	ptype, _, contentsReader, err := readHeader(raw)
   155  	if ptype != packetTypeAEADEncrypted || err != nil {
   156  		t.Error("Error reading packet header")
   157  	}
   158  
   159  	if err = packet.parse(contentsReader); err != nil {
   160  		t.Error(err)
   161  	}
   162  	// decrypted plaintext can be read from 'rc'
   163  	rc, err := packet.decrypt(key)
   164  
   165  	got, err := readDecryptedStream(rc)
   166  	if err != nil {
   167  		t.Error(err)
   168  	}
   169  	// Close MUST be called - it checks if the final chunk was witnessed
   170  	if err = rc.Close(); err != nil {
   171  		t.Error(err)
   172  	}
   173  	want := plain
   174  	if !bytes.Equal(got, want) {
   175  		t.Errorf("Error encrypting/decrypting random stream")
   176  	}
   177  }
   178  
   179  // Encrypts a random stream, corrupt some bytes, and check if it fails
   180  func TestAeadCorruptStreamRandomizeSlow(t *testing.T) {
   181  	key := randomKey(16)
   182  	config := randomConfig()
   183  	randomLength := mathrand.Intn(maxPlaintextLength) + 1
   184  	raw, plain, err := randomStream(key, randomLength, config)
   185  	if err != nil {
   186  		t.Error(err)
   187  	}
   188  
   189  	// Corrupt some bytes of the stream
   190  	for j := 0; j < 10; j++ {
   191  		index := mathrand.Intn(len(raw.Bytes()))
   192  		if index < 8 || len(plain) == 0 {
   193  			// avoid corrupting header or nonce, that's useless
   194  			continue
   195  		}
   196  		raw.Bytes()[index] = 255 - raw.Bytes()[index]
   197  	}
   198  	packet := new(AEADEncrypted)
   199  	ptype, _, contentsReader, err := readHeader(raw)
   200  	if ptype != packetTypeAEADEncrypted || err != nil {
   201  		t.Error("Error reading packet header")
   202  	}
   203  
   204  	if err = packet.parse(contentsReader); err != nil {
   205  		// Header was corrupted
   206  		return
   207  	}
   208  	rc, err := packet.decrypt(key)
   209  	got, err := readDecryptedStream(rc)
   210  	if err == nil || err == io.EOF {
   211  		t.Errorf("No error raised when decrypting corrupt stream")
   212  	}
   213  	if bytes.Equal(got, plain) {
   214  		t.Errorf("Error: Successfully decrypted corrupt stream")
   215  	}
   216  }
   217  
   218  // Encrypts a random stream, truncate the end, and check if it fails
   219  func TestAeadTruncatedStreamRandomizeSlow(t *testing.T) {
   220  	key := randomKey(16)
   221  	config := randomConfig()
   222  	randomLength := mathrand.Intn(maxPlaintextLength)
   223  	if randomLength < 16 {
   224  		return
   225  	}
   226  
   227  	raw, plain, err := randomStream(key, randomLength, config)
   228  	if err != nil {
   229  		t.Error(err)
   230  	}
   231  
   232  	// Truncate the packet by some bytes
   233  	var truncatedRaw []byte
   234  	cut := 0
   235  	for cut == 0 {
   236  		cut = mathrand.Intn(randomLength / 2)
   237  	}
   238  	truncatedRaw = raw.Bytes()[:len(raw.Bytes())-cut]
   239  	truncated := bytes.NewBuffer(truncatedRaw)
   240  
   241  	packet := new(AEADEncrypted)
   242  	ptype, _, truncatedContentsReader, err := readHeader(truncated)
   243  	if ptype != packetTypeAEADEncrypted || err != nil {
   244  		t.Error("Error reading packet header")
   245  	}
   246  
   247  	if err = packet.parse(truncatedContentsReader); err != nil {
   248  		t.Error(err)
   249  	}
   250  	rc, err := packet.decrypt(key)
   251  	if err != nil {
   252  		return
   253  	}
   254  	got, err := readDecryptedStream(rc)
   255  	if err == nil || err == io.EOF {
   256  		t.Errorf("No truncate error raised when decrypting truncated stream")
   257  	}
   258  	if bytes.Equal(got, plain) {
   259  		t.Errorf("Error: Successfully decrypted truncated stream")
   260  	}
   261  }
   262  
   263  // Encrypts a random stream, truncate the end, and check if it fails
   264  func TestAeadUnclosedStreamRandomizeSlow(t *testing.T) {
   265  	key := randomKey(16)
   266  	config := randomConfig()
   267  	ptLen := mathrand.Intn(maxPlaintextLength)
   268  	// Sample random plaintext of given length
   269  	plain := make([]byte, ptLen)
   270  	_, err := rand.Read(plain)
   271  	if err != nil {
   272  		t.Error(err)
   273  	}
   274  	// 'writeCloser' encrypts and writes the plaintext bytes.
   275  	rawCipher := bytes.NewBuffer(nil)
   276  	writeCloser, err := SerializeAEADEncrypted(rawCipher, key, config)
   277  	if err != nil {
   278  		t.Error(err)
   279  	}
   280  	// Write the partial lengths packet into 'raw'
   281  	if _, err = writeCloser.Write(plain); err != nil {
   282  		t.Error(err)
   283  	}
   284  	// Don't call Close
   285  
   286  	packet := new(AEADEncrypted)
   287  	_, _, contentsReader, err := readHeader(rawCipher)
   288  	if err != nil {
   289  		return
   290  	}
   291  
   292  	if err = packet.parse(contentsReader); err != nil {
   293  		return
   294  	}
   295  	rc, err := packet.decrypt(key)
   296  	if err != nil {
   297  		return
   298  	}
   299  	got, err := readDecryptedStream(rc)
   300  	if err == nil || err == io.EOF {
   301  		t.Errorf("No error raised when decrypting unclosed stream")
   302  	}
   303  	if bytes.Equal(got, plain) {
   304  		t.Errorf("Error: Successfully decrypted unclosed stream")
   305  	}
   306  }
   307  
   308  // ----------------------------------- //
   309  // -------       UTILS       --------- //
   310  // ----------------------------------- //
   311  
   312  func randomKey(length int) []byte {
   313  	key := make([]byte, length)
   314  	_, err := rand.Read(key)
   315  	if err != nil {
   316  		panic("can't read from rand")
   317  	}
   318  	return key
   319  }
   320  
   321  func randomConfig() *Config {
   322  	var aeadCompatibleCiphers = []CipherFunction{
   323  		CipherAES128,
   324  		CipherAES192,
   325  		CipherAES256,
   326  	}
   327  	var modes = []AEADMode{
   328  		AEADModeEAX,
   329  		AEADModeOCB,
   330  	}
   331  
   332  	// Random chunk size
   333  	chunkSizeExp := 6 + mathrand.Intn(maxChunkSizeExp-5)
   334  	chunkSize := uint64(1 << uint(chunkSizeExp))
   335  	// Random cipher and mode
   336  	ciph := aeadCompatibleCiphers[mathrand.Intn(len(aeadCompatibleCiphers))]
   337  	aeadConf := AEADConfig{
   338  		ChunkSize:   uint64(chunkSize),
   339  		DefaultMode: modes[mathrand.Intn(len(modes))],
   340  	}
   341  	config := &Config{
   342  		AEADConfig:    &aeadConf,
   343  		DefaultCipher: ciph,
   344  	}
   345  	return config
   346  }
   347  
   348  // Samples a random plaintext and gives the raw AEADEncrypted packet contents.
   349  // Returns said contents, the plaintext, and an error.
   350  func randomStream(key []byte, ptLen int, config *Config) (*bytes.Buffer, []byte, error) {
   351  	// Sample random plaintext of given length
   352  	plaintext := make([]byte, ptLen)
   353  	_, err := rand.Read(plaintext)
   354  	if err != nil {
   355  		return nil, nil, err
   356  	}
   357  
   358  	// 'writeCloser' encrypts and writes the plaintext bytes.
   359  	rawCipher := bytes.NewBuffer(nil)
   360  	writeCloser, err := SerializeAEADEncrypted(rawCipher, key, config)
   361  	if err != nil {
   362  		return nil, nil, err
   363  	}
   364  	// Write the partial lengths packet into 'raw'
   365  	_, err = writeCloser.Write(plaintext)
   366  	if err != nil {
   367  		return nil, nil, err
   368  	}
   369  	// Close MUST be called - it appends the final auth. tag
   370  	if err = writeCloser.Close(); err != nil {
   371  		return nil, nil, err
   372  	}
   373  	// Packet is ready.
   374  	return rawCipher, plaintext, nil
   375  }
   376  
   377  func readDecryptedStream(rc io.ReadCloser) (got []byte, err error) {
   378  	for {
   379  		// Read a random number of bytes, until the end of the packet.
   380  		decrypted := make([]byte, mathrand.Intn(200))
   381  		n, err := rc.Read(decrypted)
   382  		decrypted = decrypted[:n]
   383  		got = append(got, decrypted...)
   384  		if err != nil {
   385  			if err == io.EOF {
   386  				// Finished reading
   387  				break
   388  			} else {
   389  				// Something happened
   390  				return nil, err
   391  			}
   392  		}
   393  	}
   394  	return got, err
   395  }
   396  
   397  // SerializeAEADEncrypted initializes the aeadCrypter and returns a writer.
   398  // This writer encrypts and writes bytes (see aeadEncrypter.Write()).
   399  // This funcion is moved to the test suite to prevent it from creating this deprecated package
   400  func SerializeAEADEncrypted(w io.Writer, key []byte, config *Config) (io.WriteCloser, error) {
   401  	writeCloser := noOpCloser{w}
   402  	writer, err := serializeStreamHeader(writeCloser, packetTypeAEADEncrypted)
   403  	if err != nil {
   404  		return nil, err
   405  	}
   406  
   407  	// Data for en/decryption: tag, version, cipher, aead mode, chunk size
   408  	aeadConf := config.AEAD()
   409  	prefix := []byte{
   410  		0xD4,
   411  		aeadEncryptedVersion,
   412  		byte(config.Cipher()),
   413  		byte(aeadConf.Mode()),
   414  		aeadConf.ChunkSizeByte(),
   415  	}
   416  	n, err := writer.Write(prefix[1:])
   417  	if err != nil || n < 4 {
   418  		return nil, errors.AEADError("could not write AEAD headers")
   419  	}
   420  	// Sample nonce
   421  	nonceLen := aeadConf.Mode().IvLength()
   422  	nonce := make([]byte, nonceLen)
   423  	n, err = rand.Read(nonce)
   424  	if err != nil {
   425  		panic("Could not sample random nonce")
   426  	}
   427  	_, err = writer.Write(nonce)
   428  	if err != nil {
   429  		return nil, err
   430  	}
   431  	blockCipher := CipherFunction(config.Cipher()).new(key)
   432  	alg := aeadConf.Mode().new(blockCipher)
   433  
   434  	chunkSize := decodeAEADChunkSize(aeadConf.ChunkSizeByte())
   435  	return &aeadEncrypter{
   436  		aeadCrypter: aeadCrypter{
   437  			aead:           alg,
   438  			chunkSize:      chunkSize,
   439  			associatedData: prefix,
   440  			chunkIndex:     make([]byte, 8),
   441  			initialNonce:   nonce,
   442  			packetTag:      packetTypeAEADEncrypted,
   443  		},
   444  		writer: writer,
   445  	}, nil
   446  }
   447  

View as plain text