// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package packet import ( "big" "bytes" "crypto/cipher" "crypto/openpgp/error" "crypto/openpgp/s2k" "crypto/rsa" "crypto/sha1" "io" "io/ioutil" "os" "strconv" ) // PrivateKey represents a possibly encrypted private key. See RFC 4880, // section 5.5.3. type PrivateKey struct { PublicKey Encrypted bool // if true then the private key is unavailable until Decrypt has been called. encryptedData []byte cipher CipherFunction s2k func(out, in []byte) PrivateKey interface{} // An *rsa.PrivateKey. sha1Checksum bool iv []byte } func (pk *PrivateKey) parse(r io.Reader) (err os.Error) { err = (&pk.PublicKey).parse(r) if err != nil { return } var buf [1]byte _, err = readFull(r, buf[:]) if err != nil { return } s2kType := buf[0] switch s2kType { case 0: pk.s2k = nil pk.Encrypted = false case 254, 255: _, err = readFull(r, buf[:]) if err != nil { return } pk.cipher = CipherFunction(buf[0]) pk.Encrypted = true pk.s2k, err = s2k.Parse(r) if err != nil { return } if s2kType == 254 { pk.sha1Checksum = true } default: return error.UnsupportedError("deprecated s2k function in private key") } if pk.Encrypted { blockSize := pk.cipher.blockSize() if blockSize == 0 { return error.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher))) } pk.iv = make([]byte, blockSize) _, err = readFull(r, pk.iv) if err != nil { return } } pk.encryptedData, err = ioutil.ReadAll(r) if err != nil { return } if !pk.Encrypted { return pk.parsePrivateKey(pk.encryptedData) } return } // Decrypt decrypts an encrypted private key using a passphrase. func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error { if !pk.Encrypted { return nil } key := make([]byte, pk.cipher.keySize()) pk.s2k(key, passphrase) block := pk.cipher.new(key) cfb := cipher.NewCFBDecrypter(block, pk.iv) data := pk.encryptedData cfb.XORKeyStream(data, data) if pk.sha1Checksum { if len(data) < sha1.Size { return error.StructuralError("truncated private key data") } h := sha1.New() h.Write(data[:len(data)-sha1.Size]) sum := h.Sum() if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { return error.StructuralError("private key checksum failure") } data = data[:len(data)-sha1.Size] } else { if len(data) < 2 { return error.StructuralError("truncated private key data") } var sum uint16 for i := 0; i < len(data)-2; i++ { sum += uint16(data[i]) } if data[len(data)-2] != uint8(sum>>8) || data[len(data)-1] != uint8(sum) { return error.StructuralError("private key checksum failure") } data = data[:len(data)-2] } return pk.parsePrivateKey(data) } func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) { // TODO(agl): support DSA and ECDSA private keys. rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey) rsaPriv := new(rsa.PrivateKey) rsaPriv.PublicKey = *rsaPub buf := bytes.NewBuffer(data) d, _, err := readMPI(buf) if err != nil { return } p, _, err := readMPI(buf) if err != nil { return } q, _, err := readMPI(buf) if err != nil { return } rsaPriv.D = new(big.Int).SetBytes(d) rsaPriv.P = new(big.Int).SetBytes(p) rsaPriv.Q = new(big.Int).SetBytes(q) pk.PrivateKey = rsaPriv pk.Encrypted = false pk.encryptedData = nil return nil }