mirror of
https://github.com/cheat/cheat.git
synced 2024-11-27 16:26:52 +01:00
80c91cbdee
Integrate `go-git` into the application, and use it to `git clone` cheatsheets when the installer runs. Previously, the installer required that `git` be installed on the system `PATH`, so this change has to big advantages: 1. It removes that system dependency on `git` 2. It paves the way for implementing the `--update` command Additionally, `cheat` now performs a `--depth=1` clone when installing cheatsheets, which should at least somewhat improve installation times (especially on slow network connections).
803 lines
22 KiB
Go
803 lines
22 KiB
Go
// 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 (
|
|
"crypto"
|
|
"crypto/dsa"
|
|
"crypto/rsa"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
_ "crypto/sha512"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"math/big"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
|
|
"github.com/ProtonMail/go-crypto/openpgp/ecdsa"
|
|
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
|
|
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
|
|
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
|
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
|
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
|
|
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
|
)
|
|
|
|
type kdfHashFunction byte
|
|
type kdfAlgorithm byte
|
|
|
|
// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2.
|
|
type PublicKey struct {
|
|
Version int
|
|
CreationTime time.Time
|
|
PubKeyAlgo PublicKeyAlgorithm
|
|
PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or *eddsa.PublicKey
|
|
Fingerprint []byte
|
|
KeyId uint64
|
|
IsSubkey bool
|
|
|
|
// RFC 4880 fields
|
|
n, e, p, q, g, y encoding.Field
|
|
|
|
// RFC 6637 fields
|
|
// oid contains the OID byte sequence identifying the elliptic curve used
|
|
oid encoding.Field
|
|
|
|
// kdf stores key derivation function parameters
|
|
// used for ECDH encryption. See RFC 6637, Section 9.
|
|
kdf encoding.Field
|
|
}
|
|
|
|
// UpgradeToV5 updates the version of the key to v5, and updates all necessary
|
|
// fields.
|
|
func (pk *PublicKey) UpgradeToV5() {
|
|
pk.Version = 5
|
|
pk.setFingerprintAndKeyId()
|
|
}
|
|
|
|
// signingKey provides a convenient abstraction over signature verification
|
|
// for v3 and v4 public keys.
|
|
type signingKey interface {
|
|
SerializeForHash(io.Writer) error
|
|
SerializeSignaturePrefix(io.Writer)
|
|
serializeWithoutHeaders(io.Writer) error
|
|
}
|
|
|
|
// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey.
|
|
func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey {
|
|
pk := &PublicKey{
|
|
Version: 4,
|
|
CreationTime: creationTime,
|
|
PubKeyAlgo: PubKeyAlgoRSA,
|
|
PublicKey: pub,
|
|
n: new(encoding.MPI).SetBig(pub.N),
|
|
e: new(encoding.MPI).SetBig(big.NewInt(int64(pub.E))),
|
|
}
|
|
|
|
pk.setFingerprintAndKeyId()
|
|
return pk
|
|
}
|
|
|
|
// NewDSAPublicKey returns a PublicKey that wraps the given dsa.PublicKey.
|
|
func NewDSAPublicKey(creationTime time.Time, pub *dsa.PublicKey) *PublicKey {
|
|
pk := &PublicKey{
|
|
Version: 4,
|
|
CreationTime: creationTime,
|
|
PubKeyAlgo: PubKeyAlgoDSA,
|
|
PublicKey: pub,
|
|
p: new(encoding.MPI).SetBig(pub.P),
|
|
q: new(encoding.MPI).SetBig(pub.Q),
|
|
g: new(encoding.MPI).SetBig(pub.G),
|
|
y: new(encoding.MPI).SetBig(pub.Y),
|
|
}
|
|
|
|
pk.setFingerprintAndKeyId()
|
|
return pk
|
|
}
|
|
|
|
// NewElGamalPublicKey returns a PublicKey that wraps the given elgamal.PublicKey.
|
|
func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *PublicKey {
|
|
pk := &PublicKey{
|
|
Version: 4,
|
|
CreationTime: creationTime,
|
|
PubKeyAlgo: PubKeyAlgoElGamal,
|
|
PublicKey: pub,
|
|
p: new(encoding.MPI).SetBig(pub.P),
|
|
g: new(encoding.MPI).SetBig(pub.G),
|
|
y: new(encoding.MPI).SetBig(pub.Y),
|
|
}
|
|
|
|
pk.setFingerprintAndKeyId()
|
|
return pk
|
|
}
|
|
|
|
func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey {
|
|
pk := &PublicKey{
|
|
Version: 4,
|
|
CreationTime: creationTime,
|
|
PubKeyAlgo: PubKeyAlgoECDSA,
|
|
PublicKey: pub,
|
|
p: encoding.NewMPI(pub.MarshalPoint()),
|
|
}
|
|
|
|
curveInfo := ecc.FindByCurve(pub.GetCurve())
|
|
if curveInfo == nil {
|
|
panic("unknown elliptic curve")
|
|
}
|
|
pk.oid = curveInfo.Oid
|
|
pk.setFingerprintAndKeyId()
|
|
return pk
|
|
}
|
|
|
|
func NewECDHPublicKey(creationTime time.Time, pub *ecdh.PublicKey) *PublicKey {
|
|
var pk *PublicKey
|
|
var kdf = encoding.NewOID([]byte{0x1, pub.Hash.Id(), pub.Cipher.Id()})
|
|
pk = &PublicKey{
|
|
Version: 4,
|
|
CreationTime: creationTime,
|
|
PubKeyAlgo: PubKeyAlgoECDH,
|
|
PublicKey: pub,
|
|
p: encoding.NewMPI(pub.MarshalPoint()),
|
|
kdf: kdf,
|
|
}
|
|
|
|
curveInfo := ecc.FindByCurve(pub.GetCurve())
|
|
|
|
if curveInfo == nil {
|
|
panic("unknown elliptic curve")
|
|
}
|
|
|
|
pk.oid = curveInfo.Oid
|
|
pk.setFingerprintAndKeyId()
|
|
return pk
|
|
}
|
|
|
|
func NewEdDSAPublicKey(creationTime time.Time, pub *eddsa.PublicKey) *PublicKey {
|
|
curveInfo := ecc.FindByCurve(pub.GetCurve())
|
|
pk := &PublicKey{
|
|
Version: 4,
|
|
CreationTime: creationTime,
|
|
PubKeyAlgo: PubKeyAlgoEdDSA,
|
|
PublicKey: pub,
|
|
oid: curveInfo.Oid,
|
|
// Native point format, see draft-koch-eddsa-for-openpgp-04, Appendix B
|
|
p: encoding.NewMPI(pub.MarshalPoint()),
|
|
}
|
|
|
|
pk.setFingerprintAndKeyId()
|
|
return pk
|
|
}
|
|
|
|
func (pk *PublicKey) parse(r io.Reader) (err error) {
|
|
// RFC 4880, section 5.5.2
|
|
var buf [6]byte
|
|
_, err = readFull(r, buf[:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
if buf[0] != 4 && buf[0] != 5 {
|
|
return errors.UnsupportedError("public key version " + strconv.Itoa(int(buf[0])))
|
|
}
|
|
|
|
pk.Version = int(buf[0])
|
|
if pk.Version == 5 {
|
|
var n [4]byte
|
|
_, err = readFull(r, n[:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0)
|
|
pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5])
|
|
switch pk.PubKeyAlgo {
|
|
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
|
err = pk.parseRSA(r)
|
|
case PubKeyAlgoDSA:
|
|
err = pk.parseDSA(r)
|
|
case PubKeyAlgoElGamal:
|
|
err = pk.parseElGamal(r)
|
|
case PubKeyAlgoECDSA:
|
|
err = pk.parseECDSA(r)
|
|
case PubKeyAlgoECDH:
|
|
err = pk.parseECDH(r)
|
|
case PubKeyAlgoEdDSA:
|
|
err = pk.parseEdDSA(r)
|
|
default:
|
|
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo)))
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
pk.setFingerprintAndKeyId()
|
|
return
|
|
}
|
|
|
|
func (pk *PublicKey) setFingerprintAndKeyId() {
|
|
// RFC 4880, section 12.2
|
|
if pk.Version == 5 {
|
|
fingerprint := sha256.New()
|
|
pk.SerializeForHash(fingerprint)
|
|
pk.Fingerprint = make([]byte, 32)
|
|
copy(pk.Fingerprint, fingerprint.Sum(nil))
|
|
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[:8])
|
|
} else {
|
|
fingerprint := sha1.New()
|
|
pk.SerializeForHash(fingerprint)
|
|
pk.Fingerprint = make([]byte, 20)
|
|
copy(pk.Fingerprint, fingerprint.Sum(nil))
|
|
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20])
|
|
}
|
|
}
|
|
|
|
// parseRSA parses RSA public key material from the given Reader. See RFC 4880,
|
|
// section 5.5.2.
|
|
func (pk *PublicKey) parseRSA(r io.Reader) (err error) {
|
|
pk.n = new(encoding.MPI)
|
|
if _, err = pk.n.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
pk.e = new(encoding.MPI)
|
|
if _, err = pk.e.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
|
|
if len(pk.e.Bytes()) > 3 {
|
|
err = errors.UnsupportedError("large public exponent")
|
|
return
|
|
}
|
|
rsa := &rsa.PublicKey{
|
|
N: new(big.Int).SetBytes(pk.n.Bytes()),
|
|
E: 0,
|
|
}
|
|
for i := 0; i < len(pk.e.Bytes()); i++ {
|
|
rsa.E <<= 8
|
|
rsa.E |= int(pk.e.Bytes()[i])
|
|
}
|
|
pk.PublicKey = rsa
|
|
return
|
|
}
|
|
|
|
// parseDSA parses DSA public key material from the given Reader. See RFC 4880,
|
|
// section 5.5.2.
|
|
func (pk *PublicKey) parseDSA(r io.Reader) (err error) {
|
|
pk.p = new(encoding.MPI)
|
|
if _, err = pk.p.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
pk.q = new(encoding.MPI)
|
|
if _, err = pk.q.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
pk.g = new(encoding.MPI)
|
|
if _, err = pk.g.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
pk.y = new(encoding.MPI)
|
|
if _, err = pk.y.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
|
|
dsa := new(dsa.PublicKey)
|
|
dsa.P = new(big.Int).SetBytes(pk.p.Bytes())
|
|
dsa.Q = new(big.Int).SetBytes(pk.q.Bytes())
|
|
dsa.G = new(big.Int).SetBytes(pk.g.Bytes())
|
|
dsa.Y = new(big.Int).SetBytes(pk.y.Bytes())
|
|
pk.PublicKey = dsa
|
|
return
|
|
}
|
|
|
|
// parseElGamal parses ElGamal public key material from the given Reader. See
|
|
// RFC 4880, section 5.5.2.
|
|
func (pk *PublicKey) parseElGamal(r io.Reader) (err error) {
|
|
pk.p = new(encoding.MPI)
|
|
if _, err = pk.p.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
pk.g = new(encoding.MPI)
|
|
if _, err = pk.g.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
pk.y = new(encoding.MPI)
|
|
if _, err = pk.y.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
|
|
elgamal := new(elgamal.PublicKey)
|
|
elgamal.P = new(big.Int).SetBytes(pk.p.Bytes())
|
|
elgamal.G = new(big.Int).SetBytes(pk.g.Bytes())
|
|
elgamal.Y = new(big.Int).SetBytes(pk.y.Bytes())
|
|
pk.PublicKey = elgamal
|
|
return
|
|
}
|
|
|
|
// parseECDSA parses ECDSA public key material from the given Reader. See
|
|
// RFC 6637, Section 9.
|
|
func (pk *PublicKey) parseECDSA(r io.Reader) (err error) {
|
|
pk.oid = new(encoding.OID)
|
|
if _, err = pk.oid.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
pk.p = new(encoding.MPI)
|
|
if _, err = pk.p.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
|
|
curveInfo := ecc.FindByOid(pk.oid)
|
|
if curveInfo == nil {
|
|
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
|
|
}
|
|
|
|
c, ok := curveInfo.Curve.(ecc.ECDSACurve)
|
|
if !ok {
|
|
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
|
|
}
|
|
|
|
ecdsaKey := ecdsa.NewPublicKey(c)
|
|
err = ecdsaKey.UnmarshalPoint(pk.p.Bytes())
|
|
pk.PublicKey = ecdsaKey
|
|
|
|
return
|
|
}
|
|
|
|
// parseECDH parses ECDH public key material from the given Reader. See
|
|
// RFC 6637, Section 9.
|
|
func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
|
|
pk.oid = new(encoding.OID)
|
|
if _, err = pk.oid.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
pk.p = new(encoding.MPI)
|
|
if _, err = pk.p.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
pk.kdf = new(encoding.OID)
|
|
if _, err = pk.kdf.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
|
|
curveInfo := ecc.FindByOid(pk.oid)
|
|
|
|
if curveInfo == nil {
|
|
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
|
|
}
|
|
|
|
c, ok := curveInfo.Curve.(ecc.ECDHCurve)
|
|
if !ok {
|
|
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
|
|
}
|
|
|
|
if kdfLen := len(pk.kdf.Bytes()); kdfLen < 3 {
|
|
return errors.UnsupportedError("unsupported ECDH KDF length: " + strconv.Itoa(kdfLen))
|
|
}
|
|
if reserved := pk.kdf.Bytes()[0]; reserved != 0x01 {
|
|
return errors.UnsupportedError("unsupported KDF reserved field: " + strconv.Itoa(int(reserved)))
|
|
}
|
|
kdfHash, ok := algorithm.HashById[pk.kdf.Bytes()[1]]
|
|
if !ok {
|
|
return errors.UnsupportedError("unsupported ECDH KDF hash: " + strconv.Itoa(int(pk.kdf.Bytes()[1])))
|
|
}
|
|
kdfCipher, ok := algorithm.CipherById[pk.kdf.Bytes()[2]]
|
|
if !ok {
|
|
return errors.UnsupportedError("unsupported ECDH KDF cipher: " + strconv.Itoa(int(pk.kdf.Bytes()[2])))
|
|
}
|
|
|
|
ecdhKey := ecdh.NewPublicKey(c, kdfHash, kdfCipher)
|
|
err = ecdhKey.UnmarshalPoint(pk.p.Bytes())
|
|
pk.PublicKey = ecdhKey
|
|
|
|
return
|
|
}
|
|
|
|
func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) {
|
|
pk.oid = new(encoding.OID)
|
|
if _, err = pk.oid.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
curveInfo := ecc.FindByOid(pk.oid)
|
|
if curveInfo == nil {
|
|
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
|
|
}
|
|
|
|
c, ok := curveInfo.Curve.(ecc.EdDSACurve)
|
|
if !ok {
|
|
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
|
|
}
|
|
|
|
pk.p = new(encoding.MPI)
|
|
if _, err = pk.p.ReadFrom(r); err != nil {
|
|
return
|
|
}
|
|
|
|
pub := eddsa.NewPublicKey(c)
|
|
|
|
switch flag := pk.p.Bytes()[0]; flag {
|
|
case 0x04:
|
|
// TODO: see _grcy_ecc_eddsa_ensure_compact in grcypt
|
|
return errors.UnsupportedError("unsupported EdDSA compression: " + strconv.Itoa(int(flag)))
|
|
case 0x40:
|
|
err = pub.UnmarshalPoint(pk.p.Bytes())
|
|
default:
|
|
return errors.UnsupportedError("unsupported EdDSA compression: " + strconv.Itoa(int(flag)))
|
|
}
|
|
|
|
pk.PublicKey = pub
|
|
return
|
|
}
|
|
|
|
// SerializeForHash serializes the PublicKey to w with the special packet
|
|
// header format needed for hashing.
|
|
func (pk *PublicKey) SerializeForHash(w io.Writer) error {
|
|
pk.SerializeSignaturePrefix(w)
|
|
return pk.serializeWithoutHeaders(w)
|
|
}
|
|
|
|
// SerializeSignaturePrefix writes the prefix for this public key to the given Writer.
|
|
// The prefix is used when calculating a signature over this public key. See
|
|
// RFC 4880, section 5.2.4.
|
|
func (pk *PublicKey) SerializeSignaturePrefix(w io.Writer) {
|
|
var pLength = pk.algorithmSpecificByteCount()
|
|
if pk.Version == 5 {
|
|
pLength += 10 // version, timestamp (4), algorithm, key octet count (4).
|
|
w.Write([]byte{
|
|
0x9A,
|
|
byte(pLength >> 24),
|
|
byte(pLength >> 16),
|
|
byte(pLength >> 8),
|
|
byte(pLength),
|
|
})
|
|
return
|
|
}
|
|
pLength += 6
|
|
w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)})
|
|
}
|
|
|
|
func (pk *PublicKey) Serialize(w io.Writer) (err error) {
|
|
length := 6 // 6 byte header
|
|
length += pk.algorithmSpecificByteCount()
|
|
if pk.Version == 5 {
|
|
length += 4 // octet key count
|
|
}
|
|
packetType := packetTypePublicKey
|
|
if pk.IsSubkey {
|
|
packetType = packetTypePublicSubkey
|
|
}
|
|
err = serializeHeader(w, packetType, length)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return pk.serializeWithoutHeaders(w)
|
|
}
|
|
|
|
func (pk *PublicKey) algorithmSpecificByteCount() int {
|
|
length := 0
|
|
switch pk.PubKeyAlgo {
|
|
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
|
length += int(pk.n.EncodedLength())
|
|
length += int(pk.e.EncodedLength())
|
|
case PubKeyAlgoDSA:
|
|
length += int(pk.p.EncodedLength())
|
|
length += int(pk.q.EncodedLength())
|
|
length += int(pk.g.EncodedLength())
|
|
length += int(pk.y.EncodedLength())
|
|
case PubKeyAlgoElGamal:
|
|
length += int(pk.p.EncodedLength())
|
|
length += int(pk.g.EncodedLength())
|
|
length += int(pk.y.EncodedLength())
|
|
case PubKeyAlgoECDSA:
|
|
length += int(pk.oid.EncodedLength())
|
|
length += int(pk.p.EncodedLength())
|
|
case PubKeyAlgoECDH:
|
|
length += int(pk.oid.EncodedLength())
|
|
length += int(pk.p.EncodedLength())
|
|
length += int(pk.kdf.EncodedLength())
|
|
case PubKeyAlgoEdDSA:
|
|
length += int(pk.oid.EncodedLength())
|
|
length += int(pk.p.EncodedLength())
|
|
default:
|
|
panic("unknown public key algorithm")
|
|
}
|
|
return length
|
|
}
|
|
|
|
// serializeWithoutHeaders marshals the PublicKey to w in the form of an
|
|
// OpenPGP public key packet, not including the packet header.
|
|
func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
|
|
t := uint32(pk.CreationTime.Unix())
|
|
if _, err = w.Write([]byte{
|
|
byte(pk.Version),
|
|
byte(t >> 24), byte(t >> 16), byte(t >> 8), byte(t),
|
|
byte(pk.PubKeyAlgo),
|
|
}); err != nil {
|
|
return
|
|
}
|
|
|
|
if pk.Version == 5 {
|
|
n := pk.algorithmSpecificByteCount()
|
|
if _, err = w.Write([]byte{
|
|
byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n),
|
|
}); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
switch pk.PubKeyAlgo {
|
|
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
|
if _, err = w.Write(pk.n.EncodedBytes()); err != nil {
|
|
return
|
|
}
|
|
_, err = w.Write(pk.e.EncodedBytes())
|
|
return
|
|
case PubKeyAlgoDSA:
|
|
if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
|
|
return
|
|
}
|
|
if _, err = w.Write(pk.q.EncodedBytes()); err != nil {
|
|
return
|
|
}
|
|
if _, err = w.Write(pk.g.EncodedBytes()); err != nil {
|
|
return
|
|
}
|
|
_, err = w.Write(pk.y.EncodedBytes())
|
|
return
|
|
case PubKeyAlgoElGamal:
|
|
if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
|
|
return
|
|
}
|
|
if _, err = w.Write(pk.g.EncodedBytes()); err != nil {
|
|
return
|
|
}
|
|
_, err = w.Write(pk.y.EncodedBytes())
|
|
return
|
|
case PubKeyAlgoECDSA:
|
|
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
|
|
return
|
|
}
|
|
_, err = w.Write(pk.p.EncodedBytes())
|
|
return
|
|
case PubKeyAlgoECDH:
|
|
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
|
|
return
|
|
}
|
|
if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
|
|
return
|
|
}
|
|
_, err = w.Write(pk.kdf.EncodedBytes())
|
|
return
|
|
case PubKeyAlgoEdDSA:
|
|
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
|
|
return
|
|
}
|
|
_, err = w.Write(pk.p.EncodedBytes())
|
|
return
|
|
}
|
|
return errors.InvalidArgumentError("bad public-key algorithm")
|
|
}
|
|
|
|
// CanSign returns true iff this public key can generate signatures
|
|
func (pk *PublicKey) CanSign() bool {
|
|
return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal && pk.PubKeyAlgo != PubKeyAlgoECDH
|
|
}
|
|
|
|
// VerifySignature returns nil iff sig is a valid signature, made by this
|
|
// public key, of the data hashed into signed. signed is mutated by this call.
|
|
func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) {
|
|
if !pk.CanSign() {
|
|
return errors.InvalidArgumentError("public key cannot generate signatures")
|
|
}
|
|
if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) {
|
|
sig.AddMetadataToHashSuffix()
|
|
}
|
|
signed.Write(sig.HashSuffix)
|
|
hashBytes := signed.Sum(nil)
|
|
if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] {
|
|
return errors.SignatureError("hash tag doesn't match")
|
|
}
|
|
|
|
if pk.PubKeyAlgo != sig.PubKeyAlgo {
|
|
return errors.InvalidArgumentError("public key and signature use different algorithms")
|
|
}
|
|
|
|
switch pk.PubKeyAlgo {
|
|
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
|
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
|
|
err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.Bytes()))
|
|
if err != nil {
|
|
return errors.SignatureError("RSA verification failure")
|
|
}
|
|
return nil
|
|
case PubKeyAlgoDSA:
|
|
dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey)
|
|
// Need to truncate hashBytes to match FIPS 186-3 section 4.6.
|
|
subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8
|
|
if len(hashBytes) > subgroupSize {
|
|
hashBytes = hashBytes[:subgroupSize]
|
|
}
|
|
if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.Bytes()), new(big.Int).SetBytes(sig.DSASigS.Bytes())) {
|
|
return errors.SignatureError("DSA verification failure")
|
|
}
|
|
return nil
|
|
case PubKeyAlgoECDSA:
|
|
ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey)
|
|
if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.Bytes()), new(big.Int).SetBytes(sig.ECDSASigS.Bytes())) {
|
|
return errors.SignatureError("ECDSA verification failure")
|
|
}
|
|
return nil
|
|
case PubKeyAlgoEdDSA:
|
|
eddsaPublicKey := pk.PublicKey.(*eddsa.PublicKey)
|
|
if !eddsa.Verify(eddsaPublicKey, hashBytes, sig.EdDSASigR.Bytes(), sig.EdDSASigS.Bytes()) {
|
|
return errors.SignatureError("EdDSA verification failure")
|
|
}
|
|
return nil
|
|
default:
|
|
return errors.SignatureError("Unsupported public key algorithm used in signature")
|
|
}
|
|
}
|
|
|
|
// keySignatureHash returns a Hash of the message that needs to be signed for
|
|
// pk to assert a subkey relationship to signed.
|
|
func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
|
if !hashFunc.Available() {
|
|
return nil, errors.UnsupportedError("hash function")
|
|
}
|
|
h = hashFunc.New()
|
|
|
|
// RFC 4880, section 5.2.4
|
|
err = pk.SerializeForHash(h)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = signed.SerializeForHash(h)
|
|
return
|
|
}
|
|
|
|
// VerifyKeySignature returns nil iff sig is a valid signature, made by this
|
|
// public key, of signed.
|
|
func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error {
|
|
h, err := keySignatureHash(pk, signed, sig.Hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = pk.VerifySignature(h, sig); err != nil {
|
|
return err
|
|
}
|
|
|
|
if sig.FlagSign {
|
|
// Signing subkeys must be cross-signed. See
|
|
// https://www.gnupg.org/faq/subkey-cross-certify.html.
|
|
if sig.EmbeddedSignature == nil {
|
|
return errors.StructuralError("signing subkey is missing cross-signature")
|
|
}
|
|
// Verify the cross-signature. This is calculated over the same
|
|
// data as the main signature, so we cannot just recursively
|
|
// call signed.VerifyKeySignature(...)
|
|
if h, err = keySignatureHash(pk, signed, sig.EmbeddedSignature.Hash); err != nil {
|
|
return errors.StructuralError("error while hashing for cross-signature: " + err.Error())
|
|
}
|
|
if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil {
|
|
return errors.StructuralError("error while verifying cross-signature: " + err.Error())
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
|
if !hashFunc.Available() {
|
|
return nil, errors.UnsupportedError("hash function")
|
|
}
|
|
h = hashFunc.New()
|
|
|
|
// RFC 4880, section 5.2.4
|
|
err = pk.SerializeForHash(h)
|
|
|
|
return
|
|
}
|
|
|
|
// VerifyRevocationSignature returns nil iff sig is a valid signature, made by this
|
|
// public key.
|
|
func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) {
|
|
h, err := keyRevocationHash(pk, sig.Hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return pk.VerifySignature(h, sig)
|
|
}
|
|
|
|
// VerifySubkeyRevocationSignature returns nil iff sig is a valid subkey revocation signature,
|
|
// made by this public key, of signed.
|
|
func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signed *PublicKey) (err error) {
|
|
h, err := keySignatureHash(pk, signed, sig.Hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return pk.VerifySignature(h, sig)
|
|
}
|
|
|
|
// userIdSignatureHash returns a Hash of the message that needs to be signed
|
|
// to assert that pk is a valid key for id.
|
|
func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
|
if !hashFunc.Available() {
|
|
return nil, errors.UnsupportedError("hash function")
|
|
}
|
|
h = hashFunc.New()
|
|
|
|
// RFC 4880, section 5.2.4
|
|
pk.SerializeSignaturePrefix(h)
|
|
pk.serializeWithoutHeaders(h)
|
|
|
|
var buf [5]byte
|
|
buf[0] = 0xb4
|
|
buf[1] = byte(len(id) >> 24)
|
|
buf[2] = byte(len(id) >> 16)
|
|
buf[3] = byte(len(id) >> 8)
|
|
buf[4] = byte(len(id))
|
|
h.Write(buf[:])
|
|
h.Write([]byte(id))
|
|
|
|
return
|
|
}
|
|
|
|
// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this
|
|
// public key, that id is the identity of pub.
|
|
func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) {
|
|
h, err := userIdSignatureHash(id, pub, sig.Hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return pk.VerifySignature(h, sig)
|
|
}
|
|
|
|
// KeyIdString returns the public key's fingerprint in capital hex
|
|
// (e.g. "6C7EE1B8621CC013").
|
|
func (pk *PublicKey) KeyIdString() string {
|
|
return fmt.Sprintf("%X", pk.Fingerprint[12:20])
|
|
}
|
|
|
|
// KeyIdShortString returns the short form of public key's fingerprint
|
|
// in capital hex, as shown by gpg --list-keys (e.g. "621CC013").
|
|
func (pk *PublicKey) KeyIdShortString() string {
|
|
return fmt.Sprintf("%X", pk.Fingerprint[16:20])
|
|
}
|
|
|
|
// BitLength returns the bit length for the given public key.
|
|
func (pk *PublicKey) BitLength() (bitLength uint16, err error) {
|
|
switch pk.PubKeyAlgo {
|
|
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
|
bitLength = pk.n.BitLength()
|
|
case PubKeyAlgoDSA:
|
|
bitLength = pk.p.BitLength()
|
|
case PubKeyAlgoElGamal:
|
|
bitLength = pk.p.BitLength()
|
|
case PubKeyAlgoECDSA:
|
|
bitLength = pk.p.BitLength()
|
|
case PubKeyAlgoECDH:
|
|
bitLength = pk.p.BitLength()
|
|
case PubKeyAlgoEdDSA:
|
|
bitLength = pk.p.BitLength()
|
|
default:
|
|
err = errors.InvalidArgumentError("bad public-key algorithm")
|
|
}
|
|
return
|
|
}
|
|
|
|
// KeyExpired returns whether sig is a self-signature of a key that has
|
|
// expired or is created in the future.
|
|
func (pk *PublicKey) KeyExpired(sig *Signature, currentTime time.Time) bool {
|
|
if pk.CreationTime.After(currentTime) {
|
|
return true
|
|
}
|
|
if sig.KeyLifetimeSecs == nil || *sig.KeyLifetimeSecs == 0 {
|
|
return false
|
|
}
|
|
expiry := pk.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second)
|
|
return currentTime.After(expiry)
|
|
}
|