update vendor keybase/go-crypto (#10234)

release/v1.15
6543 2020-02-11 19:58:23 +01:00 committed by GitHub
parent 86fdba177a
commit bfd62b6f01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1062 additions and 769 deletions

2
go.mod
View File

@ -58,7 +58,7 @@ require (
github.com/jmhodges/levigo v1.0.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect
github.com/joho/godotenv v1.3.0 // indirect github.com/joho/godotenv v1.3.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657 github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657
github.com/keybase/go-crypto v0.0.0-20170605145657-00ac4db533f6 github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
github.com/klauspost/compress v1.9.2 github.com/klauspost/compress v1.9.2
github.com/lafriks/xormstore v1.3.2 github.com/lafriks/xormstore v1.3.2
github.com/lib/pq v1.2.0 github.com/lib/pq v1.2.0

7
go.sum
View File

@ -9,8 +9,6 @@ cloud.google.com/go v0.45.0 h1:bALuGBSgE+BD4rxsopAYlqjcwqcQtye6pWG4bC3N/k0=
cloud.google.com/go v0.45.0/go.mod h1:452BcPOeI9AZfbvDw0Tbo7D32wA+WX9WME8AZwMEDZU= cloud.google.com/go v0.45.0/go.mod h1:452BcPOeI9AZfbvDw0Tbo7D32wA+WX9WME8AZwMEDZU=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
gitea.com/lunny/levelqueue v0.1.0 h1:7wMk0VH6mvKN6vZEZCy9nUDgRmdPLgeNrm1NkW8EHNk=
gitea.com/lunny/levelqueue v0.1.0/go.mod h1:G7hVb908t0Bl0uk7zGSg14fyzNtxgtD9Shf04wkMK7s=
gitea.com/lunny/levelqueue v0.2.0 h1:lR/5EAwQtFcn5YvPEkNMw0p9pAy2/O2nSP5ImECLA2E= gitea.com/lunny/levelqueue v0.2.0 h1:lR/5EAwQtFcn5YvPEkNMw0p9pAy2/O2nSP5ImECLA2E=
gitea.com/lunny/levelqueue v0.2.0/go.mod h1:G7hVb908t0Bl0uk7zGSg14fyzNtxgtD9Shf04wkMK7s= gitea.com/lunny/levelqueue v0.2.0/go.mod h1:G7hVb908t0Bl0uk7zGSg14fyzNtxgtD9Shf04wkMK7s=
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b h1:vXt85uYV17KURaUlhU7v4GbCShkqRZDSfo0TkC0YCjQ= gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b h1:vXt85uYV17KURaUlhU7v4GbCShkqRZDSfo0TkC0YCjQ=
@ -340,8 +338,8 @@ github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657 h1:vE7J1m7c
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/keybase/go-crypto v0.0.0-20170605145657-00ac4db533f6 h1:9mszGwKDxHEY2cy+9XxCQKWIfkGPSAEFrcN8ghzyAKg= github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+4tLukHoB9iqHOu3LmLhRmgUxZo6Vp4=
github.com/keybase/go-crypto v0.0.0-20170605145657-00ac4db533f6/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.9.2 h1:LfVyl+ZlLlLDeQ/d2AqfGIIH4qEDu0Ed2S5GyhCWIWY= github.com/klauspost/compress v1.9.2 h1:LfVyl+ZlLlLDeQ/d2AqfGIIH4qEDu0Ed2S5GyhCWIWY=
@ -508,7 +506,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/src-d/enry v1.7.3 h1:jG2fmEaQaURh0qqU/sn82BRzVa6d4EVHJIw6gc98bak=
github.com/src-d/enry/v2 v2.1.0 h1:z1L8t+B8bh3mmjPkJrgOTnVRpFGmTPJsplHX9wAn6BI= github.com/src-d/enry/v2 v2.1.0 h1:z1L8t+B8bh3mmjPkJrgOTnVRpFGmTPJsplHX9wAn6BI=
github.com/src-d/enry/v2 v2.1.0/go.mod h1:qQeCMRwzMF3ckeGr+h0tJLdxXnq+NVZsIDMELj0t028= github.com/src-d/enry/v2 v2.1.0/go.mod h1:qQeCMRwzMF3ckeGr+h0tJLdxXnq+NVZsIDMELj0t028=
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=

View File

@ -4,7 +4,7 @@
// Package cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common // Package cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common
// OpenPGP cipher. // OpenPGP cipher.
package cast5 package cast5 // import "github.com/keybase/go-crypto/cast5"
import "errors" import "errors"

View File

@ -0,0 +1,8 @@
// Copyright 2012 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.
// This code was translated into a form compatible with 6a from the public
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
#define REDMASK51 0x0007FFFFFFFFFFFF

View File

@ -3,12 +3,12 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This code was translated into a form compatible with 6a from the public // This code was translated into a form compatible with 6a from the public
// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
DATA ·REDMASK51(SB)/8, $0x0007FFFFFFFFFFFF // These constants cannot be encoded in non-MOVQ immediates.
GLOBL ·REDMASK51(SB), 8, $8 // We access them directly from memory instead.
DATA ·_121666_213(SB)/8, $996687872 DATA ·_121666_213(SB)/8, $996687872
GLOBL ·_121666_213(SB), 8, $8 GLOBL ·_121666_213(SB), 8, $8

View File

@ -2,87 +2,64 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This code was translated into a form compatible with 6a from the public
// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
// func cswap(inout *[5]uint64, v uint64) // func cswap(inout *[4][5]uint64, v uint64)
TEXT ·cswap(SB),7,$0 TEXT ·cswap(SB),7,$0
MOVQ inout+0(FP),DI MOVQ inout+0(FP),DI
MOVQ v+8(FP),SI MOVQ v+8(FP),SI
CMPQ SI,$1 SUBQ $1, SI
MOVQ 0(DI),SI NOTQ SI
MOVQ 80(DI),DX MOVQ SI, X15
MOVQ 8(DI),CX PSHUFD $0x44, X15, X15
MOVQ 88(DI),R8
MOVQ SI,R9 MOVOU 0(DI), X0
CMOVQEQ DX,SI MOVOU 16(DI), X2
CMOVQEQ R9,DX MOVOU 32(DI), X4
MOVQ CX,R9 MOVOU 48(DI), X6
CMOVQEQ R8,CX MOVOU 64(DI), X8
CMOVQEQ R9,R8 MOVOU 80(DI), X1
MOVQ SI,0(DI) MOVOU 96(DI), X3
MOVQ DX,80(DI) MOVOU 112(DI), X5
MOVQ CX,8(DI) MOVOU 128(DI), X7
MOVQ R8,88(DI) MOVOU 144(DI), X9
MOVQ 16(DI),SI
MOVQ 96(DI),DX MOVO X1, X10
MOVQ 24(DI),CX MOVO X3, X11
MOVQ 104(DI),R8 MOVO X5, X12
MOVQ SI,R9 MOVO X7, X13
CMOVQEQ DX,SI MOVO X9, X14
CMOVQEQ R9,DX
MOVQ CX,R9 PXOR X0, X10
CMOVQEQ R8,CX PXOR X2, X11
CMOVQEQ R9,R8 PXOR X4, X12
MOVQ SI,16(DI) PXOR X6, X13
MOVQ DX,96(DI) PXOR X8, X14
MOVQ CX,24(DI) PAND X15, X10
MOVQ R8,104(DI) PAND X15, X11
MOVQ 32(DI),SI PAND X15, X12
MOVQ 112(DI),DX PAND X15, X13
MOVQ 40(DI),CX PAND X15, X14
MOVQ 120(DI),R8 PXOR X10, X0
MOVQ SI,R9 PXOR X10, X1
CMOVQEQ DX,SI PXOR X11, X2
CMOVQEQ R9,DX PXOR X11, X3
MOVQ CX,R9 PXOR X12, X4
CMOVQEQ R8,CX PXOR X12, X5
CMOVQEQ R9,R8 PXOR X13, X6
MOVQ SI,32(DI) PXOR X13, X7
MOVQ DX,112(DI) PXOR X14, X8
MOVQ CX,40(DI) PXOR X14, X9
MOVQ R8,120(DI)
MOVQ 48(DI),SI MOVOU X0, 0(DI)
MOVQ 128(DI),DX MOVOU X2, 16(DI)
MOVQ 56(DI),CX MOVOU X4, 32(DI)
MOVQ 136(DI),R8 MOVOU X6, 48(DI)
MOVQ SI,R9 MOVOU X8, 64(DI)
CMOVQEQ DX,SI MOVOU X1, 80(DI)
CMOVQEQ R9,DX MOVOU X3, 96(DI)
MOVQ CX,R9 MOVOU X5, 112(DI)
CMOVQEQ R8,CX MOVOU X7, 128(DI)
CMOVQEQ R9,R8 MOVOU X9, 144(DI)
MOVQ SI,48(DI)
MOVQ DX,128(DI)
MOVQ CX,56(DI)
MOVQ R8,136(DI)
MOVQ 64(DI),SI
MOVQ 144(DI),DX
MOVQ 72(DI),CX
MOVQ 152(DI),R8
MOVQ SI,R9
CMOVQEQ DX,SI
CMOVQEQ R9,DX
MOVQ CX,R9
CMOVQEQ R8,CX
CMOVQEQ R9,R8
MOVQ SI,64(DI)
MOVQ DX,144(DI)
MOVQ CX,72(DI)
MOVQ R8,152(DI)
MOVQ DI,AX
MOVQ SI,DX
RET RET

View File

@ -2,12 +2,16 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// We have a implementation in amd64 assembly so this code is only run on // We have an implementation in amd64 assembly so this code is only run on
// non-amd64 platforms. The amd64 assembly does not support gccgo. // non-amd64 platforms. The amd64 assembly does not support gccgo.
// +build !amd64 gccgo appengine // +build !amd64 gccgo appengine
package curve25519 package curve25519
import (
"encoding/binary"
)
// This code is a port of the public domain, "ref10" implementation of // This code is a port of the public domain, "ref10" implementation of
// curve25519 from SUPERCOP 20130419 by D. J. Bernstein. // curve25519 from SUPERCOP 20130419 by D. J. Bernstein.
@ -50,17 +54,11 @@ func feCopy(dst, src *fieldElement) {
// //
// Preconditions: b in {0,1}. // Preconditions: b in {0,1}.
func feCSwap(f, g *fieldElement, b int32) { func feCSwap(f, g *fieldElement, b int32) {
var x fieldElement
b = -b b = -b
for i := range x {
x[i] = b & (f[i] ^ g[i])
}
for i := range f { for i := range f {
f[i] ^= x[i] t := b & (f[i] ^ g[i])
} f[i] ^= t
for i := range g { g[i] ^= t
g[i] ^= x[i]
} }
} }
@ -75,12 +73,7 @@ func load3(in []byte) int64 {
// load4 reads a 32-bit, little-endian value from in. // load4 reads a 32-bit, little-endian value from in.
func load4(in []byte) int64 { func load4(in []byte) int64 {
var r int64 return int64(binary.LittleEndian.Uint32(in))
r = int64(in[0])
r |= int64(in[1]) << 8
r |= int64(in[2]) << 16
r |= int64(in[3]) << 24
return r
} }
func feFromBytes(dst *fieldElement, src *[32]byte) { func feFromBytes(dst *fieldElement, src *[32]byte) {

View File

@ -16,19 +16,30 @@ func copyReverse(dst []byte, src []byte) {
// Curve 25519 multiplication functions expect scalars in reverse // Curve 25519 multiplication functions expect scalars in reverse
// order than PGP. To keep the curve25519Curve type consistent // order than PGP. To keep the curve25519Curve type consistent
// with other curves, we reverse it here. // with other curves, we reverse it here.
for i, j := 0, len(src)-1; j >= 0; i, j = i+1, j-1 { for i, j := 0, len(src)-1; j >= 0 && i < len(dst); i, j = i+1, j-1 {
dst[i] = src[j] dst[i] = src[j]
} }
} }
func copyTruncate(dst []byte, src []byte) {
lenDst, lenSrc := len(dst), len(src)
if lenDst == lenSrc {
copy(dst, src)
} else if lenDst > lenSrc {
copy(dst[lenDst-lenSrc:lenDst], src)
} else if lenDst < lenSrc {
copy(dst, src[:lenDst])
}
}
func (cv25519Curve) ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) { func (cv25519Curve) ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) {
// Assume y1 is 0 with cv25519. // Assume y1 is 0 with cv25519.
var dst [32]byte var dst [32]byte
var x1Bytes [32]byte var x1Bytes [32]byte
var scalarBytes [32]byte var scalarBytes [32]byte
copy(x1Bytes[:], x1.Bytes()[:32]) copyTruncate(x1Bytes[:], x1.Bytes())
copyReverse(scalarBytes[:], scalar[:32]) copyReverse(scalarBytes[:], scalar)
scalarMult(&dst, &scalarBytes, &x1Bytes) scalarMult(&dst, &scalarBytes, &x1Bytes)
@ -63,7 +74,7 @@ func (cv25519Curve) MarshalType40(x, y *big.Int) []byte {
ret[0] = 0x40 ret[0] = 0x40
xBytes := x.Bytes() xBytes := x.Bytes()
copy(ret[1+byteLen-len(xBytes):], xBytes) copyTruncate(ret[1:], xBytes)
return ret return ret
} }
@ -91,10 +102,10 @@ func initCv25519() {
// Some code relies on these parameters being available for // Some code relies on these parameters being available for
// checking Curve coordinate length. They should not be used // checking Curve coordinate length. They should not be used
// directly for any calculations. // directly for any calculations.
cv25519.P, _ = new (big.Int).SetString("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", 16) cv25519.P, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", 16)
cv25519.N, _ = new (big.Int).SetString("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed", 16) cv25519.N, _ = new(big.Int).SetString("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed", 16)
cv25519.Gx, _ = new (big.Int).SetString("9", 16) cv25519.Gx, _ = new(big.Int).SetString("9", 16)
cv25519.Gy, _ = new (big.Int).SetString("20ae19a1b8a086b4e01edd2c7748d14c923d4d7e6d7c61b229e9c5a27eced3d9", 16) cv25519.Gy, _ = new(big.Int).SetString("20ae19a1b8a086b4e01edd2c7748d14c923d4d7e6d7c61b229e9c5a27eced3d9", 16)
cv25519.BitSize = 256 cv25519.BitSize = 256
} }

View File

@ -3,8 +3,8 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package curve25519 provides an implementation of scalar multiplication on // Package curve25519 provides an implementation of scalar multiplication on
// the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html // the elliptic curve known as curve25519. See https://cr.yp.to/ecdh.html
package curve25519 package curve25519 // import "github.com/keybase/go-crypto/curve25519"
// basePoint is the x coordinate of the generator of the curve. // basePoint is the x coordinate of the generator of the curve.
var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

View File

@ -3,33 +3,22 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This code was translated into a form compatible with 6a from the public // This code was translated into a form compatible with 6a from the public
// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
#include "const_amd64.h"
// func freeze(inout *[5]uint64) // func freeze(inout *[5]uint64)
TEXT ·freeze(SB),7,$96-8 TEXT ·freeze(SB),7,$0-8
MOVQ inout+0(FP), DI MOVQ inout+0(FP), DI
MOVQ SP,R11
MOVQ $31,CX
NOTQ CX
ANDQ CX,SP
ADDQ $32,SP
MOVQ R11,0(SP)
MOVQ R12,8(SP)
MOVQ R13,16(SP)
MOVQ R14,24(SP)
MOVQ R15,32(SP)
MOVQ BX,40(SP)
MOVQ BP,48(SP)
MOVQ 0(DI),SI MOVQ 0(DI),SI
MOVQ 8(DI),DX MOVQ 8(DI),DX
MOVQ 16(DI),CX MOVQ 16(DI),CX
MOVQ 24(DI),R8 MOVQ 24(DI),R8
MOVQ 32(DI),R9 MOVQ 32(DI),R9
MOVQ ·REDMASK51(SB),AX MOVQ $REDMASK51,AX
MOVQ AX,R10 MOVQ AX,R10
SUBQ $18,R10 SUBQ $18,R10
MOVQ $3,R11 MOVQ $3,R11
@ -81,14 +70,4 @@ REDUCELOOP:
MOVQ CX,16(DI) MOVQ CX,16(DI)
MOVQ R8,24(DI) MOVQ R8,24(DI)
MOVQ R9,32(DI) MOVQ R9,32(DI)
MOVQ 0(SP),R11
MOVQ 8(SP),R12
MOVQ 16(SP),R13
MOVQ 24(SP),R14
MOVQ 32(SP),R15
MOVQ 40(SP),BX
MOVQ 48(SP),BP
MOVQ R11,SP
MOVQ DI,AX
MOVQ SI,DX
RET RET

File diff suppressed because it is too large Load Diff

View File

@ -3,40 +3,28 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This code was translated into a form compatible with 6a from the public // This code was translated into a form compatible with 6a from the public
// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
#include "const_amd64.h"
// func mul(dest, a, b *[5]uint64) // func mul(dest, a, b *[5]uint64)
TEXT ·mul(SB),0,$128-24 TEXT ·mul(SB),0,$16-24
MOVQ dest+0(FP), DI MOVQ dest+0(FP), DI
MOVQ a+8(FP), SI MOVQ a+8(FP), SI
MOVQ b+16(FP), DX MOVQ b+16(FP), DX
MOVQ SP,R11
MOVQ $31,CX
NOTQ CX
ANDQ CX,SP
ADDQ $32,SP
MOVQ R11,0(SP)
MOVQ R12,8(SP)
MOVQ R13,16(SP)
MOVQ R14,24(SP)
MOVQ R15,32(SP)
MOVQ BX,40(SP)
MOVQ BP,48(SP)
MOVQ DI,56(SP)
MOVQ DX,CX MOVQ DX,CX
MOVQ 24(SI),DX MOVQ 24(SI),DX
IMUL3Q $19,DX,AX IMUL3Q $19,DX,AX
MOVQ AX,64(SP) MOVQ AX,0(SP)
MULQ 16(CX) MULQ 16(CX)
MOVQ AX,R8 MOVQ AX,R8
MOVQ DX,R9 MOVQ DX,R9
MOVQ 32(SI),DX MOVQ 32(SI),DX
IMUL3Q $19,DX,AX IMUL3Q $19,DX,AX
MOVQ AX,72(SP) MOVQ AX,8(SP)
MULQ 8(CX) MULQ 8(CX)
ADDQ AX,R8 ADDQ AX,R8
ADCQ DX,R9 ADCQ DX,R9
@ -111,11 +99,11 @@ TEXT ·mul(SB),0,$128-24
MULQ 8(CX) MULQ 8(CX)
ADDQ AX,BX ADDQ AX,BX
ADCQ DX,BP ADCQ DX,BP
MOVQ 64(SP),AX MOVQ 0(SP),AX
MULQ 24(CX) MULQ 24(CX)
ADDQ AX,R10 ADDQ AX,R10
ADCQ DX,R11 ADCQ DX,R11
MOVQ 64(SP),AX MOVQ 0(SP),AX
MULQ 32(CX) MULQ 32(CX)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
@ -123,19 +111,19 @@ TEXT ·mul(SB),0,$128-24
MULQ 0(CX) MULQ 0(CX)
ADDQ AX,BX ADDQ AX,BX
ADCQ DX,BP ADCQ DX,BP
MOVQ 72(SP),AX MOVQ 8(SP),AX
MULQ 16(CX) MULQ 16(CX)
ADDQ AX,R10 ADDQ AX,R10
ADCQ DX,R11 ADCQ DX,R11
MOVQ 72(SP),AX MOVQ 8(SP),AX
MULQ 24(CX) MULQ 24(CX)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ 72(SP),AX MOVQ 8(SP),AX
MULQ 32(CX) MULQ 32(CX)
ADDQ AX,R14 ADDQ AX,R14
ADCQ DX,R15 ADCQ DX,R15
MOVQ ·REDMASK51(SB),SI MOVQ $REDMASK51,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8
ANDQ SI,R8 ANDQ SI,R8
SHLQ $13,R11:R10 SHLQ $13,R11:R10
@ -178,14 +166,4 @@ TEXT ·mul(SB),0,$128-24
MOVQ R9,16(DI) MOVQ R9,16(DI)
MOVQ AX,24(DI) MOVQ AX,24(DI)
MOVQ R10,32(DI) MOVQ R10,32(DI)
MOVQ 0(SP),R11
MOVQ 8(SP),R12
MOVQ 16(SP),R13
MOVQ 24(SP),R14
MOVQ 32(SP),R15
MOVQ 40(SP),BX
MOVQ 48(SP),BP
MOVQ R11,SP
MOVQ DI,AX
MOVQ SI,DX
RET RET

View File

@ -3,28 +3,17 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This code was translated into a form compatible with 6a from the public // This code was translated into a form compatible with 6a from the public
// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
#include "const_amd64.h"
// func square(out, in *[5]uint64) // func square(out, in *[5]uint64)
TEXT ·square(SB),7,$96-16 TEXT ·square(SB),7,$0-16
MOVQ out+0(FP), DI MOVQ out+0(FP), DI
MOVQ in+8(FP), SI MOVQ in+8(FP), SI
MOVQ SP,R11
MOVQ $31,CX
NOTQ CX
ANDQ CX,SP
ADDQ $32, SP
MOVQ R11,0(SP)
MOVQ R12,8(SP)
MOVQ R13,16(SP)
MOVQ R14,24(SP)
MOVQ R15,32(SP)
MOVQ BX,40(SP)
MOVQ BP,48(SP)
MOVQ 0(SI),AX MOVQ 0(SI),AX
MULQ 0(SI) MULQ 0(SI)
MOVQ AX,CX MOVQ AX,CX
@ -97,7 +86,7 @@ TEXT ·square(SB),7,$96-16
MULQ 32(SI) MULQ 32(SI)
ADDQ AX,R13 ADDQ AX,R13
ADCQ DX,R14 ADCQ DX,R14
MOVQ ·REDMASK51(SB),SI MOVQ $REDMASK51,SI
SHLQ $13,R8:CX SHLQ $13,R8:CX
ANDQ SI,CX ANDQ SI,CX
SHLQ $13,R10:R9 SHLQ $13,R10:R9
@ -140,14 +129,4 @@ TEXT ·square(SB),7,$96-16
MOVQ R9,16(DI) MOVQ R9,16(DI)
MOVQ AX,24(DI) MOVQ AX,24(DI)
MOVQ R10,32(DI) MOVQ R10,32(DI)
MOVQ 0(SP),R11
MOVQ 8(SP),R12
MOVQ 16(SP),R13
MOVQ 24(SP),R14
MOVQ 32(SP),R15
MOVQ 40(SP),BX
MOVQ 48(SP),BP
MOVQ R11,SP
MOVQ DI,AX
MOVQ SI,DX
RET RET

View File

@ -3,20 +3,23 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package ed25519 implements the Ed25519 signature algorithm. See // Package ed25519 implements the Ed25519 signature algorithm. See
// http://ed25519.cr.yp.to/. // https://ed25519.cr.yp.to/.
// //
// These functions are also compatible with the “Ed25519” function defined in // These functions are also compatible with the “Ed25519” function defined in
// https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05. // RFC 8032. However, unlike RFC 8032's formulation, this package's private key
// representation includes a public key suffix to make multiple signing
// operations with the same key more efficient. This package refers to the RFC
// 8032 private key as the “seed”.
package ed25519 package ed25519
// This code is a port of the public domain, “ref10” implementation of ed25519 // This code is a port of the public domain, “ref10” implementation of ed25519
// from SUPERCOP. // from SUPERCOP.
import ( import (
"bytes"
"crypto" "crypto"
cryptorand "crypto/rand" cryptorand "crypto/rand"
"crypto/sha512" "crypto/sha512"
"crypto/subtle"
"errors" "errors"
"io" "io"
"strconv" "strconv"
@ -31,6 +34,8 @@ const (
PrivateKeySize = 64 PrivateKeySize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package. // SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64 SignatureSize = 64
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
SeedSize = 32
) )
// PublicKey is the type of Ed25519 public keys. // PublicKey is the type of Ed25519 public keys.
@ -46,6 +51,15 @@ func (priv PrivateKey) Public() crypto.PublicKey {
return PublicKey(publicKey) return PublicKey(publicKey)
} }
// Seed returns the private key seed corresponding to priv. It is provided for
// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
// in this package.
func (priv PrivateKey) Seed() []byte {
seed := make([]byte, SeedSize)
copy(seed, priv[:32])
return seed
}
// Sign signs the given message with priv. // Sign signs the given message with priv.
// Ed25519 performs two passes over messages to be signed and therefore cannot // Ed25519 performs two passes over messages to be signed and therefore cannot
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to // handle pre-hashed messages. Thus opts.HashFunc() must return zero to
@ -61,19 +75,33 @@ func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOp
// GenerateKey generates a public/private key pair using entropy from rand. // GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used. // If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) { func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
if rand == nil { if rand == nil {
rand = cryptorand.Reader rand = cryptorand.Reader
} }
privateKey = make([]byte, PrivateKeySize) seed := make([]byte, SeedSize)
publicKey = make([]byte, PublicKeySize) if _, err := io.ReadFull(rand, seed); err != nil {
_, err = io.ReadFull(rand, privateKey[:32])
if err != nil {
return nil, nil, err return nil, nil, err
} }
digest := sha512.Sum512(privateKey[:32]) privateKey := NewKeyFromSeed(seed)
publicKey := make([]byte, PublicKeySize)
copy(publicKey, privateKey[32:])
return publicKey, privateKey, nil
}
// NewKeyFromSeed calculates a private key from a seed. It will panic if
// len(seed) is not SeedSize. This function is provided for interoperability
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
// package.
func NewKeyFromSeed(seed []byte) PrivateKey {
if l := len(seed); l != SeedSize {
panic("ed25519: bad seed length: " + strconv.Itoa(l))
}
digest := sha512.Sum512(seed)
digest[0] &= 248 digest[0] &= 248
digest[31] &= 127 digest[31] &= 127
digest[31] |= 64 digest[31] |= 64
@ -85,10 +113,11 @@ func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, er
var publicKeyBytes [32]byte var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes) A.ToBytes(&publicKeyBytes)
privateKey := make([]byte, PrivateKeySize)
copy(privateKey, seed)
copy(privateKey[32:], publicKeyBytes[:]) copy(privateKey[32:], publicKeyBytes[:])
copy(publicKey, publicKeyBytes[:])
return publicKey, privateKey, nil return privateKey
} }
// Sign signs the message with privateKey and returns a signature. It will // Sign signs the message with privateKey and returns a signature. It will
@ -171,11 +200,18 @@ func Verify(publicKey PublicKey, message, sig []byte) bool {
edwards25519.ScReduce(&hReduced, &digest) edwards25519.ScReduce(&hReduced, &digest)
var R edwards25519.ProjectiveGroupElement var R edwards25519.ProjectiveGroupElement
var b [32]byte var s [32]byte
copy(b[:], sig[32:]) copy(s[:], sig[32:])
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
// the range [0, order) in order to prevent signature malleability.
if !edwards25519.ScMinimal(&s) {
return false
}
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
var checkR [32]byte var checkR [32]byte
R.ToBytes(&checkR) R.ToBytes(&checkR)
return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1 return bytes.Equal(sig[:32], checkR[:])
} }

