...

Source file src/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go

Documentation: golang.org/x/crypto/openpgp/packet

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package packet
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/cipher"
    10  	"io"
    11  	"strconv"
    12  
    13  	"golang.org/x/crypto/openpgp/errors"
    14  	"golang.org/x/crypto/openpgp/s2k"
    15  )
    16  
    17  // This is the largest session key that we'll support. Since no 512-bit cipher
    18  // has even been seriously used, this is comfortably large.
    19  const maxSessionKeySizeInBytes = 64
    20  
    21  // SymmetricKeyEncrypted represents a passphrase protected session key. See RFC
    22  // 4880, section 5.3.
    23  type SymmetricKeyEncrypted struct {
    24  	CipherFunc   CipherFunction
    25  	s2k          func(out, in []byte)
    26  	encryptedKey []byte
    27  }
    28  
    29  const symmetricKeyEncryptedVersion = 4
    30  
    31  func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
    32  	// RFC 4880, section 5.3.
    33  	var buf [2]byte
    34  	if _, err := readFull(r, buf[:]); err != nil {
    35  		return err
    36  	}
    37  	if buf[0] != symmetricKeyEncryptedVersion {
    38  		return errors.UnsupportedError("SymmetricKeyEncrypted version")
    39  	}
    40  	ske.CipherFunc = CipherFunction(buf[1])
    41  
    42  	if ske.CipherFunc.KeySize() == 0 {
    43  		return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1])))
    44  	}
    45  
    46  	var err error
    47  	ske.s2k, err = s2k.Parse(r)
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	encryptedKey := make([]byte, maxSessionKeySizeInBytes)
    53  	// The session key may follow. We just have to try and read to find
    54  	// out. If it exists then we limit it to maxSessionKeySizeInBytes.
    55  	n, err := readFull(r, encryptedKey)
    56  	if err != nil && err != io.ErrUnexpectedEOF {
    57  		return err
    58  	}
    59  
    60  	if n != 0 {
    61  		if n == maxSessionKeySizeInBytes {
    62  			return errors.UnsupportedError("oversized encrypted session key")
    63  		}
    64  		ske.encryptedKey = encryptedKey[:n]
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  // Decrypt attempts to decrypt an encrypted session key and returns the key and
    71  // the cipher to use when decrypting a subsequent Symmetrically Encrypted Data
    72  // packet.
    73  func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) {
    74  	key := make([]byte, ske.CipherFunc.KeySize())
    75  	ske.s2k(key, passphrase)
    76  
    77  	if len(ske.encryptedKey) == 0 {
    78  		return key, ske.CipherFunc, nil
    79  	}
    80  
    81  	// the IV is all zeros
    82  	iv := make([]byte, ske.CipherFunc.blockSize())
    83  	c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv)
    84  	plaintextKey := make([]byte, len(ske.encryptedKey))
    85  	c.XORKeyStream(plaintextKey, ske.encryptedKey)
    86  	cipherFunc := CipherFunction(plaintextKey[0])
    87  	if cipherFunc.blockSize() == 0 {
    88  		return nil, ske.CipherFunc, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
    89  	}
    90  	plaintextKey = plaintextKey[1:]
    91  	if l, cipherKeySize := len(plaintextKey), cipherFunc.KeySize(); l != cipherFunc.KeySize() {
    92  		return nil, cipherFunc, errors.StructuralError("length of decrypted key (" + strconv.Itoa(l) + ") " +
    93  			"not equal to cipher keysize (" + strconv.Itoa(cipherKeySize) + ")")
    94  	}
    95  	return plaintextKey, cipherFunc, nil
    96  }
    97  
    98  // SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w. The
    99  // packet contains a random session key, encrypted by a key derived from the
   100  // given passphrase. The session key is returned and must be passed to
   101  // SerializeSymmetricallyEncrypted.
   102  // If config is nil, sensible defaults will be used.
   103  func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) {
   104  	cipherFunc := config.Cipher()
   105  	keySize := cipherFunc.KeySize()
   106  	if keySize == 0 {
   107  		return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
   108  	}
   109  
   110  	s2kBuf := new(bytes.Buffer)
   111  	keyEncryptingKey := make([]byte, keySize)
   112  	// s2k.Serialize salts and stretches the passphrase, and writes the
   113  	// resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf.
   114  	err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, &s2k.Config{Hash: config.Hash(), S2KCount: config.PasswordHashIterations()})
   115  	if err != nil {
   116  		return
   117  	}
   118  	s2kBytes := s2kBuf.Bytes()
   119  
   120  	packetLength := 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize
   121  	err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength)
   122  	if err != nil {
   123  		return
   124  	}
   125  
   126  	var buf [2]byte
   127  	buf[0] = symmetricKeyEncryptedVersion
   128  	buf[1] = byte(cipherFunc)
   129  	_, err = w.Write(buf[:])
   130  	if err != nil {
   131  		return
   132  	}
   133  	_, err = w.Write(s2kBytes)
   134  	if err != nil {
   135  		return
   136  	}
   137  
   138  	sessionKey := make([]byte, keySize)
   139  	_, err = io.ReadFull(config.Random(), sessionKey)
   140  	if err != nil {
   141  		return
   142  	}
   143  	iv := make([]byte, cipherFunc.blockSize())
   144  	c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv)
   145  	encryptedCipherAndKey := make([]byte, keySize+1)
   146  	c.XORKeyStream(encryptedCipherAndKey, buf[1:])
   147  	c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey)
   148  	_, err = w.Write(encryptedCipherAndKey)
   149  	if err != nil {
   150  		return
   151  	}
   152  
   153  	key = sessionKey
   154  	return
   155  }
   156  

View as plain text