| // 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 |
| } |