View File

@ -4,6 +4,8 @@
package edwards25519 package edwards25519
import "encoding/binary"
// This code is a port of the public domain, “ref10” implementation of ed25519 // This code is a port of the public domain, “ref10” implementation of ed25519
// from SUPERCOP. // from SUPERCOP.
@ -1769,3 +1771,23 @@ func ScReduce(out *[32]byte, s *[64]byte) {
out[30] = byte(s11 >> 9) out[30] = byte(s11 >> 9)
out[31] = byte(s11 >> 17) out[31] = byte(s11 >> 17)
} }
// order is the order of Curve25519 in little-endian form.
var order = [4]uint64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0, 0x1000000000000000}
// ScMinimal returns true if the given scalar is less than the order of the
// curve.
func ScMinimal(scalar *[32]byte) bool {
for i := 3; ; i-- {
v := binary.LittleEndian.Uint64(scalar[i*8:])
if v > order[i] {
return false
} else if v < order[i] {
break
} else if i == 0 {
return false
}
}
return true
}

View File

@ -10,6 +10,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"fmt"
"io" "io"
"strings" "strings"
"unicode" "unicode"
@ -89,49 +90,113 @@ func (l *lineReader) Read(p []byte) (n int, err error) {
return return
} }
line, _, err := l.in.ReadLine() line, isPrefix, err := l.in.ReadLine()
if err != nil { if err != nil {
return return
} }
// Entry-level cleanup, just trim spaces.
line = bytes.TrimFunc(line, ourIsSpace) line = bytes.TrimFunc(line, ourIsSpace)
if len(line) == 5 && line[0] == '=' { lineWithChecksum := false
// This is the checksum line foldedChecksum := false
if !isPrefix && len(line) >= 5 && line[len(line)-5] == '=' && line[len(line)-4] != '=' {
// This is the checksum line. Checksum should appear on separate line,
// but some bundles don't have a newline between main payload and the
// checksum, and we try to support that.
// `=` is not a base64 character with the exception of padding, and the
// padding can only be 2 characters long at most ("=="), so we can
// safely assume that 5 characters starting with `=` at the end of the
// line can't be a valid ending of a base64 stream. In other words, `=`
// at position len-5 in base64 stream can never be a valid part of that
// stream.
// Checksum can never appear if isPrefix is true - that is, when
// ReadLine returned non-final part of some line because it was longer
// than its buffer.
if l.crc != nil {
// Error out early if there are multiple checksums.
return 0, ArmorCorrupt
}
var expectedBytes [3]byte var expectedBytes [3]byte
var m int var m int
m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[len(line)-4:])
if m != 3 || err != nil { if err != nil {
return return 0, fmt.Errorf("error decoding CRC: %s", err.Error())
} else if m != 3 {
return 0, fmt.Errorf("error decoding CRC: wrong size CRC")
} }
crc := uint32(expectedBytes[0])<<16 | crc := uint32(expectedBytes[0])<<16 |
uint32(expectedBytes[1])<<8 | uint32(expectedBytes[1])<<8 |
uint32(expectedBytes[2]) uint32(expectedBytes[2])
l.crc = &crc l.crc = &crc
for { line = line[:len(line)-5]
line, _, err = l.in.ReadLine()
if err != nil && err != io.EOF { lineWithChecksum = true
return
// If we've found a checksum but there is still data left, we don't
// want to enter the "looking for armor end" loop, we still need to
// return the leftover data to the reader.
foldedChecksum = len(line) > 0
// At this point, `line` contains leftover data or "" (if checksum
// was on separate line.)
} }
expectArmorEnd := false
if l.crc != nil && !foldedChecksum {
// "looking for armor end" loop
// We have a checksum, and we are now reading what comes afterwards.
// Skip all empty lines until we see something and we except it to be
// ArmorEnd at this point.
// This loop is not entered if there is more data *before* the CRC
// suffix (if the CRC is not on separate line).
for {
if len(strings.TrimSpace(string(line))) > 0 { if len(strings.TrimSpace(string(line))) > 0 {
break break
} }
lineWithChecksum = false
line, _, err = l.in.ReadLine()
if err == io.EOF {
break
} }
if !bytes.HasPrefix(line, armorEnd) { if err != nil {
return 0, ArmorCorrupt return
} }
}
l.eof = true expectArmorEnd = true
return 0, io.EOF
} }
if bytes.HasPrefix(line, armorEnd) { if bytes.HasPrefix(line, armorEnd) {
// Unexpected ending, there was no checksum. if lineWithChecksum {
l.eof = true // ArmorEnd and checksum at the same line?
l.crc = nil return 0, ArmorCorrupt
return 0, io.EOF
} }
l.eof = true
return 0, io.EOF
} else if expectArmorEnd {
// We wanted armorEnd but didn't see one.
return 0, ArmorCorrupt
}
// Clean-up line from whitespace to pass it further (to base64
// decoder). This is done after test for CRC and test for
// armorEnd. Keys that have whitespace in CRC will have CRC
// treated as part of the payload and probably fail in base64
// reading.
line = bytes.Map(func(r rune) rune {
if ourIsSpace(r) {
return -1
}
return r
}, line)
n = copy(p, line) n = copy(p, line)
bytesToSave := len(line) - n bytesToSave := len(line) - n

