Change to keybase openpgp fork as it has important fixes
This commit is contained in:
parent
0a40dcc593
commit
c1a46349fa
|
@ -13,8 +13,8 @@ import (
|
|||
"github.com/hashicorp/vault/helper/pgpkeys"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
func getPubKeyFiles(t *testing.T) (string, []string, error) {
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
// EncryptShares takes an ordered set of byte slices to encrypt and the
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
)
|
||||
|
||||
// PGPPubKeyFiles implements the flag.Value interface and allows
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
func TestPubKeyFilesFlag_implements(t *testing.T) {
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/vault/helper/jsonutil"
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
func TestFetchKeybasePubkeys(t *testing.T) {
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"github.com/hashicorp/vault/helper/jsonutil"
|
||||
"github.com/hashicorp/vault/physical"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2013 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 ed25519 implements the Ed25519 signature algorithm. See
|
||||
// http://ed25519.cr.yp.to/.
|
||||
package ed25519
|
||||
|
||||
// This code is a port of the public domain, "ref10" implementation of ed25519
|
||||
// from SUPERCOP.
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"io"
|
||||
|
||||
"github.com/agl/ed25519/edwards25519"
|
||||
)
|
||||
|
||||
const (
|
||||
PublicKeySize = 32
|
||||
PrivateKeySize = 64
|
||||
SignatureSize = 64
|
||||
)
|
||||
|
||||
// GenerateKey generates a public/private key pair using randomness from rand.
|
||||
func GenerateKey(rand io.Reader) (publicKey *[PublicKeySize]byte, privateKey *[PrivateKeySize]byte, err error) {
|
||||
privateKey = new([64]byte)
|
||||
publicKey = new([32]byte)
|
||||
_, err = io.ReadFull(rand, privateKey[:32])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
h := sha512.New()
|
||||
h.Write(privateKey[:32])
|
||||
digest := h.Sum(nil)
|
||||
|
||||
digest[0] &= 248
|
||||
digest[31] &= 127
|
||||
digest[31] |= 64
|
||||
|
||||
var A edwards25519.ExtendedGroupElement
|
||||
var hBytes [32]byte
|
||||
copy(hBytes[:], digest)
|
||||
edwards25519.GeScalarMultBase(&A, &hBytes)
|
||||
A.ToBytes(publicKey)
|
||||
|
||||
copy(privateKey[32:], publicKey[:])
|
||||
return
|
||||
}
|
||||
|
||||
// Sign signs the message with privateKey and returns a signature.
|
||||
func Sign(privateKey *[PrivateKeySize]byte, message []byte) *[SignatureSize]byte {
|
||||
h := sha512.New()
|
||||
h.Write(privateKey[:32])
|
||||
|
||||
var digest1, messageDigest, hramDigest [64]byte
|
||||
var expandedSecretKey [32]byte
|
||||
h.Sum(digest1[:0])
|
||||
copy(expandedSecretKey[:], digest1[:])
|
||||
expandedSecretKey[0] &= 248
|
||||
expandedSecretKey[31] &= 63
|
||||
expandedSecretKey[31] |= 64
|
||||
|
||||
h.Reset()
|
||||
h.Write(digest1[32:])
|
||||
h.Write(message)
|
||||
h.Sum(messageDigest[:0])
|
||||
|
||||
var messageDigestReduced [32]byte
|
||||
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
|
||||
var R edwards25519.ExtendedGroupElement
|
||||
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
|
||||
|
||||
var encodedR [32]byte
|
||||
R.ToBytes(&encodedR)
|
||||
|
||||
h.Reset()
|
||||
h.Write(encodedR[:])
|
||||
h.Write(privateKey[32:])
|
||||
h.Write(message)
|
||||
h.Sum(hramDigest[:0])
|
||||
var hramDigestReduced [32]byte
|
||||
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
|
||||
|
||||
var s [32]byte
|
||||
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
|
||||
|
||||
signature := new([64]byte)
|
||||
copy(signature[:], encodedR[:])
|
||||
copy(signature[32:], s[:])
|
||||
return signature
|
||||
}
|
||||
|
||||
// Verify returns true iff sig is a valid signature of message by publicKey.
|
||||
func Verify(publicKey *[PublicKeySize]byte, message []byte, sig *[SignatureSize]byte) bool {
|
||||
if sig[63]&224 != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var A edwards25519.ExtendedGroupElement
|
||||
if !A.FromBytes(publicKey) {
|
||||
return false
|
||||
}
|
||||
edwards25519.FeNeg(&A.X, &A.X)
|
||||
edwards25519.FeNeg(&A.T, &A.T)
|
||||
|
||||
h := sha512.New()
|
||||
h.Write(sig[:32])
|
||||
h.Write(publicKey[:])
|
||||
h.Write(message)
|
||||
var digest [64]byte
|
||||
h.Sum(digest[:0])
|
||||
|
||||
var hReduced [32]byte
|
||||
edwards25519.ScReduce(&hReduced, &digest)
|
||||
|
||||
var R edwards25519.ProjectiveGroupElement
|
||||
var b [32]byte
|
||||
copy(b[:], sig[32:])
|
||||
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
|
||||
|
||||
var checkR [32]byte
|
||||
R.ToBytes(&checkR)
|
||||
return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,22 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
|
@ -0,0 +1,134 @@
|
|||
// Package brainpool implements Brainpool elliptic curves.
|
||||
// Implementation of rcurves is from github.com/ebfe/brainpool
|
||||
// Note that these curves are implemented with naive, non-constant time operations
|
||||
// and are likely not suitable for enviroments where timing attacks are a concern.
|
||||
package brainpool
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
p256t1, p384t1, p512t1 *elliptic.CurveParams
|
||||
p256r1, p384r1, p512r1 *rcurve
|
||||
)
|
||||
|
||||
func initAll() {
|
||||
initP256t1()
|
||||
initP384t1()
|
||||
initP512t1()
|
||||
initP256r1()
|
||||
initP384r1()
|
||||
initP512r1()
|
||||
}
|
||||
|
||||
func initP256t1() {
|
||||
p256t1 = &elliptic.CurveParams{Name: "brainpoolP256t1"}
|
||||
p256t1.P, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16)
|
||||
p256t1.N, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16)
|
||||
p256t1.B, _ = new(big.Int).SetString("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16)
|
||||
p256t1.Gx, _ = new(big.Int).SetString("A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4", 16)
|
||||
p256t1.Gy, _ = new(big.Int).SetString("2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE", 16)
|
||||
p256t1.BitSize = 256
|
||||
}
|
||||
|
||||
func initP256r1() {
|
||||
twisted := p256t1
|
||||
params := &elliptic.CurveParams{
|
||||
Name: "brainpoolP256r1",
|
||||
P: twisted.P,
|
||||
N: twisted.N,
|
||||
BitSize: twisted.BitSize,
|
||||
}
|
||||
params.Gx, _ = new(big.Int).SetString("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262", 16)
|
||||
params.Gy, _ = new(big.Int).SetString("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997", 16)
|
||||
z, _ := new(big.Int).SetString("3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0", 16)
|
||||
p256r1 = newrcurve(twisted, params, z)
|
||||
}
|
||||
|
||||
func initP384t1() {
|
||||
p384t1 = &elliptic.CurveParams{Name: "brainpoolP384t1"}
|
||||
p384t1.P, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16)
|
||||
p384t1.N, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16)
|
||||
p384t1.B, _ = new(big.Int).SetString("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16)
|
||||
p384t1.Gx, _ = new(big.Int).SetString("18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC", 16)
|
||||
p384t1.Gy, _ = new(big.Int).SetString("25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928", 16)
|
||||
p384t1.BitSize = 384
|
||||
}
|
||||
|
||||
func initP384r1() {
|
||||
twisted := p384t1
|
||||
params := &elliptic.CurveParams{
|
||||
Name: "brainpoolP384r1",
|
||||
P: twisted.P,
|
||||
N: twisted.N,
|
||||
BitSize: twisted.BitSize,
|
||||
}
|
||||
params.Gx, _ = new(big.Int).SetString("1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E", 16)
|
||||
params.Gy, _ = new(big.Int).SetString("8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315", 16)
|
||||
z, _ := new(big.Int).SetString("41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE97D2D63DBC87BCCDDCCC5DA39E8589291C", 16)
|
||||
p384r1 = newrcurve(twisted, params, z)
|
||||
}
|
||||
|
||||
func initP512t1() {
|
||||
p512t1 = &elliptic.CurveParams{Name: "brainpoolP512t1"}
|
||||
p512t1.P, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16)
|
||||
p512t1.N, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16)
|
||||
p512t1.B, _ = new(big.Int).SetString("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16)
|
||||
p512t1.Gx, _ = new(big.Int).SetString("640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA", 16)
|
||||
p512t1.Gy, _ = new(big.Int).SetString("5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332", 16)
|
||||
p512t1.BitSize = 512
|
||||
}
|
||||
|
||||
func initP512r1() {
|
||||
twisted := p512t1
|
||||
params := &elliptic.CurveParams{
|
||||
Name: "brainpoolP512r1",
|
||||
P: twisted.P,
|
||||
N: twisted.N,
|
||||
BitSize: twisted.BitSize,
|
||||
}
|
||||
params.Gx, _ = new(big.Int).SetString("81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822", 16)
|
||||
params.Gy, _ = new(big.Int).SetString("7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892", 16)
|
||||
z, _ := new(big.Int).SetString("12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB", 16)
|
||||
p512r1 = newrcurve(twisted, params, z)
|
||||
}
|
||||
|
||||
// P256t1 returns a Curve which implements Brainpool P256t1 (see RFC 5639, section 3.4)
|
||||
func P256t1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p256t1
|
||||
}
|
||||
|
||||
// P256r1 returns a Curve which implements Brainpool P256r1 (see RFC 5639, section 3.4)
|
||||
func P256r1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p256r1
|
||||
}
|
||||
|
||||
// P384t1 returns a Curve which implements Brainpool P384t1 (see RFC 5639, section 3.6)
|
||||
func P384t1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p384t1
|
||||
}
|
||||
|
||||
// P384r1 returns a Curve which implements Brainpool P384r1 (see RFC 5639, section 3.6)
|
||||
func P384r1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p384r1
|
||||
}
|
||||
|
||||
// P512t1 returns a Curve which implements Brainpool P512t1 (see RFC 5639, section 3.7)
|
||||
func P512t1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p512t1
|
||||
}
|
||||
|
||||
// P512r1 returns a Curve which implements Brainpool P512r1 (see RFC 5639, section 3.7)
|
||||
func P512r1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p512r1
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package brainpool
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var _ elliptic.Curve = (*rcurve)(nil)
|
||||
|
||||
type rcurve struct {
|
||||
twisted elliptic.Curve
|
||||
params *elliptic.CurveParams
|
||||
z *big.Int
|
||||
zinv *big.Int
|
||||
z2 *big.Int
|
||||
z3 *big.Int
|
||||
zinv2 *big.Int
|
||||
zinv3 *big.Int
|
||||
}
|
||||
|
||||
var (
|
||||
two = big.NewInt(2)
|
||||
three = big.NewInt(3)
|
||||
)
|
||||
|
||||
func newrcurve(twisted elliptic.Curve, params *elliptic.CurveParams, z *big.Int) *rcurve {
|
||||
zinv := new(big.Int).ModInverse(z, params.P)
|
||||
return &rcurve{
|
||||
twisted: twisted,
|
||||
params: params,
|
||||
z: z,
|
||||
zinv: zinv,
|
||||
z2: new(big.Int).Exp(z, two, params.P),
|
||||
z3: new(big.Int).Exp(z, three, params.P),
|
||||
zinv2: new(big.Int).Exp(zinv, two, params.P),
|
||||
zinv3: new(big.Int).Exp(zinv, three, params.P),
|
||||
}
|
||||
}
|
||||
|
||||
func (curve *rcurve) toTwisted(x, y *big.Int) (*big.Int, *big.Int) {
|
||||
var tx, ty big.Int
|
||||
tx.Mul(x, curve.z2)
|
||||
tx.Mod(&tx, curve.params.P)
|
||||
ty.Mul(y, curve.z3)
|
||||
ty.Mod(&ty, curve.params.P)
|
||||
return &tx, &ty
|
||||
}
|
||||
|
||||
func (curve *rcurve) fromTwisted(tx, ty *big.Int) (*big.Int, *big.Int) {
|
||||
var x, y big.Int
|
||||
x.Mul(tx, curve.zinv2)
|
||||
x.Mod(&x, curve.params.P)
|
||||
y.Mul(ty, curve.zinv3)
|
||||
y.Mod(&y, curve.params.P)
|
||||
return &x, &y
|
||||
}
|
||||
|
||||
func (curve *rcurve) Params() *elliptic.CurveParams {
|
||||
return curve.params
|
||||
}
|
||||
|
||||
func (curve *rcurve) IsOnCurve(x, y *big.Int) bool {
|
||||
return curve.twisted.IsOnCurve(curve.toTwisted(x, y))
|
||||
}
|
||||
|
||||
func (curve *rcurve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) {
|
||||
tx1, ty1 := curve.toTwisted(x1, y1)
|
||||
tx2, ty2 := curve.toTwisted(x2, y2)
|
||||
return curve.fromTwisted(curve.twisted.Add(tx1, ty1, tx2, ty2))
|
||||
}
|
||||
|
||||
func (curve *rcurve) Double(x1, y1 *big.Int) (x, y *big.Int) {
|
||||
return curve.fromTwisted(curve.twisted.Double(curve.toTwisted(x1, y1)))
|
||||
}
|
||||
|
||||
func (curve *rcurve) ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) {
|
||||
tx1, ty1 := curve.toTwisted(x1, y1)
|
||||
return curve.fromTwisted(curve.twisted.ScalarMult(tx1, ty1, scalar))
|
||||
}
|
||||
|
||||
func (curve *rcurve) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
|
||||
return curve.fromTwisted(curve.twisted.ScalarBaseMult(scalar))
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// Package cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common
|
||||
// OpenPGP cipher.
|
||||
package cast5 // import "golang.org/x/crypto/cast5"
|
||||
package cast5
|
||||
|
||||
import "errors"
|
||||
|
|
@ -4,14 +4,16 @@
|
|||
|
||||
// Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is
|
||||
// very similar to PEM except that it has an additional CRC checksum.
|
||||
package armor // import "golang.org/x/crypto/openpgp/armor"
|
||||
package armor // import "github.com/keybase/go-crypto/openpgp/armor"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// A Block represents an OpenPGP armored structure.
|
||||
|
@ -99,9 +101,14 @@ func (l *lineReader) Read(p []byte) (n int, err error) {
|
|||
uint32(expectedBytes[1])<<8 |
|
||||
uint32(expectedBytes[2])
|
||||
|
||||
line, _, err = l.in.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return
|
||||
for {
|
||||
line, _, err = l.in.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return
|
||||
}
|
||||
if len(strings.TrimSpace(string(line))) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !bytes.HasPrefix(line, armorEnd) {
|
||||
return 0, ArmorCorrupt
|
|
@ -125,7 +125,7 @@ func (e *encoding) Close() (err error) {
|
|||
var b64ChecksumBytes [4]byte
|
||||
base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
|
||||
|
||||
return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
|
||||
return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine, []byte{'\n'})
|
||||
}
|
||||
|
||||
// Encode returns a WriteCloser which will encode the data written to it in
|
|
@ -10,7 +10,7 @@
|
|||
// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it
|
||||
// unsuitable for other protocols. RSA should be used in preference in any
|
||||
// case.
|
||||
package elgamal // import "golang.org/x/crypto/openpgp/elgamal"
|
||||
package elgamal // import "github.com/keybase/go-crypto/openpgp/elgamal"
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
|
@ -3,7 +3,7 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package errors contains common error types for the OpenPGP packages.
|
||||
package errors // import "golang.org/x/crypto/openpgp/errors"
|
||||
package errors // import "github.com/keybase/go-crypto/openpgp/errors"
|
||||
|
||||
import (
|
||||
"strconv"
|
|
@ -5,13 +5,12 @@
|
|||
package openpgp
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"github.com/keybase/go-crypto/openpgp/armor"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
"github.com/keybase/go-crypto/rsa"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/armor"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
// PublicKeyType is the armor type for a PGP public key.
|
||||
|
@ -29,6 +28,7 @@ type Entity struct {
|
|||
Identities map[string]*Identity // indexed by Identity.Name
|
||||
Revocations []*packet.Signature
|
||||
Subkeys []Subkey
|
||||
BadSubkeys []BadSubkey
|
||||
}
|
||||
|
||||
// An Identity represents an identity claimed by an Entity and zero or more
|
||||
|
@ -46,6 +46,14 @@ type Subkey struct {
|
|||
PublicKey *packet.PublicKey
|
||||
PrivateKey *packet.PrivateKey
|
||||
Sig *packet.Signature
|
||||
Revocation *packet.Signature
|
||||
}
|
||||
|
||||
// BadSubkey is one that failed reconstruction, but we'll keep it around for
|
||||
// informational purposes.
|
||||
type BadSubkey struct {
|
||||
Subkey
|
||||
Err error
|
||||
}
|
||||
|
||||
// A Key identifies a specific public key in an Entity. This is either the
|
||||
|
@ -94,10 +102,23 @@ func (e *Entity) encryptionKey(now time.Time) (Key, bool) {
|
|||
// Iterate the keys to find the newest key
|
||||
var maxTime time.Time
|
||||
for i, subkey := range e.Subkeys {
|
||||
if subkey.Sig.FlagsValid &&
|
||||
subkey.Sig.FlagEncryptCommunications &&
|
||||
|
||||
// NOTE(maxtaco)
|
||||
// If there is a Flags subpacket, then we have to follow it, and only
|
||||
// use keys that are marked for Encryption of Communication. If there
|
||||
// isn't a Flags subpacket, and this is an Encrypt-Only key (right now only ElGamal
|
||||
// suffices), then we implicitly use it. The check for primary below is a little
|
||||
// more open-ended, but for now, let's be strict and potentially open up
|
||||
// if we see bugs in the wild.
|
||||
//
|
||||
// One more note: old DSA/ElGamal keys tend not to have the Flags subpacket,
|
||||
// so this sort of thing is pretty important for encrypting to older keys.
|
||||
//
|
||||
if ((subkey.Sig.FlagsValid && subkey.Sig.FlagEncryptCommunications) ||
|
||||
(!subkey.Sig.FlagsValid && subkey.PublicKey.PubKeyAlgo == packet.PubKeyAlgoElGamal)) &&
|
||||
subkey.PublicKey.PubKeyAlgo.CanEncrypt() &&
|
||||
!subkey.Sig.KeyExpired(now) &&
|
||||
subkey.Revocation == nil &&
|
||||
(maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) {
|
||||
candidateSubkey = i
|
||||
maxTime = subkey.Sig.CreationTime
|
||||
|
@ -113,8 +134,11 @@ func (e *Entity) encryptionKey(now time.Time) (Key, bool) {
|
|||
// the primary key doesn't have any usage metadata then we
|
||||
// assume that the primary key is ok. Or, if the primary key is
|
||||
// marked as ok to encrypt to, then we can obviously use it.
|
||||
//
|
||||
// NOTE(maxtaco) - see note above, how this policy is a little too open-ended
|
||||
// for my liking, but leave it for now.
|
||||
i := e.primaryIdentity()
|
||||
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications &&
|
||||
if (!i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications) &&
|
||||
e.PrimaryKey.PubKeyAlgo.CanEncrypt() &&
|
||||
!i.SelfSignature.KeyExpired(now) {
|
||||
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
|
||||
|
@ -130,9 +154,10 @@ func (e *Entity) signingKey(now time.Time) (Key, bool) {
|
|||
candidateSubkey := -1
|
||||
|
||||
for i, subkey := range e.Subkeys {
|
||||
if subkey.Sig.FlagsValid &&
|
||||
subkey.Sig.FlagSign &&
|
||||
if (!subkey.Sig.FlagsValid || subkey.Sig.FlagSign) &&
|
||||
subkey.PrivateKey.PrivateKey != nil &&
|
||||
subkey.PublicKey.PubKeyAlgo.CanSign() &&
|
||||
subkey.Revocation == nil &&
|
||||
!subkey.Sig.KeyExpired(now) {
|
||||
candidateSubkey = i
|
||||
break
|
||||
|
@ -147,8 +172,10 @@ func (e *Entity) signingKey(now time.Time) (Key, bool) {
|
|||
// If we have no candidate subkey then we assume that it's ok to sign
|
||||
// with the primary key.
|
||||
i := e.primaryIdentity()
|
||||
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign &&
|
||||
!i.SelfSignature.KeyExpired(now) {
|
||||
if (!i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign) &&
|
||||
e.PrimaryKey.PubKeyAlgo.CanSign() &&
|
||||
!i.SelfSignature.KeyExpired(now) &&
|
||||
e.PrivateKey.PrivateKey != nil {
|
||||
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
|
||||
}
|
||||
|
||||
|
@ -176,7 +203,15 @@ func (el EntityList) KeysById(id uint64) (keys []Key) {
|
|||
|
||||
for _, subKey := range e.Subkeys {
|
||||
if subKey.PublicKey.KeyId == id {
|
||||
keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig})
|
||||
|
||||
// If there's both a a revocation and a sig, then take the
|
||||
// revocation. Otherwise, we can proceed with the sig.
|
||||
sig := subKey.Revocation
|
||||
if sig == nil {
|
||||
sig = subKey.Sig
|
||||
}
|
||||
|
||||
keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, sig})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -196,20 +231,43 @@ func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) {
|
|||
continue
|
||||
}
|
||||
|
||||
if key.SelfSignature.FlagsValid && requiredUsage != 0 {
|
||||
if requiredUsage != 0 {
|
||||
var usage byte
|
||||
if key.SelfSignature.FlagCertify {
|
||||
usage |= packet.KeyFlagCertify
|
||||
}
|
||||
if key.SelfSignature.FlagSign {
|
||||
usage |= packet.KeyFlagSign
|
||||
}
|
||||
if key.SelfSignature.FlagEncryptCommunications {
|
||||
|
||||
switch {
|
||||
case key.SelfSignature.FlagsValid:
|
||||
if key.SelfSignature.FlagCertify {
|
||||
usage |= packet.KeyFlagCertify
|
||||
}
|
||||
if key.SelfSignature.FlagSign {
|
||||
usage |= packet.KeyFlagSign
|
||||
}
|
||||
if key.SelfSignature.FlagEncryptCommunications {
|
||||
usage |= packet.KeyFlagEncryptCommunications
|
||||
}
|
||||
if key.SelfSignature.FlagEncryptStorage {
|
||||
usage |= packet.KeyFlagEncryptStorage
|
||||
}
|
||||
|
||||
case key.PublicKey.PubKeyAlgo == packet.PubKeyAlgoElGamal:
|
||||
// We also need to handle the case where, although the sig's
|
||||
// flags aren't valid, the key can is implicitly usable for
|
||||
// encryption by virtue of being ElGamal. See also the comment
|
||||
// in encryptionKey() above.
|
||||
usage |= packet.KeyFlagEncryptCommunications
|
||||
}
|
||||
if key.SelfSignature.FlagEncryptStorage {
|
||||
usage |= packet.KeyFlagEncryptStorage
|
||||
|
||||
case key.PublicKey.PubKeyAlgo == packet.PubKeyAlgoDSA:
|
||||
usage |= packet.KeyFlagSign
|
||||
|
||||
// For a primary RSA key without any key flags, be as permissiable
|
||||
// as possible.
|
||||
case key.PublicKey.PubKeyAlgo == packet.PubKeyAlgoRSA &&
|
||||
key.Entity.PrimaryKey.KeyId == id:
|
||||
usage = (packet.KeyFlagCertify | packet.KeyFlagSign |
|
||||
packet.KeyFlagEncryptCommunications | packet.KeyFlagEncryptStorage)
|
||||
}
|
||||
|
||||
if usage&requiredUsage != requiredUsage {
|
||||
continue
|
||||
}
|
||||
|
@ -224,7 +282,7 @@ func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) {
|
|||
func (el EntityList) DecryptionKeys() (keys []Key) {
|
||||
for _, e := range el {
|
||||
for _, subKey := range e.Subkeys {
|
||||
if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) {
|
||||
if subKey.PrivateKey != nil && subKey.PrivateKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) {
|
||||
keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig})
|
||||
}
|
||||
}
|
||||
|
@ -346,45 +404,93 @@ EachPacket:
|
|||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch pkt := p.(type) {
|
||||
case *packet.UserId:
|
||||
|
||||
// Make a new Identity object, that we might wind up throwing away.
|
||||
// We'll only add it if we get a valid self-signature over this
|
||||
// userID.
|
||||
current = new(Identity)
|
||||
current.Name = pkt.Id
|
||||
current.UserId = pkt
|
||||
e.Identities[pkt.Id] = current
|
||||
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if err == io.EOF {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig, ok := p.(*packet.Signature)
|
||||
if !ok {
|
||||
return nil, errors.StructuralError("user ID packet not followed by self-signature")
|
||||
}
|
||||
|
||||
if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId {
|
||||
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil {
|
||||
return nil, errors.StructuralError("user ID self-signature invalid: " + err.Error())
|
||||
}
|
||||
current.SelfSignature = sig
|
||||
break
|
||||
}
|
||||
current.Signatures = append(current.Signatures, sig)
|
||||
}
|
||||
case *packet.Signature:
|
||||
if pkt.SigType == packet.SigTypeKeyRevocation {
|
||||
|
||||
// These are signatures by other people on this key. Let's just ignore them
|
||||
// from the beginning, since they shouldn't affect our key decoding one way
|
||||
// or the other.
|
||||
if pkt.IssuerKeyId != nil && *pkt.IssuerKeyId != e.PrimaryKey.KeyId {
|
||||
continue
|
||||
}
|
||||
|
||||
// If this is a signature made by the keyholder, and the signature has stubbed out
|
||||
// critical packets, then *now* we need to bail out.
|
||||
if e := pkt.StubbedOutCriticalError; e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
// Next handle the case of a self-signature. According to RFC8440,
|
||||
// Section 5.2.3.3, if there are several self-signatures,
|
||||
// we should take the newer one. If they were both created
|
||||
// at the same time, but one of them has keyflags specified and the
|
||||
// other doesn't, keep the one with the keyflags. We have actually
|
||||
// seen this in the wild (see the 'Yield' test in read_test.go).
|
||||
// If there is a tie, and both have the same value for FlagsValid,
|
||||
// then "last writer wins."
|
||||
//
|
||||
// HOWEVER! We have seen yet more keys in the wild (see the 'Spiros'
|
||||
// test in read_test.go), in which the later self-signature is a bunch
|
||||
// of junk, and doesn't even specify key flags. Does it really make
|
||||
// sense to overwrite reasonable key flags with the empty set? I'm not
|
||||
// sure what that would be trying to achieve, and plus GPG seems to be
|
||||
// ok with this situation, and ignores the later (empty) keyflag set.
|
||||
// So further tighten our overwrite rules, and only allow the later
|
||||
// signature to overwrite the earlier signature if so doing won't
|
||||
// trash the key flags.
|
||||
if current != nil &&
|
||||
(current.SelfSignature == nil ||
|
||||
(!pkt.CreationTime.Before(current.SelfSignature.CreationTime) &&
|
||||
(pkt.FlagsValid || !current.SelfSignature.FlagsValid))) &&
|
||||
(pkt.SigType == packet.SigTypePositiveCert || pkt.SigType == packet.SigTypeGenericCert) &&
|
||||
pkt.IssuerKeyId != nil &&
|
||||
*pkt.IssuerKeyId == e.PrimaryKey.KeyId {
|
||||
|
||||
if err = e.PrimaryKey.VerifyUserIdSignature(current.Name, e.PrimaryKey, pkt); err == nil {
|
||||
|
||||
current.SelfSignature = pkt
|
||||
|
||||
// NOTE(maxtaco) 2016.01.11
|
||||
// Only register an identity once we've gotten a valid self-signature.
|
||||
// It's possible therefore for us to throw away `current` in the case
|
||||
// no valid self-signatures were found. That's OK as long as there are
|
||||
// other identies that make sense.
|
||||
//
|
||||
// NOTE! We might later see a revocation for this very same UID, and it
|
||||
// won't be undone. We've preserved this feature from the original
|
||||
// Google OpenPGP we forked from.
|
||||
e.Identities[current.Name] = current
|
||||
} else {
|
||||
// We really should warn that there was a failure here. Not raise an error
|
||||
// since this really shouldn't be a fail-stop error.
|
||||
}
|
||||
} else if pkt.SigType == packet.SigTypeKeyRevocation {
|
||||
// These revocations won't revoke UIDs as handled above, so lookout!
|
||||
revocations = append(revocations, pkt)
|
||||
} else if pkt.SigType == packet.SigTypeDirectSignature {
|
||||
// TODO: RFC4880 5.2.1 permits signatures
|
||||
// directly on keys (eg. to bind additional
|
||||
// revocation keys).
|
||||
} else if current == nil {
|
||||
return nil, errors.StructuralError("signature packet found before user id packet")
|
||||
// NOTE(maxtaco)
|
||||
//
|
||||
// See https://github.com/keybase/client/issues/2666
|
||||
//
|
||||
// There might have been a user attribute picture before this signature,
|
||||
// in which case this is still a valid PGP key. In the future we might
|
||||
// not ignore user attributes (like picture). But either way, it doesn't
|
||||
// make sense to bail out here. Keep looking for other valid signatures.
|
||||
//
|
||||
// Used to be:
|
||||
// return nil, errors.StructuralError("signature packet found before user id packet")
|
||||
} else {
|
||||
current.Signatures = append(current.Signatures, pkt)
|
||||
}
|
||||
|
@ -432,26 +538,59 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p
|
|||
var subKey Subkey
|
||||
subKey.PublicKey = pub
|
||||
subKey.PrivateKey = priv
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
return io.ErrUnexpectedEOF
|
||||
var lastErr error
|
||||
for {
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
sig, ok := p.(*packet.Signature)
|
||||
if !ok {
|
||||
// Hit a non-signature packet, so assume we're up to the next key
|
||||
packets.Unread(p)
|
||||
break
|
||||
}
|
||||
if st := sig.SigType; st != packet.SigTypeSubkeyBinding && st != packet.SigTypeSubkeyRevocation {
|
||||
|
||||
// Note(maxtaco):
|
||||
// We used to error out here, but instead, let's fast-forward past
|
||||
// packets that are in the wrong place (like misplaced 0x13 signatures)
|
||||
// until we get to one that works. For a test case,
|
||||
// see TestWithBadSubkeySignaturePackets.
|
||||
|
||||
continue
|
||||
}
|
||||
err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, sig)
|
||||
if err != nil {
|
||||
// Non valid signature, so again, no need to abandon all hope, just continue;
|
||||
// make a note of the error we hit.
|
||||
lastErr = errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
continue
|
||||
}
|
||||
switch sig.SigType {
|
||||
case packet.SigTypeSubkeyBinding:
|
||||
// First writer wins
|
||||
if subKey.Sig == nil {
|
||||
subKey.Sig = sig
|
||||
}
|
||||
case packet.SigTypeSubkeyRevocation:
|
||||
// First writer wins
|
||||
if subKey.Revocation == nil {
|
||||
subKey.Revocation = sig
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
if subKey.Sig != nil {
|
||||
e.Subkeys = append(e.Subkeys, subKey)
|
||||
} else {
|
||||
if lastErr == nil {
|
||||
lastErr = errors.StructuralError("Subkey wasn't signed; expected a 'binding' signature")
|
||||
}
|
||||
e.BadSubkeys = append(e.BadSubkeys, BadSubkey{Subkey: subKey, Err: lastErr})
|
||||
}
|
||||
var ok bool
|
||||
subKey.Sig, ok = p.(*packet.Signature)
|
||||
if !ok {
|
||||
return errors.StructuralError("subkey packet not followed by signature")
|
||||
}
|
||||
if subKey.Sig.SigType != packet.SigTypeSubkeyBinding && subKey.Sig.SigType != packet.SigTypeSubkeyRevocation {
|
||||
return errors.StructuralError("subkey signature with wrong type")
|
||||
}
|
||||
err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig)
|
||||
if err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
e.Subkeys = append(e.Subkeys, subKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -539,9 +678,11 @@ func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
if e.PrivateKey.PrivateKey != nil {
|
||||
err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = ident.SelfSignature.Serialize(w)
|
||||
if err != nil {
|
||||
|
@ -553,10 +694,23 @@ func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
// Workaround shortcoming of SignKey(), which doesn't work to reverse-sign
|
||||
// sub-signing keys. So if requested, just reuse the signatures already
|
||||
// available to us (if we read this key from a keyring).
|
||||
if e.PrivateKey.PrivateKey != nil && !config.ReuseSignatures() {
|
||||
err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if subkey.Revocation != nil {
|
||||
err = subkey.Revocation.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = subkey.Sig.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -593,6 +747,13 @@ func (e *Entity) Serialize(w io.Writer) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if subkey.Revocation != nil {
|
||||
err = subkey.Revocation.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = subkey.Sig.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -631,3 +792,21 @@ func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Co
|
|||
ident.Signatures = append(ident.Signatures, sig)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopySubkeyRevocations copies subkey revocations from the src Entity over
|
||||
// to the receiver entity. We need this because `gpg --export-secret-key` does
|
||||
// not appear to output subkey revocations. In this case we need to manually
|
||||
// merge with the output of `gpg --export`.
|
||||
func (e *Entity) CopySubkeyRevocations(src *Entity) {
|
||||
m := make(map[[20]byte]*packet.Signature)
|
||||
for _, subkey := range src.Subkeys {
|
||||
if subkey.Revocation != nil {
|
||||
m[subkey.PublicKey.Fingerprint] = subkey.Revocation
|
||||
}
|
||||
}
|
||||
for i, subkey := range e.Subkeys {
|
||||
if r := m[subkey.PublicKey.Fingerprint]; r != nil {
|
||||
e.Subkeys[i].Revocation = r
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,9 +8,10 @@ import (
|
|||
"compress/bzip2"
|
||||
"compress/flate"
|
||||
"compress/zlib"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// Compressed represents a compressed OpenPGP packet. The decompressed contents
|
|
@ -46,6 +46,9 @@ type Config struct {
|
|||
// RSABits is the number of bits in new RSA keys made with NewEntity.
|
||||
// If zero, then 2048 bit keys are created.
|
||||
RSABits int
|
||||
// ReuseSignatures tells us to reuse existing Signatures
|
||||
// on serialized output.
|
||||
ReuseSignaturesOnSerialize bool
|
||||
}
|
||||
|
||||
func (c *Config) Random() io.Reader {
|
||||
|
@ -89,3 +92,7 @@ func (c *Config) PasswordHashIterations() int {
|
|||
}
|
||||
return c.S2KCount
|
||||
}
|
||||
|
||||
func (c *Config) ReuseSignatures() bool {
|
||||
return c != nil && c.ReuseSignaturesOnSerialize
|
||||
}
|
|
@ -5,14 +5,14 @@
|
|||
package packet
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/openpgp/elgamal"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/elgamal"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/rsa"
|
||||
)
|
||||
|
||||
const encryptedKeyVersion = 3
|
|
@ -7,10 +7,11 @@ package packet
|
|||
import (
|
||||
"crypto"
|
||||
"encoding/binary"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/s2k"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/s2k"
|
||||
)
|
||||
|
||||
// OnePassSignature represents a one-pass signature packet. See RFC 4880,
|
|
@ -9,7 +9,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is
|
|
@ -4,17 +4,18 @@
|
|||
|
||||
// Package packet implements parsing and serialization of OpenPGP packets, as
|
||||
// specified in RFC 4880.
|
||||
package packet // import "golang.org/x/crypto/openpgp/packet"
|
||||
package packet // import "github.com/keybase/go-crypto/openpgp/packet"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"golang.org/x/crypto/cast5"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/keybase/go-crypto/cast5"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// readFull is the same as io.ReadFull except that reading zero bytes returns
|
||||
|
@ -412,6 +413,8 @@ const (
|
|||
// RFC 6637, Section 5.
|
||||
PubKeyAlgoECDH PublicKeyAlgorithm = 18
|
||||
PubKeyAlgoECDSA PublicKeyAlgorithm = 19
|
||||
// RFC -1
|
||||
PubKeyAlgoEdDSA PublicKeyAlgorithm = 22
|
||||
)
|
||||
|
||||
// CanEncrypt returns true if it's possible to encrypt a message to a public
|
||||
|
@ -428,7 +431,7 @@ func (pka PublicKeyAlgorithm) CanEncrypt() bool {
|
|||
// sign a message.
|
||||
func (pka PublicKeyAlgorithm) CanSign() bool {
|
||||
switch pka {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA:
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA:
|
||||
return true
|
||||
}
|
||||
return false
|
|
@ -9,7 +9,6 @@ import (
|
|||
"crypto/cipher"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -17,9 +16,10 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/elgamal"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/s2k"
|
||||
"github.com/keybase/go-crypto/openpgp/elgamal"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/s2k"
|
||||
"github.com/keybase/go-crypto/rsa"
|
||||
)
|
||||
|
||||
// PrivateKey represents a possibly encrypted private key. See RFC 4880,
|
||||
|
@ -94,10 +94,16 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
|||
if s2kType == 254 {
|
||||
pk.sha1Checksum = true
|
||||
}
|
||||
// S2K == nil implies that we got a "GNU Dummy" S2K. For instance,
|
||||
// because our master secret key is on a USB key in a vault somewhere.
|
||||
// In that case, there is no further data to consume here.
|
||||
if pk.s2k == nil {
|
||||
pk.Encrypted = false
|
||||
return
|
||||
}
|
||||
default:
|
||||
return errors.UnsupportedError("deprecated s2k function in private key")
|
||||
}
|
||||
|
||||
if pk.Encrypted {
|
||||
blockSize := pk.cipher.blockSize()
|
||||
if blockSize == 0 {
|
||||
|
@ -137,24 +143,36 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf.WriteByte(0 /* no encryption */)
|
||||
|
||||
privateKeyBuf := bytes.NewBuffer(nil)
|
||||
|
||||
switch priv := pk.PrivateKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
err = serializeRSAPrivateKey(privateKeyBuf, priv)
|
||||
case *dsa.PrivateKey:
|
||||
err = serializeDSAPrivateKey(privateKeyBuf, priv)
|
||||
case *elgamal.PrivateKey:
|
||||
err = serializeElGamalPrivateKey(privateKeyBuf, priv)
|
||||
case *ecdsa.PrivateKey:
|
||||
err = serializeECDSAPrivateKey(privateKeyBuf, priv)
|
||||
default:
|
||||
err = errors.InvalidArgumentError("unknown private key type")
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
if pk.PrivateKey == nil {
|
||||
_, err = buf.Write([]byte{
|
||||
254, // SHA-1 Convention
|
||||
9, // Encryption scheme (AES256)
|
||||
101, // GNU Extensions
|
||||
2, // Hash value (SHA1)
|
||||
'G', 'N', 'U', // "GNU" as a string
|
||||
1, // Extension type 1001 (minus 1000)
|
||||
})
|
||||
} else {
|
||||
buf.WriteByte(0 /* no encryption */)
|
||||
|
||||
switch priv := pk.PrivateKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
err = serializeRSAPrivateKey(privateKeyBuf, priv)
|
||||
case *dsa.PrivateKey:
|
||||
err = serializeDSAPrivateKey(privateKeyBuf, priv)
|
||||
case *elgamal.PrivateKey:
|
||||
err = serializeElGamalPrivateKey(privateKeyBuf, priv)
|
||||
case *ecdsa.PrivateKey:
|
||||
err = serializeECDSAPrivateKey(privateKeyBuf, priv)
|
||||
default:
|
||||
err = errors.InvalidArgumentError("unknown private key type")
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ptype := packetTypePrivateKey
|
||||
|
@ -176,11 +194,13 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
checksum := mod64kHash(privateKeyBytes)
|
||||
var checksumBytes [2]byte
|
||||
checksumBytes[0] = byte(checksum >> 8)
|
||||
checksumBytes[1] = byte(checksum)
|
||||
_, err = w.Write(checksumBytes[:])
|
||||
if len(privateKeyBytes) > 0 {
|
||||
checksum := mod64kHash(privateKeyBytes)
|
||||
var checksumBytes [2]byte
|
||||
checksumBytes[0] = byte(checksum >> 8)
|
||||
checksumBytes[1] = byte(checksum)
|
||||
_, err = w.Write(checksumBytes[:])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -218,6 +238,10 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) error {
|
|||
if !pk.Encrypted {
|
||||
return nil
|
||||
}
|
||||
// For GNU Dummy S2K, there's no key here, so don't do anything.
|
||||
if pk.s2k == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
key := make([]byte, pk.cipher.KeySize())
|
||||
pk.s2k(key, passphrase)
|
||||
|
@ -344,6 +368,8 @@ func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) {
|
|||
|
||||
func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) {
|
||||
ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey)
|
||||
ecdsaPriv := new(ecdsa.PrivateKey)
|
||||
ecdsaPriv.PublicKey = *ecdsaPub
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
d, _, err := readMPI(buf)
|
||||
|
@ -351,10 +377,8 @@ func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
pk.PrivateKey = &ecdsa.PrivateKey{
|
||||
PublicKey: *ecdsaPub,
|
||||
D: new(big.Int).SetBytes(d),
|
||||
}
|
||||
ecdsaPriv.D = new(big.Int).SetBytes(d)
|
||||
pk.PrivateKey = ecdsaPriv
|
||||
pk.Encrypted = false
|
||||
pk.encryptedData = nil
|
||||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
_ "crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
|
@ -22,8 +21,11 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/elgamal"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"github.com/agl/ed25519"
|
||||
"github.com/keybase/go-crypto/brainpool"
|
||||
"github.com/keybase/go-crypto/openpgp/elgamal"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/rsa"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -33,9 +35,17 @@ var (
|
|||
oidCurveP384 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x22}
|
||||
// NIST curve P-521
|
||||
oidCurveP521 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x23}
|
||||
// Brainpool curve P-256r1
|
||||
oidCurveP256r1 []byte = []byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07}
|
||||
// Brainpool curve P-384r1
|
||||
oidCurveP384r1 []byte = []byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B}
|
||||
// Brainpool curve P-512r1
|
||||
oidCurveP512r1 []byte = []byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D}
|
||||
// EdDSA
|
||||
oidEdDSA []byte = []byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}
|
||||
)
|
||||
|
||||
const maxOIDLength = 8
|
||||
const maxOIDLength = 10
|
||||
|
||||
// ecdsaKey stores the algorithm-specific fields for ECDSA keys.
|
||||
// as defined in RFC 6637, Section 9.
|
||||
|
@ -46,6 +56,22 @@ type ecdsaKey struct {
|
|||
p parsedMPI
|
||||
}
|
||||
|
||||
type edDSAkey struct {
|
||||
ecdsaKey
|
||||
}
|
||||
|
||||
func (e *edDSAkey) Verify(payload []byte, r parsedMPI, s parsedMPI) bool {
|
||||
var key [ed25519.PublicKeySize]byte
|
||||
var sig [ed25519.SignatureSize]byte
|
||||
|
||||
// note(mnk): I'm not entirely sure why we need to ignore the first byte.
|
||||
copy(key[:], e.p.bytes[1:])
|
||||
n := copy(sig[:], r.bytes)
|
||||
copy(sig[n:], s.bytes)
|
||||
|
||||
return ed25519.Verify(&key, payload, &sig)
|
||||
}
|
||||
|
||||
// parseOID reads the OID for the curve as defined in RFC 6637, Section 9.
|
||||
func parseOID(r io.Reader) (oid []byte, err error) {
|
||||
buf := make([]byte, maxOIDLength)
|
||||
|
@ -88,6 +114,12 @@ func (f *ecdsaKey) newECDSA() (*ecdsa.PublicKey, error) {
|
|||
c = elliptic.P384()
|
||||
} else if bytes.Equal(f.oid, oidCurveP521) {
|
||||
c = elliptic.P521()
|
||||
} else if bytes.Equal(f.oid, oidCurveP256r1) {
|
||||
c = brainpool.P256r1()
|
||||
} else if bytes.Equal(f.oid, oidCurveP384r1) {
|
||||
c = brainpool.P384r1()
|
||||
} else if bytes.Equal(f.oid, oidCurveP512r1) {
|
||||
c = brainpool.P512r1()
|
||||
} else {
|
||||
return nil, errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", f.oid))
|
||||
}
|
||||
|
@ -163,6 +195,9 @@ type PublicKey struct {
|
|||
// RFC 6637 fields
|
||||
ec *ecdsaKey
|
||||
ecdh *ecdhKdf
|
||||
|
||||
// EdDSA fields (no RFC available), uses ecdsa scaffolding
|
||||
edk *edDSAkey
|
||||
}
|
||||
|
||||
// signingKey provides a convenient abstraction over signature verification
|
||||
|
@ -172,21 +207,28 @@ type signingKey interface {
|
|||
serializeWithoutHeaders(io.Writer) error
|
||||
}
|
||||
|
||||
func fromBig(n *big.Int) parsedMPI {
|
||||
func FromBig(n *big.Int) parsedMPI {
|
||||
return parsedMPI{
|
||||
bytes: n.Bytes(),
|
||||
bitLength: uint16(n.BitLen()),
|
||||
}
|
||||
}
|
||||
|
||||
func FromBytes(bytes []byte) parsedMPI {
|
||||
return parsedMPI{
|
||||
bytes: bytes,
|
||||
bitLength: uint16(8 * len(bytes)),
|
||||
}
|
||||
}
|
||||
|
||||
// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey.
|
||||
func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoRSA,
|
||||
PublicKey: pub,
|
||||
n: fromBig(pub.N),
|
||||
e: fromBig(big.NewInt(int64(pub.E))),
|
||||
n: FromBig(pub.N),
|
||||
e: FromBig(big.NewInt(int64(pub.E))),
|
||||
}
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
|
@ -199,25 +241,35 @@ func NewDSAPublicKey(creationTime time.Time, pub *dsa.PublicKey) *PublicKey {
|
|||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoDSA,
|
||||
PublicKey: pub,
|
||||
p: fromBig(pub.P),
|
||||
q: fromBig(pub.Q),
|
||||
g: fromBig(pub.G),
|
||||
y: fromBig(pub.Y),
|
||||
p: FromBig(pub.P),
|
||||
q: FromBig(pub.Q),
|
||||
g: FromBig(pub.G),
|
||||
y: FromBig(pub.Y),
|
||||
}
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
// check EdDSA public key material.
|
||||
// There is currently no RFC for it, but it doesn't mean it's not
|
||||
// implemented or in use.
|
||||
func (e *edDSAkey) check() error {
|
||||
if !bytes.Equal(e.oid, oidEdDSA) {
|
||||
return errors.UnsupportedError(fmt.Sprintf("Bad OID for EdDSA key: %v", e.oid))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewElGamalPublicKey returns a PublicKey that wraps the given elgamal.PublicKey.
|
||||
func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoElGamal,
|
||||
PublicKey: pub,
|
||||
p: fromBig(pub.P),
|
||||
g: fromBig(pub.G),
|
||||
y: fromBig(pub.Y),
|
||||
p: FromBig(pub.P),
|
||||
g: FromBig(pub.G),
|
||||
y: FromBig(pub.Y),
|
||||
}
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
|
@ -231,7 +283,6 @@ func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey
|
|||
PublicKey: pub,
|
||||
ec: new(ecdsaKey),
|
||||
}
|
||||
|
||||
switch pub.Curve {
|
||||
case elliptic.P256():
|
||||
pk.ec.oid = oidCurveP256
|
||||
|
@ -239,10 +290,13 @@ func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey
|
|||
pk.ec.oid = oidCurveP384
|
||||
case elliptic.P521():
|
||||
pk.ec.oid = oidCurveP521
|
||||
default:
|
||||
panic("unknown elliptic curve")
|
||||
case brainpool.P256r1():
|
||||
pk.ec.oid = oidCurveP256r1
|
||||
case brainpool.P384r1():
|
||||
pk.ec.oid = oidCurveP384r1
|
||||
case brainpool.P512r1():
|
||||
pk.ec.oid = oidCurveP512r1
|
||||
}
|
||||
|
||||
pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
pk.ec.p.bitLength = uint16(8 * len(pk.ec.p.bytes))
|
||||
|
||||
|
@ -269,6 +323,12 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
|
|||
err = pk.parseDSA(r)
|
||||
case PubKeyAlgoElGamal:
|
||||
err = pk.parseElGamal(r)
|
||||
case PubKeyAlgoEdDSA:
|
||||
pk.edk = &edDSAkey{}
|
||||
if err = pk.edk.parse(r); err != nil {
|
||||
return err
|
||||
}
|
||||
err = pk.edk.check()
|
||||
case PubKeyAlgoECDSA:
|
||||
pk.ec = new(ecdsaKey)
|
||||
if err = pk.ec.parse(r); err != nil {
|
||||
|
@ -318,7 +378,7 @@ func (pk *PublicKey) parseRSA(r io.Reader) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
if len(pk.e.bytes) > 3 {
|
||||
if len(pk.e.bytes) > 7 {
|
||||
err = errors.UnsupportedError("large public exponent")
|
||||
return
|
||||
}
|
||||
|
@ -328,7 +388,7 @@ func (pk *PublicKey) parseRSA(r io.Reader) (err error) {
|
|||
}
|
||||
for i := 0; i < len(pk.e.bytes); i++ {
|
||||
rsa.E <<= 8
|
||||
rsa.E |= int(pk.e.bytes[i])
|
||||
rsa.E |= int64(pk.e.bytes[i])
|
||||
}
|
||||
pk.PublicKey = rsa
|
||||
return
|
||||
|
@ -410,6 +470,8 @@ func (pk *PublicKey) SerializeSignaturePrefix(h io.Writer) {
|
|||
case PubKeyAlgoECDH:
|
||||
pLength += uint16(pk.ec.byteLen())
|
||||
pLength += uint16(pk.ecdh.byteLen())
|
||||
case PubKeyAlgoEdDSA:
|
||||
pLength += uint16(pk.edk.byteLen())
|
||||
default:
|
||||
panic("unknown public key algorithm")
|
||||
}
|
||||
|
@ -439,6 +501,8 @@ func (pk *PublicKey) Serialize(w io.Writer) (err error) {
|
|||
case PubKeyAlgoECDH:
|
||||
length += pk.ec.byteLen()
|
||||
length += pk.ecdh.byteLen()
|
||||
case PubKeyAlgoEdDSA:
|
||||
length += pk.edk.byteLen()
|
||||
default:
|
||||
panic("unknown public key algorithm")
|
||||
}
|
||||
|
@ -480,6 +544,8 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
|
|||
return writeMPIs(w, pk.p, pk.g, pk.y)
|
||||
case PubKeyAlgoECDSA:
|
||||
return pk.ec.serialize(w)
|
||||
case PubKeyAlgoEdDSA:
|
||||
return pk.edk.serialize(w)
|
||||
case PubKeyAlgoECDH:
|
||||
if err = pk.ec.serialize(w); err != nil {
|
||||
return
|
||||
|
@ -537,6 +603,11 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
|
|||
return errors.SignatureError("ECDSA verification failure")
|
||||
}
|
||||
return nil
|
||||
case PubKeyAlgoEdDSA:
|
||||
if !pk.edk.Verify(hashBytes, sig.EdDSASigR, sig.EdDSASigS) {
|
||||
return errors.SignatureError("EdDSA verification failure")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return errors.SignatureError("Unsupported public key algorithm used in signature")
|
||||
}
|
||||
|
@ -596,12 +667,18 @@ func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash,
|
|||
}
|
||||
h = hashFunc.New()
|
||||
|
||||
updateKeySignatureHash(pk, signed, h)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// updateKeySignatureHash does the actual hash updates for keySignatureHash.
|
||||
func updateKeySignatureHash(pk, signed signingKey, h hash.Hash) {
|
||||
// RFC 4880, section 5.2.4
|
||||
pk.SerializeSignaturePrefix(h)
|
||||
pk.serializeWithoutHeaders(h)
|
||||
signed.SerializeSignaturePrefix(h)
|
||||
signed.serializeWithoutHeaders(h)
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyKeySignature returns nil iff sig is a valid signature, made by this
|
||||
|
@ -616,6 +693,19 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error
|
|||
}
|
||||
|
||||
if sig.FlagSign {
|
||||
|
||||
// BUG(maxtaco)
|
||||
//
|
||||
// We should check for more than FlagsSign here, because if
|
||||
// you read keys.go, we can sometimes use signing subkeys even if they're
|
||||
// not explicitly flagged as such. However, so doing fails lots of currently
|
||||
// working tests, so I'm not going to do much here.
|
||||
//
|
||||
// In other words, we should have this disjunction in the condition above:
|
||||
//
|
||||
// || (!sig.FlagsValid && pk.PubKeyAlgo.CanSign()) {
|
||||
//
|
||||
|
||||
// Signing subkeys must be cross-signed. See
|
||||
// https://www.gnupg.org/faq/subkey-cross-certify.html.
|
||||
if sig.EmbeddedSignature == nil {
|
||||
|
@ -658,6 +748,19 @@ func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) {
|
|||
return pk.VerifySignature(h, sig)
|
||||
}
|
||||
|
||||
type teeHash struct {
|
||||
h hash.Hash
|
||||
}
|
||||
|
||||
func (t teeHash) Write(b []byte) (n int, err error) {
|
||||
fmt.Printf("hash -> %s %+v\n", string(b), b)
|
||||
return t.h.Write(b)
|
||||
}
|
||||
func (t teeHash) Sum(b []byte) []byte { return t.h.Sum(b) }
|
||||
func (t teeHash) Reset() { t.h.Reset() }
|
||||
func (t teeHash) Size() int { return t.h.Size() }
|
||||
func (t teeHash) BlockSize() int { return t.h.BlockSize() }
|
||||
|
||||
// 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) {
|
||||
|
@ -666,6 +769,14 @@ func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash
|
|||
}
|
||||
h = hashFunc.New()
|
||||
|
||||
updateUserIdSignatureHash(id, pk, h)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// updateUserIdSignatureHash does the actual hash updates for
|
||||
// userIdSignatureHash.
|
||||
func updateUserIdSignatureHash(id string, pk *PublicKey, h hash.Hash) {
|
||||
// RFC 4880, section 5.2.4
|
||||
pk.SerializeSignaturePrefix(h)
|
||||
pk.serializeWithoutHeaders(h)
|
|
@ -7,7 +7,6 @@ package packet
|
|||
import (
|
||||
"crypto"
|
||||
"crypto/md5"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
@ -16,7 +15,8 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/rsa"
|
||||
)
|
||||
|
||||
// PublicKeyV3 represents older, version 3 public keys. These keys are less secure and
|
||||
|
@ -42,8 +42,8 @@ func newRSAPublicKeyV3(creationTime time.Time, pub *rsa.PublicKey) *PublicKeyV3
|
|||
pk := &PublicKeyV3{
|
||||
CreationTime: creationTime,
|
||||
PublicKey: pub,
|
||||
n: fromBig(pub.N),
|
||||
e: fromBig(big.NewInt(int64(pub.E))),
|
||||
n: FromBig(pub.N),
|
||||
e: FromBig(big.NewInt(int64(pub.E))),
|
||||
}
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
|
@ -100,14 +100,14 @@ func (pk *PublicKeyV3) parseRSA(r io.Reader) (err error) {
|
|||
if len(pk.n.bytes) < 8 {
|
||||
return errors.StructuralError("v3 public key modulus is too short")
|
||||
}
|
||||
if len(pk.e.bytes) > 3 {
|
||||
if len(pk.e.bytes) > 7 {
|
||||
err = errors.UnsupportedError("large public exponent")
|
||||
return
|
||||
}
|
||||
rsa := &rsa.PublicKey{N: new(big.Int).SetBytes(pk.n.bytes)}
|
||||
for i := 0; i < len(pk.e.bytes); i++ {
|
||||
rsa.E <<= 8
|
||||
rsa.E |= int(pk.e.bytes[i])
|
||||
rsa.E |= int64(pk.e.bytes[i])
|
||||
}
|
||||
pk.PublicKey = rsa
|
||||
return
|
|
@ -5,8 +5,9 @@
|
|||
package packet
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"io"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// Reader reads packets from an io.Reader and allows packets to be 'unread' so
|
||||
|
@ -46,7 +47,6 @@ func (r *Reader) Next() (p Packet, err error) {
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
|
@ -9,15 +9,15 @@ import (
|
|||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/s2k"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/s2k"
|
||||
"github.com/keybase/go-crypto/rsa"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,6 +28,14 @@ const (
|
|||
KeyFlagEncryptStorage
|
||||
)
|
||||
|
||||
// Signer can be implemented by application code to do actual signing.
|
||||
type Signer interface {
|
||||
hash.Hash
|
||||
Sign(sig *Signature) error
|
||||
KeyId() uint64
|
||||
PublicKeyAlgo() PublicKeyAlgorithm
|
||||
}
|
||||
|
||||
// Signature represents a signature. See RFC 4880, section 5.2.
|
||||
type Signature struct {
|
||||
SigType SignatureType
|
||||
|
@ -44,6 +52,7 @@ type Signature struct {
|
|||
RSASignature parsedMPI
|
||||
DSASigR, DSASigS parsedMPI
|
||||
ECDSASigR, ECDSASigS parsedMPI
|
||||
EdDSASigR, EdDSASigS parsedMPI
|
||||
|
||||
// rawSubpackets contains the unparsed subpackets, in order.
|
||||
rawSubpackets []outputSubpacket
|
||||
|
@ -53,6 +62,7 @@ type Signature struct {
|
|||
|
||||
SigLifetimeSecs, KeyLifetimeSecs *uint32
|
||||
PreferredSymmetric, PreferredHash, PreferredCompression []uint8
|
||||
PreferredKeyServer string
|
||||
IssuerKeyId *uint64
|
||||
IsPrimaryId *bool
|
||||
|
||||
|
@ -66,6 +76,12 @@ type Signature struct {
|
|||
RevocationReason *uint8
|
||||
RevocationReasonText string
|
||||
|
||||
// PolicyURI is optional. See RFC 4880, Section 5.2.3.20 for details
|
||||
PolicyURI string
|
||||
|
||||
// Regex is a regex that can match a PGP UID. See RFC 4880, 5.2.3.14 for details
|
||||
Regex string
|
||||
|
||||
// MDC is set if this signature has a feature packet that indicates
|
||||
// support for MDC subpackets.
|
||||
MDC bool
|
||||
|
@ -75,6 +91,11 @@ type Signature struct {
|
|||
// subkey as their own.
|
||||
EmbeddedSignature *Signature
|
||||
|
||||
// StubbedOutCriticalError is not fail-stop, since it shouldn't break key parsing
|
||||
// when appearing in WoT-style cross signatures. But it should prevent a signature
|
||||
// from being applied to a primary or subkey.
|
||||
StubbedOutCriticalError error
|
||||
|
||||
outSubpackets []outputSubpacket
|
||||
}
|
||||
|
||||
|
@ -97,7 +118,7 @@ func (sig *Signature) parse(r io.Reader) (err error) {
|
|||
sig.SigType = SignatureType(buf[0])
|
||||
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
|
||||
switch sig.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA:
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA:
|
||||
default:
|
||||
err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
|
||||
return
|
||||
|
@ -161,6 +182,11 @@ func (sig *Signature) parse(r io.Reader) (err error) {
|
|||
if err == nil {
|
||||
sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r)
|
||||
}
|
||||
case PubKeyAlgoEdDSA:
|
||||
sig.EdDSASigR.bytes, sig.EdDSASigR.bitLength, err = readMPI(r)
|
||||
if err == nil {
|
||||
sig.EdDSASigS.bytes, sig.EdDSASigS.bitLength, err = readMPI(r)
|
||||
}
|
||||
case PubKeyAlgoECDSA:
|
||||
sig.ECDSASigR.bytes, sig.ECDSASigR.bitLength, err = readMPI(r)
|
||||
if err == nil {
|
||||
|
@ -194,12 +220,15 @@ type signatureSubpacketType uint8
|
|||
const (
|
||||
creationTimeSubpacket signatureSubpacketType = 2
|
||||
signatureExpirationSubpacket signatureSubpacketType = 3
|
||||
regularExpressionSubpacket signatureSubpacketType = 6
|
||||
keyExpirationSubpacket signatureSubpacketType = 9
|
||||
prefSymmetricAlgosSubpacket signatureSubpacketType = 11
|
||||
issuerSubpacket signatureSubpacketType = 16
|
||||
prefHashAlgosSubpacket signatureSubpacketType = 21
|
||||
prefCompressionSubpacket signatureSubpacketType = 22
|
||||
prefKeyServerSubpacket signatureSubpacketType = 24
|
||||
primaryUserIdSubpacket signatureSubpacketType = 25
|
||||
policyURISubpacket signatureSubpacketType = 26
|
||||
keyFlagsSubpacket signatureSubpacketType = 27
|
||||
reasonForRevocationSubpacket signatureSubpacketType = 29
|
||||
featuresSubpacket signatureSubpacketType = 30
|
||||
|
@ -381,6 +410,16 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
|
|||
if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding {
|
||||
return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType)))
|
||||
}
|
||||
case policyURISubpacket:
|
||||
// See RFC 4880, Section 5.2.3.20
|
||||
sig.PolicyURI = string(subpacket[:])
|
||||
case regularExpressionSubpacket:
|
||||
sig.Regex = string(subpacket[:])
|
||||
if isCritical {
|
||||
sig.StubbedOutCriticalError = errors.UnsupportedError("regex support is stubbed out")
|
||||
}
|
||||
case prefKeyServerSubpacket:
|
||||
sig.PreferredKeyServer = string(subpacket[:])
|
||||
default:
|
||||
if isCritical {
|
||||
err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType)))
|
||||
|
@ -508,12 +547,24 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) {
|
|||
// On success, the signature is stored in sig. Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) {
|
||||
signer, hashIsSigner := h.(Signer)
|
||||
|
||||
if !hashIsSigner && (priv == nil || priv.PrivateKey == nil) {
|
||||
err = errors.InvalidArgumentError("attempting to sign with nil PrivateKey")
|
||||
return
|
||||
}
|
||||
|
||||
sig.outSubpackets = sig.buildSubpackets()
|
||||
digest, err := sig.signPrepareHash(h)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if hashIsSigner {
|
||||
err = signer.Sign(sig)
|
||||
return
|
||||
}
|
||||
|
||||
switch priv.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
sig.RSASignature.bytes, err = rsa.SignPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest)
|
||||
|
@ -536,8 +587,8 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
|
|||
case PubKeyAlgoECDSA:
|
||||
r, s, err := ecdsa.Sign(config.Random(), priv.PrivateKey.(*ecdsa.PrivateKey), digest)
|
||||
if err == nil {
|
||||
sig.ECDSASigR = fromBig(r)
|
||||
sig.ECDSASigS = fromBig(s)
|
||||
sig.ECDSASigR = FromBig(r)
|
||||
sig.ECDSASigS = FromBig(s)
|
||||
}
|
||||
default:
|
||||
err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
|
||||
|
@ -558,6 +609,16 @@ func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, co
|
|||
return sig.Sign(h, priv, config)
|
||||
}
|
||||
|
||||
// SignUserIdWithSigner computes a signature from priv, asserting that pub is a
|
||||
// valid key for the identity id. On success, the signature is stored in sig.
|
||||
// Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) SignUserIdWithSigner(id string, pub *PublicKey, s Signer, config *Config) error {
|
||||
updateUserIdSignatureHash(id, pub, s)
|
||||
|
||||
return sig.Sign(s, nil, config)
|
||||
}
|
||||
|
||||
// SignKey computes a signature from priv, asserting that pub is a subkey. On
|
||||
// success, the signature is stored in sig. Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
|
@ -569,6 +630,15 @@ func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config)
|
|||
return sig.Sign(h, priv, config)
|
||||
}
|
||||
|
||||
// SignKeyWithSigner computes a signature using s, asserting that
|
||||
// signeePubKey is a subkey. On success, the signature is stored in sig. Call
|
||||
// Serialize to write it out. If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) SignKeyWithSigner(signeePubKey *PublicKey, signerPubKey *PublicKey, s Signer, config *Config) error {
|
||||
updateKeySignatureHash(signerPubKey, signeePubKey, s)
|
||||
|
||||
return sig.Sign(s, nil, config)
|
||||
}
|
||||
|
||||
// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been
|
||||
// called first.
|
||||
func (sig *Signature) Serialize(w io.Writer) (err error) {
|
||||
|
@ -586,6 +656,9 @@ func (sig *Signature) Serialize(w io.Writer) (err error) {
|
|||
case PubKeyAlgoDSA:
|
||||
sigLength = 2 + len(sig.DSASigR.bytes)
|
||||
sigLength += 2 + len(sig.DSASigS.bytes)
|
||||
case PubKeyAlgoEdDSA:
|
||||
sigLength = 2 + len(sig.EdDSASigR.bytes)
|
||||
sigLength += 2 + len(sig.EdDSASigS.bytes)
|
||||
case PubKeyAlgoECDSA:
|
||||
sigLength = 2 + len(sig.ECDSASigR.bytes)
|
||||
sigLength += 2 + len(sig.ECDSASigS.bytes)
|
||||
|
@ -626,6 +699,8 @@ func (sig *Signature) Serialize(w io.Writer) (err error) {
|
|||
err = writeMPIs(w, sig.RSASignature)
|
||||
case PubKeyAlgoDSA:
|
||||
err = writeMPIs(w, sig.DSASigR, sig.DSASigS)
|
||||
case PubKeyAlgoEdDSA:
|
||||
err = writeMPIs(w, sig.EdDSASigR, sig.EdDSASigS)
|
||||
case PubKeyAlgoECDSA:
|
||||
err = writeMPIs(w, sig.ECDSASigR, sig.ECDSASigS)
|
||||
default:
|
|
@ -12,8 +12,8 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/s2k"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/s2k"
|
||||
)
|
||||
|
||||
// SignatureV3 represents older version 3 signatures. These signatures are less secure
|
|
@ -10,8 +10,8 @@ import (
|
|||
"io"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/s2k"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/s2k"
|
||||
)
|
||||
|
||||
// This is the largest session key that we'll support. Since no 512-bit cipher
|
||||
|
@ -48,6 +48,9 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ske.s2k == nil {
|
||||
return errors.UnsupportedError("can't use dummy S2K for symmetric key encryption")
|
||||
}
|
||||
|
||||
encryptedKey := make([]byte, maxSessionKeySizeInBytes)
|
||||
// The session key may follow. We just have to try and read to find
|
|
@ -8,10 +8,11 @@ import (
|
|||
"crypto/cipher"
|
||||
"crypto/sha1"
|
||||
"crypto/subtle"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"hash"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// SymmetricallyEncrypted represents a symmetrically encrypted byte string. The
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
patch < sig-v3.patch
|
||||
patch < s2k-gnu-dummy.patch
|
||||
find . -type f -name '*.go' -exec sed -i'' -e 's/golang.org\/x\/crypto\/openpgp/github.com\/keybase\/go-crypto\/openpgp/' {} \;
|
||||
find . -type f -name '*.go-e' -exec rm {} \;
|
||||
go test ./...
|
|
@ -3,7 +3,7 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package openpgp implements high level operations on OpenPGP messages.
|
||||
package openpgp // import "golang.org/x/crypto/openpgp"
|
||||
package openpgp // import "github.com/keybase/go-crypto/openpgp"
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
|
@ -12,9 +12,9 @@ import (
|
|||
"io"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/openpgp/armor"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
"github.com/keybase/go-crypto/openpgp/armor"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
// SignatureType is the armor type for a PGP signature.
|
||||
|
@ -361,6 +361,11 @@ func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) {
|
|||
// returns the signer if the signature is valid. If the signer isn't known,
|
||||
// ErrUnknownIssuer is returned.
|
||||
func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) {
|
||||
signer, _, err = checkDetachedSignature(keyring, signed, signature)
|
||||
return signer, err
|
||||
}
|
||||
|
||||
func checkDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, issuer *uint64, err error) {
|
||||
var issuerKeyId uint64
|
||||
var hashFunc crypto.Hash
|
||||
var sigType packet.SignatureType
|
||||
|
@ -371,16 +376,16 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
|
|||
for {
|
||||
p, err = packets.Next()
|
||||
if err == io.EOF {
|
||||
return nil, errors.ErrUnknownIssuer
|
||||
return nil, nil, errors.ErrUnknownIssuer
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch sig := p.(type) {
|
||||
case *packet.Signature:
|
||||
if sig.IssuerKeyId == nil {
|
||||
return nil, errors.StructuralError("signature doesn't have an issuer")
|
||||
return nil, nil, errors.StructuralError("signature doesn't have an issuer")
|
||||
}
|
||||
issuerKeyId = *sig.IssuerKeyId
|
||||
hashFunc = sig.Hash
|
||||
|
@ -390,7 +395,7 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
|
|||
hashFunc = sig.Hash
|
||||
sigType = sig.SigType
|
||||
default:
|
||||
return nil, errors.StructuralError("non signature packet found")
|
||||
return nil, nil, errors.StructuralError("non signature packet found")
|
||||
}
|
||||
|
||||
keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign)
|
||||
|
@ -405,11 +410,11 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
|
|||
|
||||
h, wrappedHash, err := hashForSignature(hashFunc, sigType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(wrappedHash, signed); err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
|
@ -423,20 +428,24 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
|
|||
}
|
||||
|
||||
if err == nil {
|
||||
return key.Entity, nil
|
||||
return key.Entity, &issuerKeyId, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// CheckArmoredDetachedSignature performs the same actions as
|
||||
// CheckDetachedSignature but expects the signature to be armored.
|
||||
func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) {
|
||||
signer, _, err = checkArmoredDetachedSignature(keyring, signed, signature)
|
||||
return signer, err
|
||||
}
|
||||
|
||||
func checkArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, issuer *uint64, err error) {
|
||||
body, err := readArmored(signature, SignatureType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return CheckDetachedSignature(keyring, signed, body)
|
||||
return checkDetachedSignature(keyring, signed, body)
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// Package s2k implements the various OpenPGP string-to-key transforms as
|
||||
// specified in RFC 4800 section 3.7.1.
|
||||
package s2k // import "golang.org/x/crypto/openpgp/s2k"
|
||||
package s2k // import "github.com/keybase/go-crypto/openpgp/s2k"
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
|
@ -12,7 +12,7 @@ import (
|
|||
"io"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// Config collects configuration parameters for s2k key-stretching
|
||||
|
@ -151,6 +151,53 @@ func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) {
|
|||
}
|
||||
}
|
||||
|
||||
func parseGNUExtensions(r io.Reader) (f func(out, in []byte), err error) {
|
||||
var buf [9]byte
|
||||
|
||||
// A three-byte string identifier
|
||||
_, err = io.ReadFull(r, buf[:3])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
gnuExt := string(buf[:3])
|
||||
|
||||
if gnuExt != "GNU" {
|
||||
return nil, errors.UnsupportedError("Malformed GNU extension: " + gnuExt)
|
||||
}
|
||||
_, err = io.ReadFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
gnuExtType := int(buf[0])
|
||||
switch gnuExtType {
|
||||
case 1:
|
||||
return nil, nil
|
||||
case 2:
|
||||
// Read a serial number, which is prefixed by a 1-byte length.
|
||||
// The maximum length is 16.
|
||||
var lenBuf [1]byte
|
||||
_, err = io.ReadFull(r, lenBuf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
maxLen := 16
|
||||
ivLen := int(lenBuf[0])
|
||||
if ivLen > maxLen {
|
||||
ivLen = maxLen
|
||||
}
|
||||
ivBuf := make([]byte, ivLen)
|
||||
// For now we simply discard the IV
|
||||
_, err = io.ReadFull(r, ivBuf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errors.UnsupportedError("unknown S2K GNU protection mode: " + strconv.Itoa(int(gnuExtType)))
|
||||
}
|
||||
}
|
||||
|
||||
// Parse reads a binary specification for a string-to-key transformation from r
|
||||
// and returns a function which performs that transform.
|
||||
func Parse(r io.Reader) (f func(out, in []byte), err error) {
|
||||
|
@ -161,6 +208,12 @@ func Parse(r io.Reader) (f func(out, in []byte), err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// GNU Extensions; handle them before we try to look for a hash, which won't
|
||||
// be needed in most cases anyway.
|
||||
if buf[0] == 101 {
|
||||
return parseGNUExtensions(r)
|
||||
}
|
||||
|
||||
hash, ok := HashIdToHash(buf[1])
|
||||
if !ok {
|
||||
return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1])))
|
||||
|
@ -251,7 +304,7 @@ func HashIdToHash(id byte) (h crypto.Hash, ok bool) {
|
|||
}
|
||||
|
||||
// HashIdToString returns the name of the hash function corresponding to the
|
||||
// given OpenPGP hash id.
|
||||
// given OpenPGP hash id, or panics if id is unknown.
|
||||
func HashIdToString(id byte) (name string, ok bool) {
|
||||
for _, m := range hashToHashIdMapping {
|
||||
if m.id == id {
|
|
@ -0,0 +1,135 @@
|
|||
diff --git a/openpgp/read.go b/openpgp/read.go
|
||||
index a6cecc5..0c9397b 100644
|
||||
--- a/openpgp/read.go
|
||||
+++ b/openpgp/read.go
|
||||
@@ -56,8 +56,9 @@ type MessageDetails struct {
|
||||
// been consumed. Once EOF has been seen, the following fields are
|
||||
// valid. (An authentication code failure is reported as a
|
||||
// SignatureError error when reading from UnverifiedBody.)
|
||||
- SignatureError error // nil if the signature is good.
|
||||
- Signature *packet.Signature // the signature packet itself.
|
||||
+ SignatureError error // nil if the signature is good.
|
||||
+ Signature *packet.Signature // the signature packet itself, if v4 (default)
|
||||
+ SignatureV3 *packet.SignatureV3 // the signature packet if it is a v2 or v3 signature
|
||||
|
||||
decrypted io.ReadCloser
|
||||
}
|
||||
@@ -334,13 +335,15 @@ func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
var ok bool
|
||||
- if scr.md.Signature, ok = p.(*packet.Signature); !ok {
|
||||
+ if scr.md.Signature, ok = p.(*packet.Signature); ok {
|
||||
+ scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature)
|
||||
+ } else if scr.md.SignatureV3, ok = p.(*packet.SignatureV3); ok {
|
||||
+ scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignatureV3(scr.h, scr.md.SignatureV3)
|
||||
+ } else {
|
||||
scr.md.SignatureError = errors.StructuralError("LiteralData not followed by Signature")
|
||||
return
|
||||
}
|
||||
|
||||
- scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature)
|
||||
-
|
||||
// The SymmetricallyEncrypted packet, if any, might have an
|
||||
// unsigned hash of its own. In order to check this we need to
|
||||
// close that Reader.
|
||||
diff --git a/openpgp/read_test.go b/openpgp/read_test.go
|
||||
index 52f942c..abe8d7b 100644
|
||||
--- a/openpgp/read_test.go
|
||||
+++ b/openpgp/read_test.go
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
+ "golang.org/x/crypto/openpgp/armor"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
@@ -411,6 +412,50 @@ func TestIssue11504(t *testing.T) {
|
||||
testReadMessageError(t, "9303000130303030303030303030983002303030303030030000000130")
|
||||
}
|
||||
|
||||
+// TestSignatureV3Message tests the verification of V3 signature, generated
|
||||
+// with a modern V4-style key. Some people have their clients set to generate
|
||||
+// V3 signatures, so it's useful to be able to verify them.
|
||||
+func TestSignatureV3Message(t *testing.T) {
|
||||
+ sig, err := armor.Decode(strings.NewReader(signedMessageV3))
|
||||
+ if err != nil {
|
||||
+ t.Error(err)
|
||||
+ return
|
||||
+ }
|
||||
+ key, err := ReadArmoredKeyRing(strings.NewReader(keyV4forVerifyingSignedMessageV3))
|
||||
+ if err != nil {
|
||||
+ t.Error(err)
|
||||
+ return
|
||||
+ }
|
||||
+ md, err := ReadMessage(sig.Body, key, nil, nil)
|
||||
+ if err != nil {
|
||||
+ t.Error(err)
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
+ _, err = ioutil.ReadAll(md.UnverifiedBody)
|
||||
+ if err != nil {
|
||||
+ t.Error(err)
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
+ // We'll see a sig error here after reading in the UnverifiedBody above,
|
||||
+ // if there was one to see.
|
||||
+ if err = md.SignatureError; err != nil {
|
||||
+ t.Error(err)
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
+ if md.SignatureV3 == nil {
|
||||
+ t.Errorf("No available signature after checking signature")
|
||||
+ return
|
||||
+ }
|
||||
+ if md.Signature != nil {
|
||||
+ t.Errorf("Did not expect a signature V4 back")
|
||||
+ return
|
||||
+ }
|
||||
+ return
|
||||
+}
|
||||
+
|
||||
const testKey1KeyId = 0xA34D7E18C20C31BB
|
||||
const testKey3KeyId = 0x338934250CCC0360
|
||||
|
||||
@@ -504,3 +549,36 @@ const unknownHashFunctionHex = `8a00000040040001990006050253863c24000a09103b4fe6
|
||||
const missingHashFunctionHex = `8a00000040040001030006050253863c24000a09103b4fe6acc0b21f32ffff0101010101010101010101010101010101010101010101010101010101010101010101010101`
|
||||
|
||||
const campbellQuine = `a0b001000300fcffa0b001000d00f2ff000300fcffa0b001000d00f2ff8270a01c00000500faff8270a01c00000500faff000500faff001400ebff8270a01c00000500faff000500faff001400ebff428821c400001400ebff428821c400001400ebff428821c400001400ebff428821c400001400ebff428821c400000000ffff000000ffff000b00f4ff428821c400000000ffff000000ffff000b00f4ff0233214c40000100feff000233214c40000100feff0000`
|
||||
+
|
||||
+const keyV4forVerifyingSignedMessageV3 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
+Comment: GPGTools - https://gpgtools.org
|
||||
+
|
||||
+mI0EVfxoFQEEAMBIqmbDfYygcvP6Phr1wr1XI41IF7Qixqybs/foBF8qqblD9gIY
|
||||
+BKpXjnBOtbkcVOJ0nljd3/sQIfH4E0vQwK5/4YRQSI59eKOqd6Fx+fWQOLG+uu6z
|
||||
+tewpeCj9LLHvibx/Sc7VWRnrznia6ftrXxJ/wHMezSab3tnGC0YPVdGNABEBAAG0
|
||||
+JEdvY3J5cHRvIFRlc3QgS2V5IDx0aGVtYXhAZ21haWwuY29tPoi5BBMBCgAjBQJV
|
||||
+/GgVAhsDBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQeXnQmhdGW9PFVAP+
|
||||
+K7TU0qX5ArvIONIxh/WAweyOk884c5cE8f+3NOPOOCRGyVy0FId5A7MmD5GOQh4H
|
||||
+JseOZVEVCqlmngEvtHZb3U1VYtVGE5WZ+6rQhGsMcWP5qaT4soYwMBlSYxgYwQcx
|
||||
+YhN9qOr292f9j2Y//TTIJmZT4Oa+lMxhWdqTfX+qMgG4jQRV/GgVAQQArhFSiij1
|
||||
+b+hT3dnapbEU+23Z1yTu1DfF6zsxQ4XQWEV3eR8v+8mEDDNcz8oyyF56k6UQ3rXi
|
||||
+UMTIwRDg4V6SbZmaFbZYCOwp/EmXJ3rfhm7z7yzXj2OFN22luuqbyVhuL7LRdB0M
|
||||
+pxgmjXb4tTvfgKd26x34S+QqUJ7W6uprY4sAEQEAAYifBBgBCgAJBQJV/GgVAhsM
|
||||
+AAoJEHl50JoXRlvT7y8D/02ckx4OMkKBZo7viyrBw0MLG92i+DC2bs35PooHR6zz
|
||||
+786mitjOp5z2QWNLBvxC70S0qVfCIz8jKupO1J6rq6Z8CcbLF3qjm6h1omUBf8Nd
|
||||
+EfXKD2/2HV6zMKVknnKzIEzauh+eCKS2CeJUSSSryap/QLVAjRnckaES/OsEWhNB
|
||||
+=RZia
|
||||
+-----END PGP PUBLIC KEY BLOCK-----
|
||||
+`
|
||||
+
|
||||
+const signedMessageV3 = `-----BEGIN PGP MESSAGE-----
|
||||
+Comment: GPGTools - https://gpgtools.org
|
||||
+
|
||||
+owGbwMvMwMVYWXlhlrhb9GXG03JJDKF/MtxDMjKLFYAoUaEktbhEITe1uDgxPVWP
|
||||
+q5NhKjMrWAVcC9evD8z/bF/uWNjqtk/X3y5/38XGRQHm/57rrDRYuGnTw597Xqka
|
||||
+uM3137/hH3Os+Jf2dc0fXOITKwJvXJvecPVs0ta+Vg7ZO1MLn8w58Xx+6L58mbka
|
||||
+DGHyU9yTueZE8D+QF/Tz28Y78dqtF56R1VPn9Xw4uJqrWYdd7b3vIZ1V6R4Nh05d
|
||||
+iT57d/OhWwA=
|
||||
+=hG7R
|
||||
+-----END PGP MESSAGE-----
|
||||
+`
|
|
@ -11,10 +11,10 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/armor"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
"golang.org/x/crypto/openpgp/s2k"
|
||||
"github.com/keybase/go-crypto/openpgp/armor"
|
||||
"github.com/keybase/go-crypto/openpgp/errors"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
"github.com/keybase/go-crypto/openpgp/s2k"
|
||||
)
|
||||
|
||||
// DetachSign signs message with the private key from signer (which must
|
||||
|
@ -59,20 +59,57 @@ func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType p
|
|||
return out.Close()
|
||||
}
|
||||
|
||||
// SignWithSigner signs the message of type sigType with s and writes the
|
||||
// signature to w.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SignWithSigner(s packet.Signer, w io.Writer, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
|
||||
keyId := s.KeyId()
|
||||
sig := new(packet.Signature)
|
||||
sig.SigType = sigType
|
||||
sig.PubKeyAlgo = s.PublicKeyAlgo()
|
||||
sig.Hash = config.Hash()
|
||||
sig.CreationTime = config.Now()
|
||||
sig.IssuerKeyId = &keyId
|
||||
|
||||
s.Reset()
|
||||
|
||||
wrapped := s.(hash.Hash)
|
||||
|
||||
if sigType == packet.SigTypeText {
|
||||
wrapped = NewCanonicalTextHash(s)
|
||||
}
|
||||
|
||||
io.Copy(wrapped, message)
|
||||
|
||||
err = sig.Sign(s, nil, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = sig.Serialize(w)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
|
||||
if signer.PrivateKey == nil {
|
||||
signerSubkey, ok := signer.signingKey(config.Now())
|
||||
if !ok {
|
||||
err = errors.InvalidArgumentError("no valid signing keys")
|
||||
return
|
||||
}
|
||||
if signerSubkey.PrivateKey == nil {
|
||||
return errors.InvalidArgumentError("signing key doesn't have a private key")
|
||||
}
|
||||
if signer.PrivateKey.Encrypted {
|
||||
if signerSubkey.PrivateKey.Encrypted {
|
||||
return errors.InvalidArgumentError("signing key is encrypted")
|
||||
}
|
||||
|
||||
sig := new(packet.Signature)
|
||||
sig.SigType = sigType
|
||||
sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo
|
||||
sig.PubKeyAlgo = signerSubkey.PrivateKey.PubKeyAlgo
|
||||
sig.Hash = config.Hash()
|
||||
sig.CreationTime = config.Now()
|
||||
sig.IssuerKeyId = &signer.PrivateKey.KeyId
|
||||
sig.IssuerKeyId = &signerSubkey.PrivateKey.KeyId
|
||||
|
||||
h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
|
||||
if err != nil {
|
||||
|
@ -80,7 +117,7 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S
|
|||
}
|
||||
io.Copy(wrappedHash, message)
|
||||
|
||||
err = sig.Sign(h, signer.PrivateKey, config)
|
||||
err = sig.Sign(h, signerSubkey.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -198,11 +235,20 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
|
|||
hashToHashId(crypto.SHA1),
|
||||
hashToHashId(crypto.RIPEMD160),
|
||||
}
|
||||
// In the event that a recipient doesn't specify any supported ciphers
|
||||
// or hash functions, these are the ones that we assume that every
|
||||
// implementation supports.
|
||||
defaultCiphers := candidateCiphers[len(candidateCiphers)-1:]
|
||||
defaultHashes := candidateHashes[len(candidateHashes)-1:]
|
||||
|
||||
// If no preferences were specified, assume something safe and reasonable.
|
||||
defaultCiphers := []uint8{
|
||||
uint8(packet.CipherAES128),
|
||||
uint8(packet.CipherAES192),
|
||||
uint8(packet.CipherAES256),
|
||||
uint8(packet.CipherCAST5),
|
||||
}
|
||||
|
||||
defaultHashes := []uint8{
|
||||
hashToHashId(crypto.SHA256),
|
||||
hashToHashId(crypto.SHA512),
|
||||
hashToHashId(crypto.RIPEMD160),
|
||||
}
|
||||
|
||||
encryptKeys := make([]Key, len(to))
|
||||
for i := range to {
|
||||
|
@ -226,12 +272,15 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
|
|||
candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
|
||||
}
|
||||
|
||||
if len(candidateCiphers) == 0 || len(candidateHashes) == 0 {
|
||||
return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms")
|
||||
if len(candidateCiphers) == 0 {
|
||||
return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common ciphers")
|
||||
}
|
||||
if len(candidateHashes) == 0 {
|
||||
return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common hashes")
|
||||
}
|
||||
|
||||
cipher := packet.CipherFunction(candidateCiphers[0])
|
||||
// If the cipher specified by config is a candidate, we'll use that.
|
||||
// If the cipher specifed by config is a candidate, we'll use that.
|
||||
configuredCipher := config.Cipher()
|
||||
for _, c := range candidateCiphers {
|
||||
cipherFunc := packet.CipherFunction(c)
|
||||
|
@ -376,3 +425,71 @@ func (c noOpCloser) Write(data []byte) (n int, err error) {
|
|||
func (c noOpCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AttachedSign is like openpgp.Encrypt (as in p.crypto/openpgp/write.go), but
|
||||
// don't encrypt at all, just sign the literal unencrypted data.
|
||||
// Unfortunately we need to duplicate some code here that's already
|
||||
// in write.go
|
||||
func AttachedSign(out io.WriteCloser, signed Entity, hints *FileHints,
|
||||
config *packet.Config) (in io.WriteCloser, err error) {
|
||||
|
||||
if hints == nil {
|
||||
hints = &FileHints{}
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
config = &packet.Config{}
|
||||
}
|
||||
|
||||
var signer *packet.PrivateKey
|
||||
|
||||
signKey, ok := signed.signingKey(config.Now())
|
||||
if !ok {
|
||||
err = errors.InvalidArgumentError("no valid signing keys")
|
||||
return
|
||||
}
|
||||
signer = signKey.PrivateKey
|
||||
if signer == nil {
|
||||
err = errors.InvalidArgumentError("no valid signing keys")
|
||||
return
|
||||
}
|
||||
if signer.Encrypted {
|
||||
err = errors.InvalidArgumentError("signing key must be decrypted")
|
||||
return
|
||||
}
|
||||
|
||||
hasher := crypto.SHA512
|
||||
|
||||
ops := &packet.OnePassSignature{
|
||||
SigType: packet.SigTypeBinary,
|
||||
Hash: hasher,
|
||||
PubKeyAlgo: signer.PubKeyAlgo,
|
||||
KeyId: signer.KeyId,
|
||||
IsLast: true,
|
||||
}
|
||||
|
||||
if err = ops.Serialize(out); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var epochSeconds uint32
|
||||
if !hints.ModTime.IsZero() {
|
||||
epochSeconds = uint32(hints.ModTime.Unix())
|
||||
}
|
||||
|
||||
// We don't want the literal serializer to closer the output stream
|
||||
// since we're going to need to write to it when we finish up the
|
||||
// signature stuff.
|
||||
in, err = packet.SerializeLiteral(noOpCloser{out}, hints.IsBinary, hints.FileName, epochSeconds)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If we need to write a signature packet after the literal
|
||||
// data then we need to stop literalData from closing
|
||||
// encryptedData.
|
||||
in = signatureWriter{out, in, hasher, hasher.New(), signer, config}
|
||||
|
||||
return
|
||||
}
|
|
@ -0,0 +1,325 @@
|
|||
// Copyright 2009 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 rsa
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// This file implements encryption and decryption using PKCS#1 v1.5 padding.
|
||||
|
||||
// PKCS1v15DecrypterOpts is for passing options to PKCS#1 v1.5 decryption using
|
||||
// the crypto.Decrypter interface.
|
||||
type PKCS1v15DecryptOptions struct {
|
||||
// SessionKeyLen is the length of the session key that is being
|
||||
// decrypted. If not zero, then a padding error during decryption will
|
||||
// cause a random plaintext of this length to be returned rather than
|
||||
// an error. These alternatives happen in constant time.
|
||||
SessionKeyLen int
|
||||
}
|
||||
|
||||
// EncryptPKCS1v15 encrypts the given message with RSA and the padding scheme from PKCS#1 v1.5.
|
||||
// The message must be no longer than the length of the public modulus minus 11 bytes.
|
||||
//
|
||||
// The rand parameter is used as a source of entropy to ensure that encrypting
|
||||
// the same message twice doesn't result in the same ciphertext.
|
||||
//
|
||||
// WARNING: use of this function to encrypt plaintexts other than session keys
|
||||
// is dangerous. Use RSA OAEP in new protocols.
|
||||
func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err error) {
|
||||
if err := checkPub(pub); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k := (pub.N.BitLen() + 7) / 8
|
||||
if len(msg) > k-11 {
|
||||
err = ErrMessageTooLong
|
||||
return
|
||||
}
|
||||
|
||||
// EM = 0x00 || 0x02 || PS || 0x00 || M
|
||||
em := make([]byte, k)
|
||||
em[1] = 2
|
||||
ps, mm := em[2:len(em)-len(msg)-1], em[len(em)-len(msg):]
|
||||
err = nonZeroRandomBytes(ps, rand)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
em[len(em)-len(msg)-1] = 0
|
||||
copy(mm, msg)
|
||||
|
||||
m := new(big.Int).SetBytes(em)
|
||||
c := encrypt(new(big.Int), pub, m)
|
||||
|
||||
copyWithLeftPad(em, c.Bytes())
|
||||
out = em
|
||||
return
|
||||
}
|
||||
|
||||
// DecryptPKCS1v15 decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5.
|
||||
// If rand != nil, it uses RSA blinding to avoid timing side-channel attacks.
|
||||
//
|
||||
// Note that whether this function returns an error or not discloses secret
|
||||
// information. If an attacker can cause this function to run repeatedly and
|
||||
// learn whether each instance returned an error then they can decrypt and
|
||||
// forge signatures as if they had the private key. See
|
||||
// DecryptPKCS1v15SessionKey for a way of solving this problem.
|
||||
func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err error) {
|
||||
if err := checkPub(&priv.PublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valid, out, index, err := decryptPKCS1v15(rand, priv, ciphertext)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if valid == 0 {
|
||||
return nil, ErrDecryption
|
||||
}
|
||||
out = out[index:]
|
||||
return
|
||||
}
|
||||
|
||||
// DecryptPKCS1v15SessionKey decrypts a session key using RSA and the padding scheme from PKCS#1 v1.5.
|
||||
// If rand != nil, it uses RSA blinding to avoid timing side-channel attacks.
|
||||
// It returns an error if the ciphertext is the wrong length or if the
|
||||
// ciphertext is greater than the public modulus. Otherwise, no error is
|
||||
// returned. If the padding is valid, the resulting plaintext message is copied
|
||||
// into key. Otherwise, key is unchanged. These alternatives occur in constant
|
||||
// time. It is intended that the user of this function generate a random
|
||||
// session key beforehand and continue the protocol with the resulting value.
|
||||
// This will remove any possibility that an attacker can learn any information
|
||||
// about the plaintext.
|
||||
// See ``Chosen Ciphertext Attacks Against Protocols Based on the RSA
|
||||
// Encryption Standard PKCS #1'', Daniel Bleichenbacher, Advances in Cryptology
|
||||
// (Crypto '98).
|
||||
//
|
||||
// Note that if the session key is too small then it may be possible for an
|
||||
// attacker to brute-force it. If they can do that then they can learn whether
|
||||
// a random value was used (because it'll be different for the same ciphertext)
|
||||
// and thus whether the padding was correct. This defeats the point of this
|
||||
// function. Using at least a 16-byte key will protect against this attack.
|
||||
func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) (err error) {
|
||||
if err := checkPub(&priv.PublicKey); err != nil {
|
||||
return err
|
||||
}
|
||||
k := (priv.N.BitLen() + 7) / 8
|
||||
if k-(len(key)+3+8) < 0 {
|
||||
return ErrDecryption
|
||||
}
|
||||
|
||||
valid, em, index, err := decryptPKCS1v15(rand, priv, ciphertext)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(em) != k {
|
||||
// This should be impossible because decryptPKCS1v15 always
|
||||
// returns the full slice.
|
||||
return ErrDecryption
|
||||
}
|
||||
|
||||
valid &= subtle.ConstantTimeEq(int32(len(em)-index), int32(len(key)))
|
||||
subtle.ConstantTimeCopy(valid, key, em[len(em)-len(key):])
|
||||
return
|
||||
}
|
||||
|
||||
// decryptPKCS1v15 decrypts ciphertext using priv and blinds the operation if
|
||||
// rand is not nil. It returns one or zero in valid that indicates whether the
|
||||
// plaintext was correctly structured. In either case, the plaintext is
|
||||
// returned in em so that it may be read independently of whether it was valid
|
||||
// in order to maintain constant memory access patterns. If the plaintext was
|
||||
// valid then index contains the index of the original message in em.
|
||||
func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) {
|
||||
k := (priv.N.BitLen() + 7) / 8
|
||||
if k < 11 {
|
||||
err = ErrDecryption
|
||||
return
|
||||
}
|
||||
|
||||
c := new(big.Int).SetBytes(ciphertext)
|
||||
m, err := decrypt(rand, priv, c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
em = leftPad(m.Bytes(), k)
|
||||
firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0)
|
||||
secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2)
|
||||
|
||||
// The remainder of the plaintext must be a string of non-zero random
|
||||
// octets, followed by a 0, followed by the message.
|
||||
// lookingForIndex: 1 iff we are still looking for the zero.
|
||||
// index: the offset of the first zero byte.
|
||||
lookingForIndex := 1
|
||||
|
||||
for i := 2; i < len(em); i++ {
|
||||
equals0 := subtle.ConstantTimeByteEq(em[i], 0)
|
||||
index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index)
|
||||
lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
|
||||
}
|
||||
|
||||
// The PS padding must be at least 8 bytes long, and it starts two
|
||||
// bytes into em.
|
||||
validPS := subtle.ConstantTimeLessOrEq(2+8, index)
|
||||
|
||||
valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1) & validPS
|
||||
index = subtle.ConstantTimeSelect(valid, index+1, 0)
|
||||
return valid, em, index, nil
|
||||
}
|
||||
|
||||
// nonZeroRandomBytes fills the given slice with non-zero random octets.
|
||||
func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
|
||||
_, err = io.ReadFull(rand, s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
for s[i] == 0 {
|
||||
_, err = io.ReadFull(rand, s[i:i+1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// In tests, the PRNG may return all zeros so we do
|
||||
// this to break the loop.
|
||||
s[i] ^= 0x42
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// These are ASN1 DER structures:
|
||||
// DigestInfo ::= SEQUENCE {
|
||||
// digestAlgorithm AlgorithmIdentifier,
|
||||
// digest OCTET STRING
|
||||
// }
|
||||
// For performance, we don't use the generic ASN1 encoder. Rather, we
|
||||
// precompute a prefix of the digest value that makes a valid ASN1 DER string
|
||||
// with the correct contents.
|
||||
var hashPrefixes = map[crypto.Hash][]byte{
|
||||
crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
|
||||
crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
|
||||
crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
|
||||
crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
|
||||
crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
|
||||
crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
|
||||
crypto.MD5SHA1: {}, // A special TLS case which doesn't use an ASN1 prefix.
|
||||
crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14},
|
||||
}
|
||||
|
||||
// SignPKCS1v15 calculates the signature of hashed using RSASSA-PKCS1-V1_5-SIGN from RSA PKCS#1 v1.5.
|
||||
// Note that hashed must be the result of hashing the input message using the
|
||||
// given hash function. If hash is zero, hashed is signed directly. This isn't
|
||||
// advisable except for interoperability.
|
||||
//
|
||||
// If rand is not nil then RSA blinding will be used to avoid timing side-channel attacks.
|
||||
//
|
||||
// This function is deterministic. Thus, if the set of possible messages is
|
||||
// small, an attacker may be able to build a map from messages to signatures
|
||||
// and identify the signed messages. As ever, signatures provide authenticity,
|
||||
// not confidentiality.
|
||||
func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) (s []byte, err error) {
|
||||
hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tLen := len(prefix) + hashLen
|
||||
k := (priv.N.BitLen() + 7) / 8
|
||||
if k < tLen+11 {
|
||||
return nil, ErrMessageTooLong
|
||||
}
|
||||
|
||||
// EM = 0x00 || 0x01 || PS || 0x00 || T
|
||||
em := make([]byte, k)
|
||||
em[1] = 1
|
||||
for i := 2; i < k-tLen-1; i++ {
|
||||
em[i] = 0xff
|
||||
}
|
||||
copy(em[k-tLen:k-hashLen], prefix)
|
||||
copy(em[k-hashLen:k], hashed)
|
||||
|
||||
m := new(big.Int).SetBytes(em)
|
||||
c, err := decryptAndCheck(rand, priv, m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
copyWithLeftPad(em, c.Bytes())
|
||||
s = em
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyPKCS1v15 verifies an RSA PKCS#1 v1.5 signature.
|
||||
// hashed is the result of hashing the input message using the given hash
|
||||
// function and sig is the signature. A valid signature is indicated by
|
||||
// returning a nil error. If hash is zero then hashed is used directly. This
|
||||
// isn't advisable except for interoperability.
|
||||
func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) (err error) {
|
||||
hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tLen := len(prefix) + hashLen
|
||||
k := (pub.N.BitLen() + 7) / 8
|
||||
if k < tLen+11 {
|
||||
err = ErrVerification
|
||||
return
|
||||
}
|
||||
|
||||
c := new(big.Int).SetBytes(sig)
|
||||
m := encrypt(new(big.Int), pub, c)
|
||||
em := leftPad(m.Bytes(), k)
|
||||
// EM = 0x00 || 0x01 || PS || 0x00 || T
|
||||
|
||||
ok := subtle.ConstantTimeByteEq(em[0], 0)
|
||||
ok &= subtle.ConstantTimeByteEq(em[1], 1)
|
||||
ok &= subtle.ConstantTimeCompare(em[k-hashLen:k], hashed)
|
||||
ok &= subtle.ConstantTimeCompare(em[k-tLen:k-hashLen], prefix)
|
||||
ok &= subtle.ConstantTimeByteEq(em[k-tLen-1], 0)
|
||||
|
||||
for i := 2; i < k-tLen-1; i++ {
|
||||
ok &= subtle.ConstantTimeByteEq(em[i], 0xff)
|
||||
}
|
||||
|
||||
if ok != 1 {
|
||||
return ErrVerification
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) {
|
||||
// Special case: crypto.Hash(0) is used to indicate that the data is
|
||||
// signed directly.
|
||||
if hash == 0 {
|
||||
return inLen, nil, nil
|
||||
}
|
||||
|
||||
hashLen = hash.Size()
|
||||
if inLen != hashLen {
|
||||
return 0, nil, errors.New("crypto/rsa: input must be hashed message")
|
||||
}
|
||||
prefix, ok := hashPrefixes[hash]
|
||||
if !ok {
|
||||
return 0, nil, errors.New("crypto/rsa: unsupported hash function")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// copyWithLeftPad copies src to the end of dest, padding with zero bytes as
|
||||
// needed.
|
||||
func copyWithLeftPad(dest, src []byte) {
|
||||
numPaddingBytes := len(dest) - len(src)
|
||||
for i := 0; i < numPaddingBytes; i++ {
|
||||
dest[i] = 0
|
||||
}
|
||||
copy(dest[numPaddingBytes:], src)
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
// Copyright 2013 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 rsa
|
||||
|
||||
// This file implements the PSS signature scheme [1].
|
||||
//
|
||||
// [1] http://www.rsa.com/rsalabs/pkcs/files/h11300-wp-pkcs-1v2-2-rsa-cryptography-standard.pdf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) {
|
||||
// See [1], section 9.1.1
|
||||
hLen := hash.Size()
|
||||
sLen := len(salt)
|
||||
emLen := (emBits + 7) / 8
|
||||
|
||||
// 1. If the length of M is greater than the input limitation for the
|
||||
// hash function (2^61 - 1 octets for SHA-1), output "message too
|
||||
// long" and stop.
|
||||
//
|
||||
// 2. Let mHash = Hash(M), an octet string of length hLen.
|
||||
|
||||
if len(mHash) != hLen {
|
||||
return nil, errors.New("crypto/rsa: input must be hashed message")
|
||||
}
|
||||
|
||||
// 3. If emLen < hLen + sLen + 2, output "encoding error" and stop.
|
||||
|
||||
if emLen < hLen+sLen+2 {
|
||||
return nil, errors.New("crypto/rsa: encoding error")
|
||||
}
|
||||
|
||||
em := make([]byte, emLen)
|
||||
db := em[:emLen-sLen-hLen-2+1+sLen]
|
||||
h := em[emLen-sLen-hLen-2+1+sLen : emLen-1]
|
||||
|
||||
// 4. Generate a random octet string salt of length sLen; if sLen = 0,
|
||||
// then salt is the empty string.
|
||||
//
|
||||
// 5. Let
|
||||
// M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt;
|
||||
//
|
||||
// M' is an octet string of length 8 + hLen + sLen with eight
|
||||
// initial zero octets.
|
||||
//
|
||||
// 6. Let H = Hash(M'), an octet string of length hLen.
|
||||
|
||||
var prefix [8]byte
|
||||
|
||||
hash.Write(prefix[:])
|
||||
hash.Write(mHash)
|
||||
hash.Write(salt)
|
||||
|
||||
h = hash.Sum(h[:0])
|
||||
hash.Reset()
|
||||
|
||||
// 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
|
||||
// zero octets. The length of PS may be 0.
|
||||
//
|
||||
// 8. Let DB = PS || 0x01 || salt; DB is an octet string of length
|
||||
// emLen - hLen - 1.
|
||||
|
||||
db[emLen-sLen-hLen-2] = 0x01
|
||||
copy(db[emLen-sLen-hLen-1:], salt)
|
||||
|
||||
// 9. Let dbMask = MGF(H, emLen - hLen - 1).
|
||||
//
|
||||
// 10. Let maskedDB = DB \xor dbMask.
|
||||
|
||||
mgf1XOR(db, hash, h)
|
||||
|
||||
// 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in
|
||||
// maskedDB to zero.
|
||||
|
||||
db[0] &= (0xFF >> uint(8*emLen-emBits))
|
||||
|
||||
// 12. Let EM = maskedDB || H || 0xbc.
|
||||
em[emLen-1] = 0xBC
|
||||
|
||||
// 13. Output EM.
|
||||
return em, nil
|
||||
}
|
||||
|
||||
func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error {
|
||||
// 1. If the length of M is greater than the input limitation for the
|
||||
// hash function (2^61 - 1 octets for SHA-1), output "inconsistent"
|
||||
// and stop.
|
||||
//
|
||||
// 2. Let mHash = Hash(M), an octet string of length hLen.
|
||||
hLen := hash.Size()
|
||||
if hLen != len(mHash) {
|
||||
return ErrVerification
|
||||
}
|
||||
|
||||
// 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
|
||||
emLen := (emBits + 7) / 8
|
||||
if emLen < hLen+sLen+2 {
|
||||
return ErrVerification
|
||||
}
|
||||
|
||||
// 4. If the rightmost octet of EM does not have hexadecimal value
|
||||
// 0xbc, output "inconsistent" and stop.
|
||||
if em[len(em)-1] != 0xBC {
|
||||
return ErrVerification
|
||||
}
|
||||
|
||||
// 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
|
||||
// let H be the next hLen octets.
|
||||
db := em[:emLen-hLen-1]
|
||||
h := em[emLen-hLen-1 : len(em)-1]
|
||||
|
||||
// 6. If the leftmost 8 * emLen - emBits bits of the leftmost octet in
|
||||
// maskedDB are not all equal to zero, output "inconsistent" and
|
||||
// stop.
|
||||
if em[0]&(0xFF<<uint(8-(8*emLen-emBits))) != 0 {
|
||||
return ErrVerification
|
||||
}
|
||||
|
||||
// 7. Let dbMask = MGF(H, emLen - hLen - 1).
|
||||
//
|
||||
// 8. Let DB = maskedDB \xor dbMask.
|
||||
mgf1XOR(db, hash, h)
|
||||
|
||||
// 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB
|
||||
// to zero.
|
||||
db[0] &= (0xFF >> uint(8*emLen-emBits))
|
||||
|
||||
if sLen == PSSSaltLengthAuto {
|
||||
FindSaltLength:
|
||||
for sLen = emLen - (hLen + 2); sLen >= 0; sLen-- {
|
||||
switch db[emLen-hLen-sLen-2] {
|
||||
case 1:
|
||||
break FindSaltLength
|
||||
case 0:
|
||||
continue
|
||||
default:
|
||||
return ErrVerification
|
||||
}
|
||||
}
|
||||
if sLen < 0 {
|
||||
return ErrVerification
|
||||
}
|
||||
} else {
|
||||
// 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
|
||||
// or if the octet at position emLen - hLen - sLen - 1 (the leftmost
|
||||
// position is "position 1") does not have hexadecimal value 0x01,
|
||||
// output "inconsistent" and stop.
|
||||
for _, e := range db[:emLen-hLen-sLen-2] {
|
||||
if e != 0x00 {
|
||||
return ErrVerification
|
||||
}
|
||||
}
|
||||
if db[emLen-hLen-sLen-2] != 0x01 {
|
||||
return ErrVerification
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Let salt be the last sLen octets of DB.
|
||||
salt := db[len(db)-sLen:]
|
||||
|
||||
// 12. Let
|
||||
// M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
|
||||
// M' is an octet string of length 8 + hLen + sLen with eight
|
||||
// initial zero octets.
|
||||
//
|
||||
// 13. Let H' = Hash(M'), an octet string of length hLen.
|
||||
var prefix [8]byte
|
||||
hash.Write(prefix[:])
|
||||
hash.Write(mHash)
|
||||
hash.Write(salt)
|
||||
|
||||
h0 := hash.Sum(nil)
|
||||
|
||||
// 14. If H = H', output "consistent." Otherwise, output "inconsistent."
|
||||
if !bytes.Equal(h0, h) {
|
||||
return ErrVerification
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// signPSSWithSalt calculates the signature of hashed using PSS [1] with specified salt.
|
||||
// Note that hashed must be the result of hashing the input message using the
|
||||
// given hash function. salt is a random sequence of bytes whose length will be
|
||||
// later used to verify the signature.
|
||||
func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) (s []byte, err error) {
|
||||
nBits := priv.N.BitLen()
|
||||
em, err := emsaPSSEncode(hashed, nBits-1, salt, hash.New())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m := new(big.Int).SetBytes(em)
|
||||
c, err := decryptAndCheck(rand, priv, m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = make([]byte, (nBits+7)/8)
|
||||
copyWithLeftPad(s, c.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
// PSSSaltLengthAuto causes the salt in a PSS signature to be as large
|
||||
// as possible when signing, and to be auto-detected when verifying.
|
||||
PSSSaltLengthAuto = 0
|
||||
// PSSSaltLengthEqualsHash causes the salt length to equal the length
|
||||
// of the hash used in the signature.
|
||||
PSSSaltLengthEqualsHash = -1
|
||||
)
|
||||
|
||||
// PSSOptions contains options for creating and verifying PSS signatures.
|
||||
type PSSOptions struct {
|
||||
// SaltLength controls the length of the salt used in the PSS
|
||||
// signature. It can either be a number of bytes, or one of the special
|
||||
// PSSSaltLength constants.
|
||||
SaltLength int
|
||||
|
||||
// Hash, if not zero, overrides the hash function passed to SignPSS.
|
||||
// This is the only way to specify the hash function when using the
|
||||
// crypto.Signer interface.
|
||||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
// HashFunc returns pssOpts.Hash so that PSSOptions implements
|
||||
// crypto.SignerOpts.
|
||||
func (pssOpts *PSSOptions) HashFunc() crypto.Hash {
|
||||
return pssOpts.Hash
|
||||
}
|
||||
|
||||
func (opts *PSSOptions) saltLength() int {
|
||||
if opts == nil {
|
||||
return PSSSaltLengthAuto
|
||||
}
|
||||
return opts.SaltLength
|
||||
}
|
||||
|
||||
// SignPSS calculates the signature of hashed using RSASSA-PSS [1].
|
||||
// Note that hashed must be the result of hashing the input message using the
|
||||
// given hash function. The opts argument may be nil, in which case sensible
|
||||
// defaults are used.
|
||||
func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, opts *PSSOptions) (s []byte, err error) {
|
||||
saltLength := opts.saltLength()
|
||||
switch saltLength {
|
||||
case PSSSaltLengthAuto:
|
||||
saltLength = (priv.N.BitLen()+7)/8 - 2 - hash.Size()
|
||||
case PSSSaltLengthEqualsHash:
|
||||
saltLength = hash.Size()
|
||||
}
|
||||
|
||||
if opts != nil && opts.Hash != 0 {
|
||||
hash = opts.Hash
|
||||
}
|
||||
|
||||
salt := make([]byte, saltLength)
|
||||
if _, err = io.ReadFull(rand, salt); err != nil {
|
||||
return
|
||||
}
|
||||
return signPSSWithSalt(rand, priv, hash, hashed, salt)
|
||||
}
|
||||
|
||||
// VerifyPSS verifies a PSS signature.
|
||||
// hashed is the result of hashing the input message using the given hash
|
||||
// function and sig is the signature. A valid signature is indicated by
|
||||
// returning a nil error. The opts argument may be nil, in which case sensible
|
||||
// defaults are used.
|
||||
func VerifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, opts *PSSOptions) error {
|
||||
return verifyPSS(pub, hash, hashed, sig, opts.saltLength())
|
||||
}
|
||||
|
||||
// verifyPSS verifies a PSS signature with the given salt length.
|
||||
func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error {
|
||||
nBits := pub.N.BitLen()
|
||||
if len(sig) != (nBits+7)/8 {
|
||||
return ErrVerification
|
||||
}
|
||||
s := new(big.Int).SetBytes(sig)
|
||||
m := encrypt(new(big.Int), pub, s)
|
||||
emBits := nBits - 1
|
||||
emLen := (emBits + 7) / 8
|
||||
if emLen < len(m.Bytes()) {
|
||||
return ErrVerification
|
||||
}
|
||||
em := make([]byte, emLen)
|
||||
copyWithLeftPad(em, m.Bytes())
|
||||
if saltLen == PSSSaltLengthEqualsHash {
|
||||
saltLen = hash.Size()
|
||||
}
|
||||
return emsaPSSVerify(hashed, em, emBits, saltLen, hash.New())
|
||||
}
|
|
@ -0,0 +1,646 @@
|
|||
// Copyright 2009 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 rsa implements RSA encryption as specified in PKCS#1.
|
||||
//
|
||||
// RSA is a single, fundamental operation that is used in this package to
|
||||
// implement either public-key encryption or public-key signatures.
|
||||
//
|
||||
// The original specification for encryption and signatures with RSA is PKCS#1
|
||||
// and the terms "RSA encryption" and "RSA signatures" by default refer to
|
||||
// PKCS#1 version 1.5. However, that specification has flaws and new designs
|
||||
// should use version two, usually called by just OAEP and PSS, where
|
||||
// possible.
|
||||
//
|
||||
// Two sets of interfaces are included in this package. When a more abstract
|
||||
// interface isn't neccessary, there are functions for encrypting/decrypting
|
||||
// with v1.5/OAEP and signing/verifying with v1.5/PSS. If one needs to abstract
|
||||
// over the public-key primitive, the PrivateKey struct implements the
|
||||
// Decrypter and Signer interfaces from the crypto package.
|
||||
package rsa
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var bigZero = big.NewInt(0)
|
||||
var bigOne = big.NewInt(1)
|
||||
|
||||
// A PublicKey represents the public part of an RSA key.
|
||||
type PublicKey struct {
|
||||
N *big.Int // modulus
|
||||
E int64 // public exponent
|
||||
}
|
||||
|
||||
// OAEPOptions is an interface for passing options to OAEP decryption using the
|
||||
// crypto.Decrypter interface.
|
||||
type OAEPOptions struct {
|
||||
// Hash is the hash function that will be used when generating the mask.
|
||||
Hash crypto.Hash
|
||||
// Label is an arbitrary byte string that must be equal to the value
|
||||
// used when encrypting.
|
||||
Label []byte
|
||||
}
|
||||
|
||||
var (
|
||||
errPublicModulus = errors.New("crypto/rsa: missing public modulus")
|
||||
errPublicExponentSmall = errors.New("crypto/rsa: public exponent too small")
|
||||
errPublicExponentLarge = errors.New("crypto/rsa: public exponent too large")
|
||||
)
|
||||
|
||||
// checkPub sanity checks the public key before we use it.
|
||||
// We require pub.E to fit into a 32-bit integer so that we
|
||||
// do not have different behavior depending on whether
|
||||
// int is 32 or 64 bits. See also
|
||||
// http://www.imperialviolet.org/2012/03/16/rsae.html.
|
||||
func checkPub(pub *PublicKey) error {
|
||||
if pub.N == nil {
|
||||
return errPublicModulus
|
||||
}
|
||||
if pub.E < 2 {
|
||||
return errPublicExponentSmall
|
||||
}
|
||||
if pub.E > 1<<63-1 {
|
||||
return errPublicExponentLarge
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A PrivateKey represents an RSA key
|
||||
type PrivateKey struct {
|
||||
PublicKey // public part.
|
||||
D *big.Int // private exponent
|
||||
Primes []*big.Int // prime factors of N, has >= 2 elements.
|
||||
|
||||
// Precomputed contains precomputed values that speed up private
|
||||
// operations, if available.
|
||||
Precomputed PrecomputedValues
|
||||
}
|
||||
|
||||
// Public returns the public key corresponding to priv.
|
||||
func (priv *PrivateKey) Public() crypto.PublicKey {
|
||||
return &priv.PublicKey
|
||||
}
|
||||
|
||||
// Sign signs msg with priv, reading randomness from rand. If opts is a
|
||||
// *PSSOptions then the PSS algorithm will be used, otherwise PKCS#1 v1.5 will
|
||||
// be used. This method is intended to support keys where the private part is
|
||||
// kept in, for example, a hardware module. Common uses should use the Sign*
|
||||
// functions in this package.
|
||||
func (priv *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
if pssOpts, ok := opts.(*PSSOptions); ok {
|
||||
return SignPSS(rand, priv, pssOpts.Hash, msg, pssOpts)
|
||||
}
|
||||
|
||||
return SignPKCS1v15(rand, priv, opts.HashFunc(), msg)
|
||||
}
|
||||
|
||||
// Decrypt decrypts ciphertext with priv. If opts is nil or of type
|
||||
// *PKCS1v15DecryptOptions then PKCS#1 v1.5 decryption is performed. Otherwise
|
||||
// opts must have type *OAEPOptions and OAEP decryption is done.
|
||||
func (priv *PrivateKey) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error) {
|
||||
if opts == nil {
|
||||
return DecryptPKCS1v15(rand, priv, ciphertext)
|
||||
}
|
||||
|
||||
switch opts := opts.(type) {
|
||||
case *OAEPOptions:
|
||||
return DecryptOAEP(opts.Hash.New(), rand, priv, ciphertext, opts.Label)
|
||||
|
||||
case *PKCS1v15DecryptOptions:
|
||||
if l := opts.SessionKeyLen; l > 0 {
|
||||
plaintext = make([]byte, l)
|
||||
if _, err := io.ReadFull(rand, plaintext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := DecryptPKCS1v15SessionKey(rand, priv, ciphertext, plaintext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintext, nil
|
||||
} else {
|
||||
return DecryptPKCS1v15(rand, priv, ciphertext)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New("crypto/rsa: invalid options for Decrypt")
|
||||
}
|
||||
}
|
||||
|
||||
type PrecomputedValues struct {
|
||||
Dp, Dq *big.Int // D mod (P-1) (or mod Q-1)
|
||||
Qinv *big.Int // Q^-1 mod P
|
||||
|
||||
// CRTValues is used for the 3rd and subsequent primes. Due to a
|
||||
// historical accident, the CRT for the first two primes is handled
|
||||
// differently in PKCS#1 and interoperability is sufficiently
|
||||
// important that we mirror this.
|
||||
CRTValues []CRTValue
|
||||
}
|
||||
|
||||
// CRTValue contains the precomputed Chinese remainder theorem values.
|
||||
type CRTValue struct {
|
||||
Exp *big.Int // D mod (prime-1).
|
||||
Coeff *big.Int // R·Coeff ≡ 1 mod Prime.
|
||||
R *big.Int // product of primes prior to this (inc p and q).
|
||||
}
|
||||
|
||||
// Validate performs basic sanity checks on the key.
|
||||
// It returns nil if the key is valid, or else an error describing a problem.
|
||||
func (priv *PrivateKey) Validate() error {
|
||||
if err := checkPub(&priv.PublicKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check that Πprimes == n.
|
||||
modulus := new(big.Int).Set(bigOne)
|
||||
for _, prime := range priv.Primes {
|
||||
// Any primes ≤ 1 will cause divide-by-zero panics later.
|
||||
if prime.Cmp(bigOne) <= 0 {
|
||||
return errors.New("crypto/rsa: invalid prime value")
|
||||
}
|
||||
modulus.Mul(modulus, prime)
|
||||
}
|
||||
if modulus.Cmp(priv.N) != 0 {
|
||||
return errors.New("crypto/rsa: invalid modulus")
|
||||
}
|
||||
|
||||
// Check that de ≡ 1 mod p-1, for each prime.
|
||||
// This implies that e is coprime to each p-1 as e has a multiplicative
|
||||
// inverse. Therefore e is coprime to lcm(p-1,q-1,r-1,...) =
|
||||
// exponent(ℤ/nℤ). It also implies that a^de ≡ a mod p as a^(p-1) ≡ 1
|
||||
// mod p. Thus a^de ≡ a mod n for all a coprime to n, as required.
|
||||
congruence := new(big.Int)
|
||||
de := new(big.Int).SetInt64(int64(priv.E))
|
||||
de.Mul(de, priv.D)
|
||||
for _, prime := range priv.Primes {
|
||||
pminus1 := new(big.Int).Sub(prime, bigOne)
|
||||
congruence.Mod(de, pminus1)
|
||||
if congruence.Cmp(bigOne) != 0 {
|
||||
return errors.New("crypto/rsa: invalid exponents")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateKey generates an RSA keypair of the given bit size using the
|
||||
// random source random (for example, crypto/rand.Reader).
|
||||
func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err error) {
|
||||
return GenerateMultiPrimeKey(random, 2, bits)
|
||||
}
|
||||
|
||||
// GenerateMultiPrimeKey generates a multi-prime RSA keypair of the given bit
|
||||
// size and the given random source, as suggested in [1]. Although the public
|
||||
// keys are compatible (actually, indistinguishable) from the 2-prime case,
|
||||
// the private keys are not. Thus it may not be possible to export multi-prime
|
||||
// private keys in certain formats or to subsequently import them into other
|
||||
// code.
|
||||
//
|
||||
// Table 1 in [2] suggests maximum numbers of primes for a given size.
|
||||
//
|
||||
// [1] US patent 4405829 (1972, expired)
|
||||
// [2] http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf
|
||||
func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *PrivateKey, err error) {
|
||||
priv = new(PrivateKey)
|
||||
priv.E = 65537
|
||||
|
||||
if nprimes < 2 {
|
||||
return nil, errors.New("crypto/rsa: GenerateMultiPrimeKey: nprimes must be >= 2")
|
||||
}
|
||||
|
||||
primes := make([]*big.Int, nprimes)
|
||||
|
||||
NextSetOfPrimes:
|
||||
for {
|
||||
todo := bits
|
||||
// crypto/rand should set the top two bits in each prime.
|
||||
// Thus each prime has the form
|
||||
// p_i = 2^bitlen(p_i) × 0.11... (in base 2).
|
||||
// And the product is:
|
||||
// P = 2^todo × α
|
||||
// where α is the product of nprimes numbers of the form 0.11...
|
||||
//
|
||||
// If α < 1/2 (which can happen for nprimes > 2), we need to
|
||||
// shift todo to compensate for lost bits: the mean value of 0.11...
|
||||
// is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2
|
||||
// will give good results.
|
||||
if nprimes >= 7 {
|
||||
todo += (nprimes - 2) / 5
|
||||
}
|
||||
for i := 0; i < nprimes; i++ {
|
||||
primes[i], err = rand.Prime(random, todo/(nprimes-i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
todo -= primes[i].BitLen()
|
||||
}
|
||||
|
||||
// Make sure that primes is pairwise unequal.
|
||||
for i, prime := range primes {
|
||||
for j := 0; j < i; j++ {
|
||||
if prime.Cmp(primes[j]) == 0 {
|
||||
continue NextSetOfPrimes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n := new(big.Int).Set(bigOne)
|
||||
totient := new(big.Int).Set(bigOne)
|
||||
pminus1 := new(big.Int)
|
||||
for _, prime := range primes {
|
||||
n.Mul(n, prime)
|
||||
pminus1.Sub(prime, bigOne)
|
||||
totient.Mul(totient, pminus1)
|
||||
}
|
||||
if n.BitLen() != bits {
|
||||
// This should never happen for nprimes == 2 because
|
||||
// crypto/rand should set the top two bits in each prime.
|
||||
// For nprimes > 2 we hope it does not happen often.
|
||||
continue NextSetOfPrimes
|
||||
}
|
||||
|
||||
g := new(big.Int)
|
||||
priv.D = new(big.Int)
|
||||
y := new(big.Int)
|
||||
e := big.NewInt(int64(priv.E))
|
||||
g.GCD(priv.D, y, e, totient)
|
||||
|
||||
if g.Cmp(bigOne) == 0 {
|
||||
if priv.D.Sign() < 0 {
|
||||
priv.D.Add(priv.D, totient)
|
||||
}
|
||||
priv.Primes = primes
|
||||
priv.N = n
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
priv.Precompute()
|
||||
return
|
||||
}
|
||||
|
||||
// incCounter increments a four byte, big-endian counter.
|
||||
func incCounter(c *[4]byte) {
|
||||
if c[3]++; c[3] != 0 {
|
||||
return
|
||||
}
|
||||
if c[2]++; c[2] != 0 {
|
||||
return
|
||||
}
|
||||
if c[1]++; c[1] != 0 {
|
||||
return
|
||||
}
|
||||
c[0]++
|
||||
}
|
||||
|
||||
// mgf1XOR XORs the bytes in out with a mask generated using the MGF1 function
|
||||
// specified in PKCS#1 v2.1.
|
||||
func mgf1XOR(out []byte, hash hash.Hash, seed []byte) {
|
||||
var counter [4]byte
|
||||
var digest []byte
|
||||
|
||||
done := 0
|
||||
for done < len(out) {
|
||||
hash.Write(seed)
|
||||
hash.Write(counter[0:4])
|
||||
digest = hash.Sum(digest[:0])
|
||||
hash.Reset()
|
||||
|
||||
for i := 0; i < len(digest) && done < len(out); i++ {
|
||||
out[done] ^= digest[i]
|
||||
done++
|
||||
}
|
||||
incCounter(&counter)
|
||||
}
|
||||
}
|
||||
|
||||
// ErrMessageTooLong is returned when attempting to encrypt a message which is
|
||||
// too large for the size of the public key.
|
||||
var ErrMessageTooLong = errors.New("crypto/rsa: message too long for RSA public key size")
|
||||
|
||||
func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int {
|
||||
e := big.NewInt(int64(pub.E))
|
||||
c.Exp(m, e, pub.N)
|
||||
return c
|
||||
}
|
||||
|
||||
// EncryptOAEP encrypts the given message with RSA-OAEP.
|
||||
//
|
||||
// OAEP is parameterised by a hash function that is used as a random oracle.
|
||||
// Encryption and decryption of a given message must use the same hash function
|
||||
// and sha256.New() is a reasonable choice.
|
||||
//
|
||||
// The random parameter is used as a source of entropy to ensure that
|
||||
// encrypting the same message twice doesn't result in the same ciphertext.
|
||||
//
|
||||
// The label parameter may contain arbitrary data that will not be encrypted,
|
||||
// but which gives important context to the message. For example, if a given
|
||||
// public key is used to decrypt two types of messages then distinct label
|
||||
// values could be used to ensure that a ciphertext for one purpose cannot be
|
||||
// used for another by an attacker. If not required it can be empty.
|
||||
//
|
||||
// The message must be no longer than the length of the public modulus less
|
||||
// twice the hash length plus 2.
|
||||
func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) (out []byte, err error) {
|
||||
if err := checkPub(pub); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash.Reset()
|
||||
k := (pub.N.BitLen() + 7) / 8
|
||||
if len(msg) > k-2*hash.Size()-2 {
|
||||
err = ErrMessageTooLong
|
||||
return
|
||||
}
|
||||
|
||||
hash.Write(label)
|
||||
lHash := hash.Sum(nil)
|
||||
hash.Reset()
|
||||
|
||||
em := make([]byte, k)
|
||||
seed := em[1 : 1+hash.Size()]
|
||||
db := em[1+hash.Size():]
|
||||
|
||||
copy(db[0:hash.Size()], lHash)
|
||||
db[len(db)-len(msg)-1] = 1
|
||||
copy(db[len(db)-len(msg):], msg)
|
||||
|
||||
_, err = io.ReadFull(random, seed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mgf1XOR(db, hash, seed)
|
||||
mgf1XOR(seed, hash, db)
|
||||
|
||||
m := new(big.Int)
|
||||
m.SetBytes(em)
|
||||
c := encrypt(new(big.Int), pub, m)
|
||||
out = c.Bytes()
|
||||
|
||||
if len(out) < k {
|
||||
// If the output is too small, we need to left-pad with zeros.
|
||||
t := make([]byte, k)
|
||||
copy(t[k-len(out):], out)
|
||||
out = t
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ErrDecryption represents a failure to decrypt a message.
|
||||
// It is deliberately vague to avoid adaptive attacks.
|
||||
var ErrDecryption = errors.New("crypto/rsa: decryption error")
|
||||
|
||||
// ErrVerification represents a failure to verify a signature.
|
||||
// It is deliberately vague to avoid adaptive attacks.
|
||||
var ErrVerification = errors.New("crypto/rsa: verification error")
|
||||
|
||||
// modInverse returns ia, the inverse of a in the multiplicative group of prime
|
||||
// order n. It requires that a be a member of the group (i.e. less than n).
|
||||
func modInverse(a, n *big.Int) (ia *big.Int, ok bool) {
|
||||
g := new(big.Int)
|
||||
x := new(big.Int)
|
||||
y := new(big.Int)
|
||||
g.GCD(x, y, a, n)
|
||||
if g.Cmp(bigOne) != 0 {
|
||||
// In this case, a and n aren't coprime and we cannot calculate
|
||||
// the inverse. This happens because the values of n are nearly
|
||||
// prime (being the product of two primes) rather than truly
|
||||
// prime.
|
||||
return
|
||||
}
|
||||
|
||||
if x.Cmp(bigOne) < 0 {
|
||||
// 0 is not the multiplicative inverse of any element so, if x
|
||||
// < 1, then x is negative.
|
||||
x.Add(x, n)
|
||||
}
|
||||
|
||||
return x, true
|
||||
}
|
||||
|
||||
// Precompute performs some calculations that speed up private key operations
|
||||
// in the future.
|
||||
func (priv *PrivateKey) Precompute() {
|
||||
if priv.Precomputed.Dp != nil {
|
||||
return
|
||||
}
|
||||
|
||||
priv.Precomputed.Dp = new(big.Int).Sub(priv.Primes[0], bigOne)
|
||||
priv.Precomputed.Dp.Mod(priv.D, priv.Precomputed.Dp)
|
||||
|
||||
priv.Precomputed.Dq = new(big.Int).Sub(priv.Primes[1], bigOne)
|
||||
priv.Precomputed.Dq.Mod(priv.D, priv.Precomputed.Dq)
|
||||
|
||||
priv.Precomputed.Qinv = new(big.Int).ModInverse(priv.Primes[1], priv.Primes[0])
|
||||
|
||||
r := new(big.Int).Mul(priv.Primes[0], priv.Primes[1])
|
||||
priv.Precomputed.CRTValues = make([]CRTValue, len(priv.Primes)-2)
|
||||
for i := 2; i < len(priv.Primes); i++ {
|
||||
prime := priv.Primes[i]
|
||||
values := &priv.Precomputed.CRTValues[i-2]
|
||||
|
||||
values.Exp = new(big.Int).Sub(prime, bigOne)
|
||||
values.Exp.Mod(priv.D, values.Exp)
|
||||
|
||||
values.R = new(big.Int).Set(r)
|
||||
values.Coeff = new(big.Int).ModInverse(r, prime)
|
||||
|
||||
r.Mul(r, prime)
|
||||
}
|
||||
}
|
||||
|
||||
// decrypt performs an RSA decryption, resulting in a plaintext integer. If a
|
||||
// random source is given, RSA blinding is used.
|
||||
func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) {
|
||||
// TODO(agl): can we get away with reusing blinds?
|
||||
if c.Cmp(priv.N) > 0 {
|
||||
err = ErrDecryption
|
||||
return
|
||||
}
|
||||
|
||||
var ir *big.Int
|
||||
if random != nil {
|
||||
// Blinding enabled. Blinding involves multiplying c by r^e.
|
||||
// Then the decryption operation performs (m^e * r^e)^d mod n
|
||||
// which equals mr mod n. The factor of r can then be removed
|
||||
// by multiplying by the multiplicative inverse of r.
|
||||
|
||||
var r *big.Int
|
||||
|
||||
for {
|
||||
r, err = rand.Int(random, priv.N)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if r.Cmp(bigZero) == 0 {
|
||||
r = bigOne
|
||||
}
|
||||
var ok bool
|
||||
ir, ok = modInverse(r, priv.N)
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
bigE := big.NewInt(int64(priv.E))
|
||||
rpowe := new(big.Int).Exp(r, bigE, priv.N)
|
||||
cCopy := new(big.Int).Set(c)
|
||||
cCopy.Mul(cCopy, rpowe)
|
||||
cCopy.Mod(cCopy, priv.N)
|
||||
c = cCopy
|
||||
}
|
||||
|
||||
if priv.Precomputed.Dp == nil {
|
||||
m = new(big.Int).Exp(c, priv.D, priv.N)
|
||||
} else {
|
||||
// We have the precalculated values needed for the CRT.
|
||||
m = new(big.Int).Exp(c, priv.Precomputed.Dp, priv.Primes[0])
|
||||
m2 := new(big.Int).Exp(c, priv.Precomputed.Dq, priv.Primes[1])
|
||||
m.Sub(m, m2)
|
||||
if m.Sign() < 0 {
|
||||
m.Add(m, priv.Primes[0])
|
||||
}
|
||||
m.Mul(m, priv.Precomputed.Qinv)
|
||||
m.Mod(m, priv.Primes[0])
|
||||
m.Mul(m, priv.Primes[1])
|
||||
m.Add(m, m2)
|
||||
|
||||
for i, values := range priv.Precomputed.CRTValues {
|
||||
prime := priv.Primes[2+i]
|
||||
m2.Exp(c, values.Exp, prime)
|
||||
m2.Sub(m2, m)
|
||||
m2.Mul(m2, values.Coeff)
|
||||
m2.Mod(m2, prime)
|
||||
if m2.Sign() < 0 {
|
||||
m2.Add(m2, prime)
|
||||
}
|
||||
m2.Mul(m2, values.R)
|
||||
m.Add(m, m2)
|
||||
}
|
||||
}
|
||||
|
||||
if ir != nil {
|
||||
// Unblind.
|
||||
m.Mul(m, ir)
|
||||
m.Mod(m, priv.N)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func decryptAndCheck(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) {
|
||||
m, err = decrypt(random, priv, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// In order to defend against errors in the CRT computation, m^e is
|
||||
// calculated, which should match the original ciphertext.
|
||||
check := encrypt(new(big.Int), &priv.PublicKey, m)
|
||||
if c.Cmp(check) != 0 {
|
||||
return nil, errors.New("rsa: internal error")
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// DecryptOAEP decrypts ciphertext using RSA-OAEP.
|
||||
|
||||
// OAEP is parameterised by a hash function that is used as a random oracle.
|
||||
// Encryption and decryption of a given message must use the same hash function
|
||||
// and sha256.New() is a reasonable choice.
|
||||
//
|
||||
// The random parameter, if not nil, is used to blind the private-key operation
|
||||
// and avoid timing side-channel attacks. Blinding is purely internal to this
|
||||
// function – the random data need not match that used when encrypting.
|
||||
//
|
||||
// The label parameter must match the value given when encrypting. See
|
||||
// EncryptOAEP for details.
|
||||
func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) (msg []byte, err error) {
|
||||
if err := checkPub(&priv.PublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k := (priv.N.BitLen() + 7) / 8
|
||||
if len(ciphertext) > k ||
|
||||
k < hash.Size()*2+2 {
|
||||
err = ErrDecryption
|
||||
return
|
||||
}
|
||||
|
||||
c := new(big.Int).SetBytes(ciphertext)
|
||||
|
||||
m, err := decrypt(random, priv, c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hash.Write(label)
|
||||
lHash := hash.Sum(nil)
|
||||
hash.Reset()
|
||||
|
||||
// Converting the plaintext number to bytes will strip any
|
||||
// leading zeros so we may have to left pad. We do this unconditionally
|
||||
// to avoid leaking timing information. (Although we still probably
|
||||
// leak the number of leading zeros. It's not clear that we can do
|
||||
// anything about this.)
|
||||
em := leftPad(m.Bytes(), k)
|
||||
|
||||
firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0)
|
||||
|
||||
seed := em[1 : hash.Size()+1]
|
||||
db := em[hash.Size()+1:]
|
||||
|
||||
mgf1XOR(seed, hash, db)
|
||||
mgf1XOR(db, hash, seed)
|
||||
|
||||
lHash2 := db[0:hash.Size()]
|
||||
|
||||
// We have to validate the plaintext in constant time in order to avoid
|
||||
// attacks like: J. Manger. A Chosen Ciphertext Attack on RSA Optimal
|
||||
// Asymmetric Encryption Padding (OAEP) as Standardized in PKCS #1
|
||||
// v2.0. In J. Kilian, editor, Advances in Cryptology.
|
||||
lHash2Good := subtle.ConstantTimeCompare(lHash, lHash2)
|
||||
|
||||
// The remainder of the plaintext must be zero or more 0x00, followed
|
||||
// by 0x01, followed by the message.
|
||||
// lookingForIndex: 1 iff we are still looking for the 0x01
|
||||
// index: the offset of the first 0x01 byte
|
||||
// invalid: 1 iff we saw a non-zero byte before the 0x01.
|
||||
var lookingForIndex, index, invalid int
|
||||
lookingForIndex = 1
|
||||
rest := db[hash.Size():]
|
||||
|
||||
for i := 0; i < len(rest); i++ {
|
||||
equals0 := subtle.ConstantTimeByteEq(rest[i], 0)
|
||||
equals1 := subtle.ConstantTimeByteEq(rest[i], 1)
|
||||
index = subtle.ConstantTimeSelect(lookingForIndex&equals1, i, index)
|
||||
lookingForIndex = subtle.ConstantTimeSelect(equals1, 0, lookingForIndex)
|
||||
invalid = subtle.ConstantTimeSelect(lookingForIndex&^equals0, 1, invalid)
|
||||
}
|
||||
|
||||
if firstByteIsZero&lHash2Good&^invalid&^lookingForIndex != 1 {
|
||||
err = ErrDecryption
|
||||
return
|
||||
}
|
||||
|
||||
msg = rest[index+1:]
|
||||
return
|
||||
}
|
||||
|
||||
// leftPad returns a new slice of length size. The contents of input are right
|
||||
// aligned in the new slice.
|
||||
func leftPad(input []byte, size int) (out []byte) {
|
||||
n := len(input)
|
||||
if n > size {
|
||||
n = size
|
||||
}
|
||||
out = make([]byte, size)
|
||||
copy(out[len(out)-n:], input)
|
||||
return
|
||||
}
|
|
@ -30,6 +30,18 @@
|
|||
"revision": "902d95d9f311ae585ee98cfd18f418b467d60d5a",
|
||||
"revisionTime": "2016-07-20T05:16:58Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "LLVyR2dAgkihu0+HdZF+JK0gMMs=",
|
||||
"path": "github.com/agl/ed25519",
|
||||
"revision": "278e1ec8e8a6e017cd07577924d6766039146ced",
|
||||
"revisionTime": "2015-08-30T18:26:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "30PBqj9BW03KCVqASvLg3bR+xYc=",
|
||||
"path": "github.com/agl/ed25519/edwards25519",
|
||||
"revision": "278e1ec8e8a6e017cd07577924d6766039146ced",
|
||||
"revisionTime": "2015-08-30T18:26:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "d6798KSc0jDg2MHNxKdgyNfMK7A=",
|
||||
"path": "github.com/armon/go-metrics",
|
||||
|
@ -606,6 +618,60 @@
|
|||
"revision": "0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74",
|
||||
"revisionTime": "2016-02-02T18:50:14Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "VJk3rOWfxEV9Ilig5lgzH1qg8Ss=",
|
||||
"path": "github.com/keybase/go-crypto/brainpool",
|
||||
"revision": "48b6e293058d0efde5e59efe391e7f9bb2aa66cb",
|
||||
"revisionTime": "2016-07-12T12:31:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "rnRjEJs5luF+DIXp2J6LFcQk8Gg=",
|
||||
"path": "github.com/keybase/go-crypto/cast5",
|
||||
"revision": "48b6e293058d0efde5e59efe391e7f9bb2aa66cb",
|
||||
"revisionTime": "2016-07-12T12:31:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "0LLAV6MWMUedVOL8SsJ4mVUW5rI=",
|
||||
"path": "github.com/keybase/go-crypto/openpgp",
|
||||
"revision": "48b6e293058d0efde5e59efe391e7f9bb2aa66cb",
|
||||
"revisionTime": "2016-07-12T12:31:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "y61I7+hCekP1Rk0qxgUQ+iozXak=",
|
||||
"path": "github.com/keybase/go-crypto/openpgp/armor",
|
||||
"revision": "48b6e293058d0efde5e59efe391e7f9bb2aa66cb",
|
||||
"revisionTime": "2016-07-12T12:31:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "uxXG9IC/XF8jwwvZUbW65+x8/+M=",
|
||||
"path": "github.com/keybase/go-crypto/openpgp/elgamal",
|
||||
"revision": "48b6e293058d0efde5e59efe391e7f9bb2aa66cb",
|
||||
"revisionTime": "2016-07-12T12:31:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "EyUf82Yknzc75m8RcA21CNQINw0=",
|
||||
"path": "github.com/keybase/go-crypto/openpgp/errors",
|
||||
"revision": "48b6e293058d0efde5e59efe391e7f9bb2aa66cb",
|
||||
"revisionTime": "2016-07-12T12:31:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "jHo/FzWo455asp7X4+3de+yY1bM=",
|
||||
"path": "github.com/keybase/go-crypto/openpgp/packet",
|
||||
"revision": "48b6e293058d0efde5e59efe391e7f9bb2aa66cb",
|
||||
"revisionTime": "2016-07-12T12:31:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "BGDxg1Xtsz0DSPzdQGJLLQqfYc8=",
|
||||
"path": "github.com/keybase/go-crypto/openpgp/s2k",
|
||||
"revision": "48b6e293058d0efde5e59efe391e7f9bb2aa66cb",
|
||||
"revisionTime": "2016-07-12T12:31:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "rE3pp7b3gfcmBregzpIvN5IdFhY=",
|
||||
"path": "github.com/keybase/go-crypto/rsa",
|
||||
"revision": "48b6e293058d0efde5e59efe391e7f9bb2aa66cb",
|
||||
"revisionTime": "2016-07-12T12:31:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "QK3MNUdQwUBuznCHZcPijU/3DyI=",
|
||||
"path": "github.com/lib/pq",
|
||||
|
@ -720,12 +786,6 @@
|
|||
"revision": "bc89c496413265e715159bdc8478ee9a92fdc265",
|
||||
"revisionTime": "2016-07-08T11:45:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "TT1rac6kpQp2vz24m5yDGUNQ/QQ=",
|
||||
"path": "golang.org/x/crypto/cast5",
|
||||
"revision": "bc89c496413265e715159bdc8478ee9a92fdc265",
|
||||
"revisionTime": "2016-07-08T11:45:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "h+pFYiRHBogczS8/F1NoN3Ata44=",
|
||||
"path": "golang.org/x/crypto/curve25519",
|
||||
|
@ -750,42 +810,6 @@
|
|||
"revision": "bc89c496413265e715159bdc8478ee9a92fdc265",
|
||||
"revisionTime": "2016-07-08T11:45:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "N+Xd7xy/cpHaodLtnUYIj3UIDHA=",
|
||||
"path": "golang.org/x/crypto/openpgp",
|
||||
"revision": "bc89c496413265e715159bdc8478ee9a92fdc265",
|
||||
"revisionTime": "2016-07-08T11:45:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "olOKkhrdkYQHZ0lf1orrFQPQrv4=",
|
||||
"path": "golang.org/x/crypto/openpgp/armor",
|
||||
"revision": "bc89c496413265e715159bdc8478ee9a92fdc265",
|
||||
"revisionTime": "2016-07-08T11:45:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "eo/KtdjieJQXH7Qy+faXFcF70ME=",
|
||||
"path": "golang.org/x/crypto/openpgp/elgamal",
|
||||
"revision": "bc89c496413265e715159bdc8478ee9a92fdc265",
|
||||
"revisionTime": "2016-07-08T11:45:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "rlxVSaGgqdAgwblsErxTxIfuGfg=",
|
||||
"path": "golang.org/x/crypto/openpgp/errors",
|
||||
"revision": "bc89c496413265e715159bdc8478ee9a92fdc265",
|
||||
"revisionTime": "2016-07-08T11:45:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "IjQVXdetvKXlSW2yZHDd2m1TSoo=",
|
||||
"path": "golang.org/x/crypto/openpgp/packet",
|
||||
"revision": "bc89c496413265e715159bdc8478ee9a92fdc265",
|
||||
"revisionTime": "2016-07-08T11:45:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "s2qT4UwvzBSkzXuiuMkowif1Olw=",
|
||||
"path": "golang.org/x/crypto/openpgp/s2k",
|
||||
"revision": "bc89c496413265e715159bdc8478ee9a92fdc265",
|
||||
"revisionTime": "2016-07-08T11:45:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Lzk0xTzvdMm8Y4bErxJPSOj8iHk=",
|
||||
"path": "golang.org/x/crypto/ssh",
|
||||
|
|
Loading…
Reference in New Issue