View File

@ -280,3 +280,37 @@ func Unmarshal(curve elliptic.Curve, data []byte) (x, y *big.Int) {
return elliptic.Unmarshal(curve, data) return elliptic.Unmarshal(curve, data)
} }
func GenerateKey(curve elliptic.Curve, random io.Reader) (priv *PrivateKey, err error) {
var privBytes []byte
var Vx, Vy *big.Int
if _, ok := curve25519.ToCurve25519(curve); ok {
privBytes = make([]byte, 32)
_, err = io.ReadFull(random, privBytes)
if err != nil {
return nil, err
}
// NOTE: PGP expect scalars in reverse order than Curve 25519
// go library. That's why this trimming is backwards compared
// to curve25519.go
privBytes[31] &= 248
privBytes[0] &= 127
privBytes[0] |= 64
Vx,Vy = curve.ScalarBaseMult(privBytes)
} else {
privBytes, Vx, Vy, err = elliptic.GenerateKey(curve, random)
if err != nil {
return nil, err
}
}
priv = &PrivateKey{}
priv.X = new(big.Int).SetBytes(privBytes)
priv.PublicKey.Curve = curve
priv.PublicKey.X = Vx
priv.PublicKey.Y = Vy
return priv, nil
}

View File

@ -70,3 +70,11 @@ type UnknownPacketTypeError uint8
func (upte UnknownPacketTypeError) Error() string { func (upte UnknownPacketTypeError) Error() string {
return "openpgp: unknown packet type: " + strconv.Itoa(int(upte)) return "openpgp: unknown packet type: " + strconv.Itoa(int(upte))
} }
// DeprecatedKeyError indicates that the key was read and verified
// properly, but uses a deprecated algorithm and can't be used.
type DeprecatedKeyError string
func (d DeprecatedKeyError) Error() string {
return "openpgp: key is deprecated: " + string(d)
}

View File

@ -118,7 +118,8 @@ func (e *Entity) primaryIdentity() *Identity {
func (e *Entity) encryptionKey(now time.Time) (Key, bool) { func (e *Entity) encryptionKey(now time.Time) (Key, bool) {
candidateSubkey := -1 candidateSubkey := -1
// Iterate the keys to find the newest key // Iterate the keys to find the newest, non-revoked key that can
// encrypt.
var maxTime time.Time var maxTime time.Time
for i, subkey := range e.Subkeys { for i, subkey := range e.Subkeys {
@ -172,13 +173,18 @@ func (e *Entity) encryptionKey(now time.Time) (Key, bool) {
func (e *Entity) signingKey(now time.Time) (Key, bool) { func (e *Entity) signingKey(now time.Time) (Key, bool) {
candidateSubkey := -1 candidateSubkey := -1
// Iterate the keys to find the newest, non-revoked key that can
// sign.
var maxTime time.Time
for i, subkey := range e.Subkeys { 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.PrivateKey.PrivateKey != nil &&
subkey.PublicKey.PubKeyAlgo.CanSign() && subkey.PublicKey.PubKeyAlgo.CanSign() &&
!subkey.Sig.KeyExpired(now) &&
subkey.Revocation == nil && subkey.Revocation == nil &&
!subkey.Sig.KeyExpired(now) { (maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) {
candidateSubkey = i candidateSubkey = i
maxTime = subkey.Sig.CreationTime
break break
} }
} }
@ -504,7 +510,7 @@ EachPacket:
// Only register an identity once we've gotten a valid self-signature. // 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 // 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 // no valid self-signatures were found. That's OK as long as there are
// other identies that make sense. // other identities that make sense.
// //
// NOTE! We might later see a revocation for this very same UID, and it // 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 // won't be undone. We've preserved this feature from the original
@ -645,6 +651,15 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p
} }
} }
} }
if subKey.Sig != nil {
if err := subKey.PublicKey.ErrorIfDeprecated(); err != nil {
// Key passed signature check but is deprecated.
subKey.Sig = nil
lastErr = err
}
}
if subKey.Sig != nil { if subKey.Sig != nil {
e.Subkeys = append(e.Subkeys, subKey) e.Subkeys = append(e.Subkeys, subKey)
} else { } else {
@ -690,7 +705,7 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
} }
isPrimaryId := true isPrimaryId := true
e.Identities[uid.Id] = &Identity{ e.Identities[uid.Id] = &Identity{
Name: uid.Name, Name: uid.Id,
UserId: uid, UserId: uid,
SelfSignature: &packet.Signature{ SelfSignature: &packet.Signature{
CreationTime: currentTime, CreationTime: currentTime,
@ -705,6 +720,17 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
}, },
} }
// If the user passes in a DefaultHash via packet.Config, set the
// PreferredHash for the SelfSignature.
if config != nil && config.DefaultHash != 0 {
e.Identities[uid.Id].SelfSignature.PreferredHash = []uint8{hashToHashId(config.DefaultHash)}
}
// Likewise for DefaultCipher.
if config != nil && config.DefaultCipher != 0 {
e.Identities[uid.Id].SelfSignature.PreferredSymmetric = []uint8{uint8(config.DefaultCipher)}
}
e.Subkeys = make([]Subkey, 1) e.Subkeys = make([]Subkey, 1)
e.Subkeys[0] = Subkey{ e.Subkeys[0] = Subkey{
PublicKey: packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey), PublicKey: packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey),
@ -756,10 +782,16 @@ func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error
if err != nil { if err != nil {
return 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() { if e.PrivateKey.PrivateKey != nil && !config.ReuseSignatures() {
// If not reusing existing signatures, sign subkey using private key
// (subkey binding), but also sign primary key using subkey (primary
// key binding) if subkey is used for signing.
if subkey.Sig.FlagSign {
err = subkey.Sig.CrossSignKey(e.PrimaryKey, subkey.PrivateKey, config)
if err != nil {
return err
}
}
err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config) err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config)
if err != nil { if err != nil {
return return

View File

@ -83,6 +83,10 @@ func checksumKeyMaterial(key []byte) uint16 {
// private key must have been decrypted first. // private key must have been decrypted first.
// If config is nil, sensible defaults will be used. // If config is nil, sensible defaults will be used.
func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
if priv == nil || priv.PrivateKey == nil {
return errors.InvalidArgumentError("attempting to decrypt with nil PrivateKey")
}
var err error var err error
var b []byte var b []byte
@ -90,7 +94,8 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
// padding oracle attacks. // padding oracle attacks.
switch priv.PubKeyAlgo { switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
b, err = rsa.DecryptPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1.bytes) k := priv.PrivateKey.(*rsa.PrivateKey)
b, err = rsa.DecryptPKCS1v15(config.Random(), k, padToKeySize(&k.PublicKey, e.encryptedMPI1.bytes))
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes) c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes)
c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes) c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes)

View File

@ -17,6 +17,7 @@ import (
"github.com/keybase/go-crypto/cast5" "github.com/keybase/go-crypto/cast5"
"github.com/keybase/go-crypto/openpgp/errors" "github.com/keybase/go-crypto/openpgp/errors"
"github.com/keybase/go-crypto/rsa"
) )
// readFull is the same as io.ReadFull except that reading zero bytes returns // readFull is the same as io.ReadFull except that reading zero bytes returns
@ -415,6 +416,8 @@ const (
// RFC 6637, Section 5. // RFC 6637, Section 5.
PubKeyAlgoECDH PublicKeyAlgorithm = 18 PubKeyAlgoECDH PublicKeyAlgorithm = 18
PubKeyAlgoECDSA PublicKeyAlgorithm = 19 PubKeyAlgoECDSA PublicKeyAlgorithm = 19
PubKeyAlgoBadElGamal PublicKeyAlgorithm = 20 // Reserved (deprecated, formerly ElGamal Encrypt or Sign)
// RFC -1 // RFC -1
PubKeyAlgoEdDSA PublicKeyAlgorithm = 22 PubKeyAlgoEdDSA PublicKeyAlgorithm = 22
) )
@ -507,19 +510,17 @@ func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) {
numBytes := (int(bitLength) + 7) / 8 numBytes := (int(bitLength) + 7) / 8
mpi = make([]byte, numBytes) mpi = make([]byte, numBytes)
_, err = readFull(r, mpi) _, err = readFull(r, mpi)
return // According to RFC 4880 3.2. we should check that the MPI has no leading
} // zeroes (at least when not an encrypted MPI?), but this implementation
// does generate leading zeroes, so we keep accepting them.
// mpiLength returns the length of the given *big.Int when serialized as an
// MPI.
func mpiLength(n *big.Int) (mpiLengthInBytes int) {
mpiLengthInBytes = 2 /* MPI length */
mpiLengthInBytes += (n.BitLen() + 7) / 8
return return
} }
// writeMPI serializes a big integer to w. // writeMPI serializes a big integer to w.
func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) { func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) {
// Note that we can produce leading zeroes, in violation of RFC 4880 3.2.
// Implementations seem to be tolerant of them, and stripping them would
// make it complex to guarantee matching re-serialization.
_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)}) _, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
if err == nil { if err == nil {
_, err = w.Write(mpiBytes) _, err = w.Write(mpiBytes)
@ -551,6 +552,18 @@ func writeBig(w io.Writer, i *big.Int) error {
return writeMPI(w, uint16(i.BitLen()), i.Bytes()) return writeMPI(w, uint16(i.BitLen()), i.Bytes())
} }
// padToKeySize left-pads a MPI with zeroes to match the length of the
// specified RSA public.
func padToKeySize(pub *rsa.PublicKey, b []byte) []byte {
k := (pub.N.BitLen() + 7) / 8
if len(b) >= k {
return b
}
bb := make([]byte, k)
copy(bb[len(bb)-len(b):], b)
return bb
}
// CompressionAlgo Represents the different compression algorithms // CompressionAlgo Represents the different compression algorithms
// supported by OpenPGP (except for BZIP2, which is not currently // supported by OpenPGP (except for BZIP2, which is not currently
// supported). See Section 9.3 of RFC 4880. // supported). See Section 9.3 of RFC 4880.

View File

@ -44,6 +44,10 @@ type EdDSAPrivateKey struct {
seed parsedMPI seed parsedMPI
} }
func (e *EdDSAPrivateKey) Seed() []byte {
return e.seed.bytes
}
func (e *EdDSAPrivateKey) Sign(digest []byte) (R, S []byte, err error) { func (e *EdDSAPrivateKey) Sign(digest []byte) (R, S []byte, err error) {
r := bytes.NewReader(e.seed.bytes) r := bytes.NewReader(e.seed.bytes)
publicKey, privateKey, err := ed25519.GenerateKey(r) publicKey, privateKey, err := ed25519.GenerateKey(r)
@ -89,6 +93,13 @@ func NewECDSAPrivateKey(currentTime time.Time, priv *ecdsa.PrivateKey) *PrivateK
return pk return pk
} }
func NewECDHPrivateKey(currentTime time.Time, priv *ecdh.PrivateKey) *PrivateKey {
pk := new(PrivateKey)
pk.PublicKey = *NewECDHPublicKey(currentTime, &priv.PublicKey)
pk.PrivateKey = priv
return pk
}
func (pk *PrivateKey) parse(r io.Reader) (err error) { func (pk *PrivateKey) parse(r io.Reader) (err error) {
err = (&pk.PublicKey).parse(r) err = (&pk.PublicKey).parse(r)
if err != nil { if err != nil {
@ -415,8 +426,11 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) {
return pk.parseECDHPrivateKey(data) return pk.parseECDHPrivateKey(data)
case PubKeyAlgoEdDSA: case PubKeyAlgoEdDSA:
return pk.parseEdDSAPrivateKey(data) return pk.parseEdDSAPrivateKey(data)
case PubKeyAlgoBadElGamal:
return errors.UnsupportedError("parsing el-gamal sign-or-encrypt privatekeys is unsupported")
default:
return errors.UnsupportedError("cannot parse this private key type")
} }
panic("impossible")
} }
func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) { func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) {

View File

@ -27,10 +27,13 @@ import (
"github.com/keybase/go-crypto/openpgp/ecdh" "github.com/keybase/go-crypto/openpgp/ecdh"
"github.com/keybase/go-crypto/openpgp/elgamal" "github.com/keybase/go-crypto/openpgp/elgamal"
"github.com/keybase/go-crypto/openpgp/errors" "github.com/keybase/go-crypto/openpgp/errors"
"github.com/keybase/go-crypto/openpgp/s2k"
"github.com/keybase/go-crypto/rsa" "github.com/keybase/go-crypto/rsa"
) )
var ( var (
// NIST curve P-224
oidCurveP224 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x21}
// NIST curve P-256 // NIST curve P-256
oidCurveP256 []byte = []byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07} oidCurveP256 []byte = []byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}
// NIST curve P-384 // NIST curve P-384
@ -128,6 +131,8 @@ func (f *ecdsaKey) serialize(w io.Writer) (err error) {
func getCurveByOid(oid []byte) elliptic.Curve { func getCurveByOid(oid []byte) elliptic.Curve {
switch { switch {
case bytes.Equal(oid, oidCurveP224):
return elliptic.P224()
case bytes.Equal(oid, oidCurveP256): case bytes.Equal(oid, oidCurveP256):
return elliptic.P256() return elliptic.P256()
case bytes.Equal(oid, oidCurveP384): case bytes.Equal(oid, oidCurveP384):
@ -324,6 +329,30 @@ func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *Public
return pk return pk
} }
func getCurveOid(curve elliptic.Curve) (res []byte, err error) {
switch curve {
case elliptic.P224():
res = oidCurveP224
case elliptic.P256():
res = oidCurveP256
case elliptic.P384():
res = oidCurveP384
case elliptic.P521():
res = oidCurveP521
case brainpool.P256r1():
res = oidCurveP256r1
case brainpool.P384r1():
res = oidCurveP384r1
case brainpool.P512r1():
res = oidCurveP512r1
case curve25519.Cv25519():
res = oidCurve25519
default:
err = errors.UnsupportedError("unknown curve")
}
return
}
func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey { func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey {
pk := &PublicKey{ pk := &PublicKey{
CreationTime: creationTime, CreationTime: creationTime,
@ -331,22 +360,34 @@ func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey
PublicKey: pub, PublicKey: pub,
ec: new(ecdsaKey), ec: new(ecdsaKey),
} }
switch pub.Curve { oid, _ := getCurveOid(pub.Curve)
case elliptic.P256(): pk.ec.oid = oid
pk.ec.oid = oidCurveP256 bs, bitLen := ecdh.Marshal(pub.Curve, pub.X, pub.Y)
case elliptic.P384(): pk.ec.p.bytes = bs
pk.ec.oid = oidCurveP384 pk.ec.p.bitLength = uint16(bitLen)
case elliptic.P521():
pk.ec.oid = oidCurveP521 pk.setFingerPrintAndKeyId()
case brainpool.P256r1(): return pk
pk.ec.oid = oidCurveP256r1 }
case brainpool.P384r1():
pk.ec.oid = oidCurveP384r1 func NewECDHPublicKey(creationTime time.Time, pub *ecdh.PublicKey) *PublicKey {
case brainpool.P512r1(): pk := &PublicKey{
pk.ec.oid = oidCurveP512r1 CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoECDH,
PublicKey: pub,
ec: new(ecdsaKey),
}
oid, _ := getCurveOid(pub.Curve)
pk.ec.oid = oid
bs, bitLen := ecdh.Marshal(pub.Curve, pub.X, pub.Y)
pk.ec.p.bytes = bs
pk.ec.p.bitLength = uint16(bitLen)
hashbyte, _ := s2k.HashToHashId(crypto.SHA512)
pk.ecdh = &ecdhKdf{
KdfHash: kdfHashFunction(hashbyte),
KdfAlgo: kdfAlgorithm(CipherAES256),
} }
pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
pk.ec.p.bitLength = uint16(8 * len(pk.ec.p.bytes))
pk.setFingerPrintAndKeyId() pk.setFingerPrintAndKeyId()
return pk return pk
@ -377,6 +418,9 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
return err return err
} }
err = pk.edk.check() err = pk.edk.check()
if err == nil {
pk.PublicKey = ed25519.PublicKey(pk.edk.p.bytes[1:])
}
case PubKeyAlgoECDSA: case PubKeyAlgoECDSA:
pk.ec = new(ecdsaKey) pk.ec = new(ecdsaKey)
if err = pk.ec.parse(r); err != nil { if err = pk.ec.parse(r); err != nil {
@ -393,6 +437,14 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
return return
} }
pk.PublicKey, err = pk.ec.newECDH() pk.PublicKey, err = pk.ec.newECDH()
case PubKeyAlgoBadElGamal:
// Key has ElGamal format but nil-implementation - it will
// load but it's not possible to do any operations using this
// key.
err = pk.parseElGamal(r)
if err != nil {
pk.PublicKey = nil
}
default: default:
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo)))
} }
@ -433,6 +485,8 @@ func (pk *PublicKey) parseRSA(r io.Reader) (err error) {
N: new(big.Int).SetBytes(pk.n.bytes), N: new(big.Int).SetBytes(pk.n.bytes),
E: 0, E: 0,
} }
// Warning: incompatibility with crypto/rsa: keybase fork uses
// int64 public exponents instead of int32.
for i := 0; i < len(pk.e.bytes); i++ { for i := 0; i < len(pk.e.bytes); i++ {
rsa.E <<= 8 rsa.E <<= 8
rsa.E |= int64(pk.e.bytes[i]) rsa.E |= int64(pk.e.bytes[i])
@ -508,7 +562,7 @@ func (pk *PublicKey) SerializeSignaturePrefix(h io.Writer) {
pLength += 2 + uint16(len(pk.q.bytes)) pLength += 2 + uint16(len(pk.q.bytes))
pLength += 2 + uint16(len(pk.g.bytes)) pLength += 2 + uint16(len(pk.g.bytes))
pLength += 2 + uint16(len(pk.y.bytes)) pLength += 2 + uint16(len(pk.y.bytes))
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal, PubKeyAlgoBadElGamal:
pLength += 2 + uint16(len(pk.p.bytes)) pLength += 2 + uint16(len(pk.p.bytes))
pLength += 2 + uint16(len(pk.g.bytes)) pLength += 2 + uint16(len(pk.g.bytes))
pLength += 2 + uint16(len(pk.y.bytes)) pLength += 2 + uint16(len(pk.y.bytes))
@ -539,7 +593,7 @@ func (pk *PublicKey) Serialize(w io.Writer) (err error) {
length += 2 + len(pk.q.bytes) length += 2 + len(pk.q.bytes)
length += 2 + len(pk.g.bytes) length += 2 + len(pk.g.bytes)
length += 2 + len(pk.y.bytes) length += 2 + len(pk.y.bytes)
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal, PubKeyAlgoBadElGamal:
length += 2 + len(pk.p.bytes) length += 2 + len(pk.p.bytes)
length += 2 + len(pk.g.bytes) length += 2 + len(pk.g.bytes)
length += 2 + len(pk.y.bytes) length += 2 + len(pk.y.bytes)
@ -587,7 +641,7 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
return writeMPIs(w, pk.n, pk.e) return writeMPIs(w, pk.n, pk.e)
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) return writeMPIs(w, pk.p, pk.q, pk.g, pk.y)
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal, PubKeyAlgoBadElGamal:
return writeMPIs(w, pk.p, pk.g, pk.y) return writeMPIs(w, pk.p, pk.g, pk.y)
case PubKeyAlgoECDSA: case PubKeyAlgoECDSA:
return pk.ec.serialize(w) return pk.ec.serialize(w)
@ -637,7 +691,7 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
switch pk.PubKeyAlgo { switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes) err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes))
if err != nil { if err != nil {
return errors.SignatureError("RSA verification failure") return errors.SignatureError("RSA verification failure")
} }
@ -694,7 +748,7 @@ func (pk *PublicKey) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err
switch pk.PubKeyAlgo { switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
rsaPublicKey := pk.PublicKey.(*rsa.PublicKey) rsaPublicKey := pk.PublicKey.(*rsa.PublicKey)
if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil { if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes)); err != nil {
return errors.SignatureError("RSA verification failure") return errors.SignatureError("RSA verification failure")
} }
return return
@ -910,7 +964,7 @@ func (pk *PublicKey) BitLength() (bitLength uint16, err error) {
bitLength = pk.n.bitLength bitLength = pk.n.bitLength
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
bitLength = pk.p.bitLength bitLength = pk.p.bitLength
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal, PubKeyAlgoBadElGamal:
bitLength = pk.p.bitLength bitLength = pk.p.bitLength
case PubKeyAlgoECDH: case PubKeyAlgoECDH:
ecdhPublicKey := pk.PublicKey.(*ecdh.PublicKey) ecdhPublicKey := pk.PublicKey.(*ecdh.PublicKey)
@ -928,3 +982,12 @@ func (pk *PublicKey) BitLength() (bitLength uint16, err error) {
} }
return return
} }
func (pk *PublicKey) ErrorIfDeprecated() error {
switch pk.PubKeyAlgo {
case PubKeyAlgoBadElGamal:
return errors.DeprecatedKeyError("ElGamal Encrypt or Sign (algo 20) is deprecated")
default:
return nil
}
}

View File

@ -105,6 +105,8 @@ func (pk *PublicKeyV3) parseRSA(r io.Reader) (err error) {
return return
} }
rsa := &rsa.PublicKey{N: new(big.Int).SetBytes(pk.n.bytes)} rsa := &rsa.PublicKey{N: new(big.Int).SetBytes(pk.n.bytes)}
// Warning: incompatibility with crypto/rsa: keybase fork uses
// int64 public exponents instead of int32.
for i := 0; i < len(pk.e.bytes); i++ { for i := 0; i < len(pk.e.bytes); i++ {
rsa.E <<= 8 rsa.E <<= 8
rsa.E |= int64(pk.e.bytes[i]) rsa.E |= int64(pk.e.bytes[i])

View File

@ -10,6 +10,7 @@ import (
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"encoding/binary" "encoding/binary"
"fmt"
"hash" "hash"
"io" "io"
"strconv" "strconv"
@ -384,6 +385,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
err = errors.StructuralError("empty key flags subpacket") err = errors.StructuralError("empty key flags subpacket")
return return
} }
if subpacket[0] != 0 {
sig.FlagsValid = true sig.FlagsValid = true
if subpacket[0]&KeyFlagCertify != 0 { if subpacket[0]&KeyFlagCertify != 0 {
sig.FlagCertify = true sig.FlagCertify = true
@ -397,6 +399,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
if subpacket[0]&KeyFlagEncryptStorage != 0 { if subpacket[0]&KeyFlagEncryptStorage != 0 {
sig.FlagEncryptStorage = true sig.FlagEncryptStorage = true
} }
}
case reasonForRevocationSubpacket: case reasonForRevocationSubpacket:
// Reason For Revocation, section 5.2.3.23 // Reason For Revocation, section 5.2.3.23
if !isHashed { if !isHashed {
@ -624,6 +627,13 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
return return
} }
// Parameter check, if this is wrong we will make a signature but
// not serialize it later.
if sig.PubKeyAlgo != priv.PubKeyAlgo {
err = errors.InvalidArgumentError("signature pub key algo does not match priv key")
return
}
switch priv.PubKeyAlgo { switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
sig.RSASignature.bytes, err = rsa.SignPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) sig.RSASignature.bytes, err = rsa.SignPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest)
@ -637,26 +647,29 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
digest = digest[:subgroupSize] digest = digest[:subgroupSize]
} }
r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) r, s, err := dsa.Sign(config.Random(), dsaPriv, digest)
if err == nil { if err != nil {
return err
}
sig.DSASigR.bytes = r.Bytes() sig.DSASigR.bytes = r.Bytes()
sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes))
sig.DSASigS.bytes = s.Bytes() sig.DSASigS.bytes = s.Bytes()
sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes)) sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes))
}
case PubKeyAlgoECDSA: case PubKeyAlgoECDSA:
r, s, err := ecdsa.Sign(config.Random(), priv.PrivateKey.(*ecdsa.PrivateKey), digest) r, s, err := ecdsa.Sign(config.Random(), priv.PrivateKey.(*ecdsa.PrivateKey), digest)
if err == nil { if err != nil {
return err
}
sig.ECDSASigR = FromBig(r) sig.ECDSASigR = FromBig(r)
sig.ECDSASigS = FromBig(s) sig.ECDSASigS = FromBig(s)
}
case PubKeyAlgoEdDSA: case PubKeyAlgoEdDSA:
r, s, err := priv.PrivateKey.(*EdDSAPrivateKey).Sign(digest) r, s, err := priv.PrivateKey.(*EdDSAPrivateKey).Sign(digest)
if err == nil { if err != nil {
return err
}
sig.EdDSASigR = FromBytes(r) sig.EdDSASigR = FromBytes(r)
sig.EdDSASigS = FromBytes(s) sig.EdDSASigS = FromBytes(s)
}
default: default:
err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) err = errors.UnsupportedError("public key algorithm for signing: " + strconv.Itoa(int(priv.PubKeyAlgo)))
} }
return return
@ -704,6 +717,28 @@ func (sig *Signature) SignKeyWithSigner(signeePubKey *PublicKey, signerPubKey *P
return sig.Sign(s, nil, config) return sig.Sign(s, nil, config)
} }
// CrossSignKey creates PrimaryKeyBinding signature in sig.EmbeddedSignature by
// signing `primary` key's hash using `priv` subkey private key. Primary public
// key is the `signee` here.
func (sig *Signature) CrossSignKey(primary *PublicKey, priv *PrivateKey, config *Config) error {
if len(sig.outSubpackets) > 0 {
return fmt.Errorf("outSubpackets already exists, looks like CrossSignKey was called after Sign")
}
sig.EmbeddedSignature = &Signature{
CreationTime: sig.CreationTime,
SigType: SigTypePrimaryKeyBinding,
PubKeyAlgo: priv.PubKeyAlgo,
Hash: sig.Hash,
}
h, err := keySignatureHash(primary, &priv.PublicKey, sig.Hash)
if err != nil {
return err
}
return sig.EmbeddedSignature.Sign(h, priv, config)
}
// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been // Serialize marshals sig to w. Sign, SignUserId or SignKey must have been
// called first. // called first.
func (sig *Signature) Serialize(w io.Writer) (err error) { func (sig *Signature) Serialize(w io.Writer) (err error) {
@ -832,6 +867,14 @@ func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) {
subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression})
} }
if sig.EmbeddedSignature != nil {
buf := bytes.NewBuffer(nil)
if err := sig.EmbeddedSignature.Serialize(buf); err == nil {
byteContent := buf.Bytes()[2:] // skip 2-byte length header
subpackets = append(subpackets, outputSubpacket{false, embeddedSignatureSubpacket, true, byteContent})
}
}
return return
} }

View File

@ -91,10 +91,10 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunc
return nil, ske.CipherFunc, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) return nil, ske.CipherFunc, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
} }
plaintextKey = plaintextKey[1:] plaintextKey = plaintextKey[1:]
if l := len(plaintextKey); l == 0 || l%cipherFunc.blockSize() != 0 { if l, cipherKeySize := len(plaintextKey), cipherFunc.KeySize(); l != cipherFunc.KeySize() {
return nil, cipherFunc, errors.StructuralError("length of decrypted key not a multiple of block size") return nil, cipherFunc, errors.StructuralError("length of decrypted key (" + strconv.Itoa(l) + ") " +
"not equal to cipher keysize (" + strconv.Itoa(cipherKeySize) + ")")
} }
return plaintextKey, cipherFunc, nil return plaintextKey, cipherFunc, nil
} }

View File

@ -61,6 +61,9 @@ type MessageDetails struct {
Signature *packet.Signature // the signature packet itself, if v4 (default) Signature *packet.Signature // the signature packet itself, if v4 (default)
SignatureV3 *packet.SignatureV3 // the signature packet if it is a v2 or v3 signature SignatureV3 *packet.SignatureV3 // the signature packet if it is a v2 or v3 signature
// Does the Message include multiple signatures? Also called "nested signatures".
MultiSig bool
decrypted io.ReadCloser decrypted io.ReadCloser
} }
@ -158,8 +161,15 @@ FindKey:
continue continue
} }
if !pk.key.PrivateKey.Encrypted { if !pk.key.PrivateKey.Encrypted {
if pk.key.PrivateKey.PrivateKey == nil {
// Key is stubbed
continue
}
if len(pk.encryptedKey.Key) == 0 { if len(pk.encryptedKey.Key) == 0 {
pk.encryptedKey.Decrypt(pk.key.PrivateKey, config) err := pk.encryptedKey.Decrypt(pk.key.PrivateKey, config)
if err != nil {
continue
}
} }
if len(pk.encryptedKey.Key) == 0 { if len(pk.encryptedKey.Key) == 0 {
continue continue
@ -244,8 +254,17 @@ FindLiteralData:
return nil, err return nil, err
} }
case *packet.OnePassSignature: case *packet.OnePassSignature:
if !p.IsLast { if md.IsSigned {
return nil, errors.UnsupportedError("nested signatures") // If IsSigned is set, it means we have multiple
// OnePassSignature packets.
md.MultiSig = true
if md.SignedBy != nil {
// We've already found the signature we were looking
// for, made by key that we had in keyring and can
// check signature against. Continue with that instead
// of trying to find another.
continue FindLiteralData
}
} }
h, wrappedHash, err = hashForSignature(p.Hash, p.SigType) h, wrappedHash, err = hashForSignature(p.Hash, p.SigType)
@ -329,17 +348,36 @@ func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) {
n, err = scr.md.LiteralData.Body.Read(buf) n, err = scr.md.LiteralData.Body.Read(buf)
scr.wrappedHash.Write(buf[:n]) scr.wrappedHash.Write(buf[:n])
if err == io.EOF { if err == io.EOF {
for {
var p packet.Packet var p packet.Packet
p, scr.md.SignatureError = scr.packets.Next() p, scr.md.SignatureError = scr.packets.Next()
if scr.md.SignatureError != nil { if scr.md.SignatureError != nil {
if scr.md.MultiSig {
// If we are in MultiSig, we might have found other
// signature that cannot be verified using our key.
// Clear Signature field so it's clear for consumers
// that this message failed to verify.
scr.md.Signature = nil
}
return return
} }
var ok bool var ok bool
if scr.md.Signature, ok = p.(*packet.Signature); ok { if scr.md.Signature, ok = p.(*packet.Signature); ok {
var err error var err error
if keyID := scr.md.Signature.IssuerKeyId; keyID != nil {
if *keyID != scr.md.SignedBy.PublicKey.KeyId {
if scr.md.MultiSig {
continue // try again to find a sig we can verify
}
err = errors.StructuralError("bad key id")
}
}
if fingerprint := scr.md.Signature.IssuerFingerprint; fingerprint != nil { if fingerprint := scr.md.Signature.IssuerFingerprint; fingerprint != nil {
if !hmac.Equal(fingerprint, scr.md.SignedBy.PublicKey.Fingerprint[:]) { if !hmac.Equal(fingerprint, scr.md.SignedBy.PublicKey.Fingerprint[:]) {
if scr.md.MultiSig {
continue // try again to find a sig we can verify
}
err = errors.StructuralError("bad key fingerprint") err = errors.StructuralError("bad key fingerprint")
} }
} }
@ -354,6 +392,12 @@ func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) {
return return
} }
// Parse only one packet by default, unless message is MultiSig. Then
// we ask for more packets after discovering non-matching signature,
// until we find one that we can verify.
break
}
// The SymmetricallyEncrypted packet, if any, might have an // The SymmetricallyEncrypted packet, if any, might have an
// unsigned hash of its own. In order to check this we need to // unsigned hash of its own. In order to check this we need to
// close that Reader. // close that Reader.

View File

@ -458,7 +458,18 @@ func AttachedSign(out io.WriteCloser, signed Entity, hints *FileHints,
return return
} }
hasher := crypto.SHA512 if algo := config.Compression(); algo != packet.CompressionNone {
var compConfig *packet.CompressionConfig
if config != nil {
compConfig = config.CompressionConfig
}
out, err = packet.SerializeCompressed(out, algo, compConfig)
if err != nil {
return
}
}
hasher := config.Hash() // defaults to SHA-256
ops := &packet.OnePassSignature{ ops := &packet.OnePassSignature{
SigType: packet.SigTypeBinary, SigType: packet.SigTypeBinary,

2
vendor/modules.txt vendored
View File

@ -253,7 +253,7 @@ github.com/jessevdk/go-flags
github.com/kballard/go-shellquote github.com/kballard/go-shellquote
# github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd # github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd
github.com/kevinburke/ssh_config github.com/kevinburke/ssh_config
# github.com/keybase/go-crypto v0.0.0-20170605145657-00ac4db533f6 # github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
github.com/keybase/go-crypto/brainpool github.com/keybase/go-crypto/brainpool
github.com/keybase/go-crypto/cast5 github.com/keybase/go-crypto/cast5
github.com/keybase/go-crypto/curve25519 github.com/keybase/go-crypto/curve25519