1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-19 12:12:42 +02:00

Bump crypto and net packages

The old versions had some CVEs which I'm almost certain were not
relevant to lazygit but this means I get to close a couple PRs easily.
This commit is contained in:
Jesse Duffield 2025-01-03 14:01:26 +11:00
parent 4c4a2c3ff4
commit 3241a9c251
82 changed files with 1456 additions and 1957 deletions

4
go.mod
View File

@ -73,8 +73,8 @@ require (
github.com/sergi/go-diff v1.1.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/xanzy/ssh-agent v0.2.1 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.7.0 // indirect golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.28.0 // indirect golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect golang.org/x/text v0.21.0 // indirect

7
go.sum
View File

@ -327,8 +327,9 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -401,8 +402,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

4
vendor/golang.org/x/crypto/LICENSE generated vendored
View File

@ -1,4 +1,4 @@
Copyright (c) 2009 The Go Authors. All rights reserved. Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are
@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the in the documentation and/or other materials provided with the
distribution. distribution.
* Neither the name of Google Inc. nor the names of its * Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from contributors may be used to endorse or promote products derived from
this software without specific prior written permission. this software without specific prior written permission.

View File

@ -11,7 +11,7 @@
// Deprecated: any new system should use AES (from crypto/aes, if necessary in // Deprecated: any new system should use AES (from crypto/aes, if necessary in
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from // an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
// golang.org/x/crypto/chacha20poly1305). // golang.org/x/crypto/chacha20poly1305).
package blowfish // import "golang.org/x/crypto/blowfish" package blowfish
// The code is a port of Bruce Schneier's C implementation. // The code is a port of Bruce Schneier's C implementation.
// See https://www.schneier.com/blowfish.html. // See https://www.schneier.com/blowfish.html.

View File

@ -11,9 +11,12 @@
// Deprecated: any new system should use AES (from crypto/aes, if necessary in // Deprecated: any new system should use AES (from crypto/aes, if necessary in
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from // an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
// golang.org/x/crypto/chacha20poly1305). // golang.org/x/crypto/chacha20poly1305).
package cast5 // import "golang.org/x/crypto/cast5" package cast5
import "errors" import (
"errors"
"math/bits"
)
const BlockSize = 8 const BlockSize = 8
const KeySize = 16 const KeySize = 16
@ -241,19 +244,19 @@ func (c *Cipher) keySchedule(in []byte) {
// These are the three 'f' functions. See RFC 2144, section 2.2. // These are the three 'f' functions. See RFC 2144, section 2.2.
func f1(d, m uint32, r uint8) uint32 { func f1(d, m uint32, r uint8) uint32 {
t := m + d t := m + d
I := (t << r) | (t >> (32 - r)) I := bits.RotateLeft32(t, int(r))
return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff] return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff]
} }
func f2(d, m uint32, r uint8) uint32 { func f2(d, m uint32, r uint8) uint32 {
t := m ^ d t := m ^ d
I := (t << r) | (t >> (32 - r)) I := bits.RotateLeft32(t, int(r))
return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff] return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff]
} }
func f3(d, m uint32, r uint8) uint32 { func f3(d, m uint32, r uint8) uint32 {
t := m - d t := m - d
I := (t << r) | (t >> (32 - r)) I := bits.RotateLeft32(t, int(r))
return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff] return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff]
} }

View File

@ -2,8 +2,7 @@
// 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.
//go:build go1.11 && gc && !purego //go:build gc && !purego
// +build go1.11,gc,!purego
package chacha20 package chacha20

View File

@ -2,8 +2,7 @@
// 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.
//go:build go1.11 && gc && !purego //go:build gc && !purego
// +build go1.11,gc,!purego
#include "textflag.h" #include "textflag.h"

View File

@ -12,7 +12,7 @@ import (
"errors" "errors"
"math/bits" "math/bits"
"golang.org/x/crypto/internal/subtle" "golang.org/x/crypto/internal/alias"
) )
const ( const (
@ -189,7 +189,7 @@ func (s *Cipher) XORKeyStream(dst, src []byte) {
panic("chacha20: output smaller than input") panic("chacha20: output smaller than input")
} }
dst = dst[:len(src)] dst = dst[:len(src)]
if subtle.InexactOverlap(dst, src) { if alias.InexactOverlap(dst, src) {
panic("chacha20: invalid buffer overlap") panic("chacha20: invalid buffer overlap")
} }

View File

@ -2,8 +2,7 @@
// 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.
//go:build (!arm64 && !s390x && !ppc64le) || (arm64 && !go1.11) || !gc || purego //go:build (!arm64 && !s390x && !ppc64 && !ppc64le) || !gc || purego
// +build !arm64,!s390x,!ppc64le arm64,!go1.11 !gc purego
package chacha20 package chacha20

View File

@ -2,8 +2,7 @@
// 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.
//go:build gc && !purego //go:build gc && !purego && (ppc64 || ppc64le)
// +build gc,!purego
package chacha20 package chacha20

View File

@ -19,8 +19,7 @@
// The differences in this and the original implementation are // The differences in this and the original implementation are
// due to the calling conventions and initialization of constants. // due to the calling conventions and initialization of constants.
//go:build gc && !purego //go:build gc && !purego && (ppc64 || ppc64le)
// +build gc,!purego
#include "textflag.h" #include "textflag.h"
@ -34,27 +33,70 @@
#define CONSTBASE R16 #define CONSTBASE R16
#define BLOCKS R17 #define BLOCKS R17
DATA consts<>+0x00(SB)/8, $0x3320646e61707865 // for VPERMXOR
DATA consts<>+0x08(SB)/8, $0x6b20657479622d32 #define MASK R18
DATA consts<>+0x10(SB)/8, $0x0000000000000001
DATA consts<>+0x18(SB)/8, $0x0000000000000000 DATA consts<>+0x00(SB)/4, $0x61707865
DATA consts<>+0x20(SB)/8, $0x0000000000000004 DATA consts<>+0x04(SB)/4, $0x3320646e
DATA consts<>+0x28(SB)/8, $0x0000000000000000 DATA consts<>+0x08(SB)/4, $0x79622d32
DATA consts<>+0x30(SB)/8, $0x0a0b08090e0f0c0d DATA consts<>+0x0c(SB)/4, $0x6b206574
DATA consts<>+0x38(SB)/8, $0x0203000106070405 DATA consts<>+0x10(SB)/4, $0x00000001
DATA consts<>+0x40(SB)/8, $0x090a0b080d0e0f0c DATA consts<>+0x14(SB)/4, $0x00000000
DATA consts<>+0x48(SB)/8, $0x0102030005060704 DATA consts<>+0x18(SB)/4, $0x00000000
DATA consts<>+0x50(SB)/8, $0x6170786561707865 DATA consts<>+0x1c(SB)/4, $0x00000000
DATA consts<>+0x58(SB)/8, $0x6170786561707865 DATA consts<>+0x20(SB)/4, $0x00000004
DATA consts<>+0x60(SB)/8, $0x3320646e3320646e DATA consts<>+0x24(SB)/4, $0x00000000
DATA consts<>+0x68(SB)/8, $0x3320646e3320646e DATA consts<>+0x28(SB)/4, $0x00000000
DATA consts<>+0x70(SB)/8, $0x79622d3279622d32 DATA consts<>+0x2c(SB)/4, $0x00000000
DATA consts<>+0x78(SB)/8, $0x79622d3279622d32 DATA consts<>+0x30(SB)/4, $0x0e0f0c0d
DATA consts<>+0x80(SB)/8, $0x6b2065746b206574 DATA consts<>+0x34(SB)/4, $0x0a0b0809
DATA consts<>+0x88(SB)/8, $0x6b2065746b206574 DATA consts<>+0x38(SB)/4, $0x06070405
DATA consts<>+0x90(SB)/8, $0x0000000100000000 DATA consts<>+0x3c(SB)/4, $0x02030001
DATA consts<>+0x98(SB)/8, $0x0000000300000002 DATA consts<>+0x40(SB)/4, $0x0d0e0f0c
GLOBL consts<>(SB), RODATA, $0xa0 DATA consts<>+0x44(SB)/4, $0x090a0b08
DATA consts<>+0x48(SB)/4, $0x05060704
DATA consts<>+0x4c(SB)/4, $0x01020300
DATA consts<>+0x50(SB)/4, $0x61707865
DATA consts<>+0x54(SB)/4, $0x61707865
DATA consts<>+0x58(SB)/4, $0x61707865
DATA consts<>+0x5c(SB)/4, $0x61707865
DATA consts<>+0x60(SB)/4, $0x3320646e
DATA consts<>+0x64(SB)/4, $0x3320646e
DATA consts<>+0x68(SB)/4, $0x3320646e
DATA consts<>+0x6c(SB)/4, $0x3320646e
DATA consts<>+0x70(SB)/4, $0x79622d32
DATA consts<>+0x74(SB)/4, $0x79622d32
DATA consts<>+0x78(SB)/4, $0x79622d32
DATA consts<>+0x7c(SB)/4, $0x79622d32
DATA consts<>+0x80(SB)/4, $0x6b206574
DATA consts<>+0x84(SB)/4, $0x6b206574
DATA consts<>+0x88(SB)/4, $0x6b206574
DATA consts<>+0x8c(SB)/4, $0x6b206574
DATA consts<>+0x90(SB)/4, $0x00000000
DATA consts<>+0x94(SB)/4, $0x00000001
DATA consts<>+0x98(SB)/4, $0x00000002
DATA consts<>+0x9c(SB)/4, $0x00000003
DATA consts<>+0xa0(SB)/4, $0x11223300
DATA consts<>+0xa4(SB)/4, $0x55667744
DATA consts<>+0xa8(SB)/4, $0x99aabb88
DATA consts<>+0xac(SB)/4, $0xddeeffcc
DATA consts<>+0xb0(SB)/4, $0x22330011
DATA consts<>+0xb4(SB)/4, $0x66774455
DATA consts<>+0xb8(SB)/4, $0xaabb8899
DATA consts<>+0xbc(SB)/4, $0xeeffccdd
GLOBL consts<>(SB), RODATA, $0xc0
#ifdef GOARCH_ppc64
#define BE_XXBRW_INIT() \
LVSL (R0)(R0), V24 \
VSPLTISB $3, V25 \
VXOR V24, V25, V24 \
#define BE_XXBRW(vr) VPERM vr, vr, V24, vr
#else
#define BE_XXBRW_INIT()
#define BE_XXBRW(vr)
#endif
//func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32) //func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32)
TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40 TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
@ -71,6 +113,9 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
MOVD $48, R10 MOVD $48, R10
MOVD $64, R11 MOVD $64, R11
SRD $6, LEN, BLOCKS SRD $6, LEN, BLOCKS
// for VPERMXOR
MOVD $consts<>+0xa0(SB), MASK
MOVD $16, R20
// V16 // V16
LXVW4X (CONSTBASE)(R0), VS48 LXVW4X (CONSTBASE)(R0), VS48
ADD $80,CONSTBASE ADD $80,CONSTBASE
@ -85,9 +130,15 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
// Clear V27 // Clear V27
VXOR V27, V27, V27 VXOR V27, V27, V27
BE_XXBRW_INIT()
// V28 // V28
LXVW4X (CONSTBASE)(R11), VS60 LXVW4X (CONSTBASE)(R11), VS60
// Load mask constants for VPERMXOR
LXVW4X (MASK)(R0), V20
LXVW4X (MASK)(R20), V21
// splat slot from V19 -> V26 // splat slot from V19 -> V26
VSPLTW $0, V19, V26 VSPLTW $0, V19, V26
@ -98,7 +149,7 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
MOVD $10, R14 MOVD $10, R14
MOVD R14, CTR MOVD R14, CTR
PCALIGN $16
loop_outer_vsx: loop_outer_vsx:
// V0, V1, V2, V3 // V0, V1, V2, V3
LXVW4X (R0)(CONSTBASE), VS32 LXVW4X (R0)(CONSTBASE), VS32
@ -129,22 +180,17 @@ loop_outer_vsx:
VSPLTISW $12, V28 VSPLTISW $12, V28
VSPLTISW $8, V29 VSPLTISW $8, V29
VSPLTISW $7, V30 VSPLTISW $7, V30
PCALIGN $16
loop_vsx: loop_vsx:
VADDUWM V0, V4, V0 VADDUWM V0, V4, V0
VADDUWM V1, V5, V1 VADDUWM V1, V5, V1
VADDUWM V2, V6, V2 VADDUWM V2, V6, V2
VADDUWM V3, V7, V3 VADDUWM V3, V7, V3
VXOR V12, V0, V12 VPERMXOR V12, V0, V21, V12
VXOR V13, V1, V13 VPERMXOR V13, V1, V21, V13
VXOR V14, V2, V14 VPERMXOR V14, V2, V21, V14
VXOR V15, V3, V15 VPERMXOR V15, V3, V21, V15
VRLW V12, V27, V12
VRLW V13, V27, V13
VRLW V14, V27, V14
VRLW V15, V27, V15
VADDUWM V8, V12, V8 VADDUWM V8, V12, V8
VADDUWM V9, V13, V9 VADDUWM V9, V13, V9
@ -166,15 +212,10 @@ loop_vsx:
VADDUWM V2, V6, V2 VADDUWM V2, V6, V2
VADDUWM V3, V7, V3 VADDUWM V3, V7, V3
VXOR V12, V0, V12 VPERMXOR V12, V0, V20, V12
VXOR V13, V1, V13 VPERMXOR V13, V1, V20, V13
VXOR V14, V2, V14 VPERMXOR V14, V2, V20, V14
VXOR V15, V3, V15 VPERMXOR V15, V3, V20, V15
VRLW V12, V29, V12
VRLW V13, V29, V13
VRLW V14, V29, V14
VRLW V15, V29, V15
VADDUWM V8, V12, V8 VADDUWM V8, V12, V8
VADDUWM V9, V13, V9 VADDUWM V9, V13, V9
@ -196,15 +237,10 @@ loop_vsx:
VADDUWM V2, V7, V2 VADDUWM V2, V7, V2
VADDUWM V3, V4, V3 VADDUWM V3, V4, V3
VXOR V15, V0, V15 VPERMXOR V15, V0, V21, V15
VXOR V12, V1, V12 VPERMXOR V12, V1, V21, V12
VXOR V13, V2, V13 VPERMXOR V13, V2, V21, V13
VXOR V14, V3, V14 VPERMXOR V14, V3, V21, V14
VRLW V15, V27, V15
VRLW V12, V27, V12
VRLW V13, V27, V13
VRLW V14, V27, V14
VADDUWM V10, V15, V10 VADDUWM V10, V15, V10
VADDUWM V11, V12, V11 VADDUWM V11, V12, V11
@ -226,15 +262,10 @@ loop_vsx:
VADDUWM V2, V7, V2 VADDUWM V2, V7, V2
VADDUWM V3, V4, V3 VADDUWM V3, V4, V3
VXOR V15, V0, V15 VPERMXOR V15, V0, V20, V15
VXOR V12, V1, V12 VPERMXOR V12, V1, V20, V12
VXOR V13, V2, V13 VPERMXOR V13, V2, V20, V13
VXOR V14, V3, V14 VPERMXOR V14, V3, V20, V14
VRLW V15, V29, V15
VRLW V12, V29, V12
VRLW V13, V29, V13
VRLW V14, V29, V14
VADDUWM V10, V15, V10 VADDUWM V10, V15, V10
VADDUWM V11, V12, V11 VADDUWM V11, V12, V11
@ -250,48 +281,48 @@ loop_vsx:
VRLW V6, V30, V6 VRLW V6, V30, V6
VRLW V7, V30, V7 VRLW V7, V30, V7
VRLW V4, V30, V4 VRLW V4, V30, V4
BC 16, LT, loop_vsx BDNZ loop_vsx
VADDUWM V12, V26, V12 VADDUWM V12, V26, V12
WORD $0x13600F8C // VMRGEW V0, V1, V27 VMRGEW V0, V1, V27
WORD $0x13821F8C // VMRGEW V2, V3, V28 VMRGEW V2, V3, V28
WORD $0x10000E8C // VMRGOW V0, V1, V0 VMRGOW V0, V1, V0
WORD $0x10421E8C // VMRGOW V2, V3, V2 VMRGOW V2, V3, V2
WORD $0x13A42F8C // VMRGEW V4, V5, V29 VMRGEW V4, V5, V29
WORD $0x13C63F8C // VMRGEW V6, V7, V30 VMRGEW V6, V7, V30
XXPERMDI VS32, VS34, $0, VS33 XXPERMDI VS32, VS34, $0, VS33
XXPERMDI VS32, VS34, $3, VS35 XXPERMDI VS32, VS34, $3, VS35
XXPERMDI VS59, VS60, $0, VS32 XXPERMDI VS59, VS60, $0, VS32
XXPERMDI VS59, VS60, $3, VS34 XXPERMDI VS59, VS60, $3, VS34
WORD $0x10842E8C // VMRGOW V4, V5, V4 VMRGOW V4, V5, V4
WORD $0x10C63E8C // VMRGOW V6, V7, V6 VMRGOW V6, V7, V6
WORD $0x13684F8C // VMRGEW V8, V9, V27 VMRGEW V8, V9, V27
WORD $0x138A5F8C // VMRGEW V10, V11, V28 VMRGEW V10, V11, V28
XXPERMDI VS36, VS38, $0, VS37 XXPERMDI VS36, VS38, $0, VS37
XXPERMDI VS36, VS38, $3, VS39 XXPERMDI VS36, VS38, $3, VS39
XXPERMDI VS61, VS62, $0, VS36 XXPERMDI VS61, VS62, $0, VS36
XXPERMDI VS61, VS62, $3, VS38 XXPERMDI VS61, VS62, $3, VS38
WORD $0x11084E8C // VMRGOW V8, V9, V8 VMRGOW V8, V9, V8
WORD $0x114A5E8C // VMRGOW V10, V11, V10 VMRGOW V10, V11, V10
WORD $0x13AC6F8C // VMRGEW V12, V13, V29 VMRGEW V12, V13, V29
WORD $0x13CE7F8C // VMRGEW V14, V15, V30 VMRGEW V14, V15, V30
XXPERMDI VS40, VS42, $0, VS41 XXPERMDI VS40, VS42, $0, VS41
XXPERMDI VS40, VS42, $3, VS43 XXPERMDI VS40, VS42, $3, VS43
XXPERMDI VS59, VS60, $0, VS40 XXPERMDI VS59, VS60, $0, VS40
XXPERMDI VS59, VS60, $3, VS42 XXPERMDI VS59, VS60, $3, VS42
WORD $0x118C6E8C // VMRGOW V12, V13, V12 VMRGOW V12, V13, V12
WORD $0x11CE7E8C // VMRGOW V14, V15, V14 VMRGOW V14, V15, V14
VSPLTISW $4, V27 VSPLTISW $4, V27
VADDUWM V26, V27, V26 VADDUWM V26, V27, V26
@ -306,6 +337,11 @@ loop_vsx:
VADDUWM V8, V18, V8 VADDUWM V8, V18, V8
VADDUWM V12, V19, V12 VADDUWM V12, V19, V12
BE_XXBRW(V0)
BE_XXBRW(V4)
BE_XXBRW(V8)
BE_XXBRW(V12)
CMPU LEN, $64 CMPU LEN, $64
BLT tail_vsx BLT tail_vsx
@ -334,6 +370,11 @@ loop_vsx:
VADDUWM V9, V18, V8 VADDUWM V9, V18, V8
VADDUWM V13, V19, V12 VADDUWM V13, V19, V12
BE_XXBRW(V0)
BE_XXBRW(V4)
BE_XXBRW(V8)
BE_XXBRW(V12)
CMPU LEN, $64 CMPU LEN, $64
BLT tail_vsx BLT tail_vsx
@ -341,8 +382,8 @@ loop_vsx:
LXVW4X (INP)(R8), VS60 LXVW4X (INP)(R8), VS60
LXVW4X (INP)(R9), VS61 LXVW4X (INP)(R9), VS61
LXVW4X (INP)(R10), VS62 LXVW4X (INP)(R10), VS62
VXOR V27, V0, V27
VXOR V27, V0, V27
VXOR V28, V4, V28 VXOR V28, V4, V28
VXOR V29, V8, V29 VXOR V29, V8, V29
VXOR V30, V12, V30 VXOR V30, V12, V30
@ -361,6 +402,11 @@ loop_vsx:
VADDUWM V10, V18, V8 VADDUWM V10, V18, V8
VADDUWM V14, V19, V12 VADDUWM V14, V19, V12
BE_XXBRW(V0)
BE_XXBRW(V4)
BE_XXBRW(V8)
BE_XXBRW(V12)
CMPU LEN, $64 CMPU LEN, $64
BLT tail_vsx BLT tail_vsx
@ -388,6 +434,11 @@ loop_vsx:
VADDUWM V11, V18, V8 VADDUWM V11, V18, V8
VADDUWM V15, V19, V12 VADDUWM V15, V19, V12
BE_XXBRW(V0)
BE_XXBRW(V4)
BE_XXBRW(V8)
BE_XXBRW(V12)
CMPU LEN, $64 CMPU LEN, $64
BLT tail_vsx BLT tail_vsx
@ -415,9 +466,9 @@ loop_vsx:
done_vsx: done_vsx:
// Increment counter by number of 64 byte blocks // Increment counter by number of 64 byte blocks
MOVD (CNT), R14 MOVWZ (CNT), R14
ADD BLOCKS, R14 ADD BLOCKS, R14
MOVD R14, (CNT) MOVWZ R14, (CNT)
RET RET
tail_vsx: tail_vsx:
@ -432,7 +483,7 @@ tail_vsx:
ADD $-1, R11, R12 ADD $-1, R11, R12
ADD $-1, INP ADD $-1, INP
ADD $-1, OUT ADD $-1, OUT
PCALIGN $16
looptail_vsx: looptail_vsx:
// Copying the result to OUT // Copying the result to OUT
// in bytes. // in bytes.
@ -440,7 +491,7 @@ looptail_vsx:
MOVBZU 1(INP), TMP MOVBZU 1(INP), TMP
XOR KEY, TMP, KEY XOR KEY, TMP, KEY
MOVBU KEY, 1(OUT) MOVBU KEY, 1(OUT)
BC 16, LT, looptail_vsx BDNZ looptail_vsx
// Clear the stack values // Clear the stack values
STXVW4X VS48, (R11)(R0) STXVW4X VS48, (R11)(R0)

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc && !purego //go:build gc && !purego
// +build gc,!purego
package chacha20 package chacha20

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc && !purego //go:build gc && !purego
// +build gc,!purego
#include "go_asm.h" #include "go_asm.h"
#include "textflag.h" #include "textflag.h"

View File

@ -5,15 +5,12 @@
// Package curve25519 provides an implementation of the X25519 function, which // Package curve25519 provides an implementation of the X25519 function, which
// performs scalar multiplication on the elliptic curve known as Curve25519. // performs scalar multiplication on the elliptic curve known as Curve25519.
// See RFC 7748. // See RFC 7748.
package curve25519 // import "golang.org/x/crypto/curve25519" //
// This package is a wrapper for the X25519 implementation
// in the crypto/ecdh package.
package curve25519
import ( import "crypto/ecdh"
"crypto/subtle"
"errors"
"strconv"
"golang.org/x/crypto/curve25519/internal/field"
)
// ScalarMult sets dst to the product scalar * point. // ScalarMult sets dst to the product scalar * point.
// //
@ -21,55 +18,13 @@ import (
// zeroes, irrespective of the scalar. Instead, use the X25519 function, which // zeroes, irrespective of the scalar. Instead, use the X25519 function, which
// will return an error. // will return an error.
func ScalarMult(dst, scalar, point *[32]byte) { func ScalarMult(dst, scalar, point *[32]byte) {
var e [32]byte if _, err := x25519(dst, scalar[:], point[:]); err != nil {
// The only error condition for x25519 when the inputs are 32 bytes long
copy(e[:], scalar[:]) // is if the output would have been the all-zero value.
e[0] &= 248 for i := range dst {
e[31] &= 127 dst[i] = 0
e[31] |= 64 }
var x1, x2, z2, x3, z3, tmp0, tmp1 field.Element
x1.SetBytes(point[:])
x2.One()
x3.Set(&x1)
z3.One()
swap := 0
for pos := 254; pos >= 0; pos-- {
b := e[pos/8] >> uint(pos&7)
b &= 1
swap ^= int(b)
x2.Swap(&x3, swap)
z2.Swap(&z3, swap)
swap = int(b)
tmp0.Subtract(&x3, &z3)
tmp1.Subtract(&x2, &z2)
x2.Add(&x2, &z2)
z2.Add(&x3, &z3)
z3.Multiply(&tmp0, &x2)
z2.Multiply(&z2, &tmp1)
tmp0.Square(&tmp1)
tmp1.Square(&x2)
x3.Add(&z3, &z2)
z2.Subtract(&z3, &z2)
x2.Multiply(&tmp1, &tmp0)
tmp1.Subtract(&tmp1, &tmp0)
z2.Square(&z2)
z3.Mult32(&tmp1, 121666)
x3.Square(&x3)
tmp0.Add(&tmp0, &z3)
z3.Multiply(&x1, &z2)
z2.Multiply(&tmp1, &tmp0)
} }
x2.Swap(&x3, swap)
z2.Swap(&z3, swap)
z2.Invert(&z2)
x2.Multiply(&x2, &z2)
copy(dst[:], x2.Bytes())
} }
// ScalarBaseMult sets dst to the product scalar * base where base is the // ScalarBaseMult sets dst to the product scalar * base where base is the
@ -78,7 +33,12 @@ func ScalarMult(dst, scalar, point *[32]byte) {
// It is recommended to use the X25519 function with Basepoint instead, as // It is recommended to use the X25519 function with Basepoint instead, as
// copying into fixed size arrays can lead to unexpected bugs. // copying into fixed size arrays can lead to unexpected bugs.
func ScalarBaseMult(dst, scalar *[32]byte) { func ScalarBaseMult(dst, scalar *[32]byte) {
ScalarMult(dst, scalar, &basePoint) curve := ecdh.X25519()
priv, err := curve.NewPrivateKey(scalar[:])
if err != nil {
panic("curve25519: internal error: scalarBaseMult was not 32 bytes")
}
copy(dst[:], priv.PublicKey().Bytes())
} }
const ( const (
@ -91,21 +51,10 @@ const (
// Basepoint is the canonical Curve25519 generator. // Basepoint is the canonical Curve25519 generator.
var Basepoint []byte var Basepoint []byte
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}
func init() { Basepoint = basePoint[:] } func init() { Basepoint = basePoint[:] }
func checkBasepoint() {
if subtle.ConstantTimeCompare(Basepoint, []byte{
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}) != 1 {
panic("curve25519: global Basepoint value was modified")
}
}
// X25519 returns the result of the scalar multiplication (scalar * point), // X25519 returns the result of the scalar multiplication (scalar * point),
// according to RFC 7748, Section 5. scalar, point and the return value are // according to RFC 7748, Section 5. scalar, point and the return value are
// slices of 32 bytes. // slices of 32 bytes.
@ -123,24 +72,19 @@ func X25519(scalar, point []byte) ([]byte, error) {
} }
func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) {
var in [32]byte curve := ecdh.X25519()
if l := len(scalar); l != 32 { pub, err := curve.NewPublicKey(point)
return nil, errors.New("bad scalar length: " + strconv.Itoa(l) + ", expected 32") if err != nil {
return nil, err
} }
if l := len(point); l != 32 { priv, err := curve.NewPrivateKey(scalar)
return nil, errors.New("bad point length: " + strconv.Itoa(l) + ", expected 32") if err != nil {
return nil, err
} }
copy(in[:], scalar) out, err := priv.ECDH(pub)
if &point[0] == &Basepoint[0] { if err != nil {
checkBasepoint() return nil, err
ScalarBaseMult(dst, &in)
} else {
var base, zero [32]byte
copy(base[:], point)
ScalarMult(dst, &in, &base)
if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 {
return nil, errors.New("bad input point: low order point")
}
} }
copy(dst[:], out)
return dst[:], nil return dst[:], nil
} }

View File

@ -1,7 +0,0 @@
This package is kept in sync with crypto/ed25519/internal/edwards25519/field in
the standard library.
If there are any changes in the standard library that need to be synced to this
package, run sync.sh. It will not overwrite any local changes made since the
previous sync, so it's ok to land changes in this package first, and then sync
to the standard library later.

View File

@ -1,416 +0,0 @@
// Copyright (c) 2017 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 field implements fast arithmetic modulo 2^255-19.
package field
import (
"crypto/subtle"
"encoding/binary"
"math/bits"
)
// Element represents an element of the field GF(2^255-19). Note that this
// is not a cryptographically secure group, and should only be used to interact
// with edwards25519.Point coordinates.
//
// This type works similarly to math/big.Int, and all arguments and receivers
// are allowed to alias.
//
// The zero value is a valid zero element.
type Element struct {
// An element t represents the integer
// t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204
//
// Between operations, all limbs are expected to be lower than 2^52.
l0 uint64
l1 uint64
l2 uint64
l3 uint64
l4 uint64
}
const maskLow51Bits uint64 = (1 << 51) - 1
var feZero = &Element{0, 0, 0, 0, 0}
// Zero sets v = 0, and returns v.
func (v *Element) Zero() *Element {
*v = *feZero
return v
}
var feOne = &Element{1, 0, 0, 0, 0}
// One sets v = 1, and returns v.
func (v *Element) One() *Element {
*v = *feOne
return v
}
// reduce reduces v modulo 2^255 - 19 and returns it.
func (v *Element) reduce() *Element {
v.carryPropagate()
// After the light reduction we now have a field element representation
// v < 2^255 + 2^13 * 19, but need v < 2^255 - 19.
// If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1,
// generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise.
c := (v.l0 + 19) >> 51
c = (v.l1 + c) >> 51
c = (v.l2 + c) >> 51
c = (v.l3 + c) >> 51
c = (v.l4 + c) >> 51
// If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's
// effectively applying the reduction identity to the carry.
v.l0 += 19 * c
v.l1 += v.l0 >> 51
v.l0 = v.l0 & maskLow51Bits
v.l2 += v.l1 >> 51
v.l1 = v.l1 & maskLow51Bits
v.l3 += v.l2 >> 51
v.l2 = v.l2 & maskLow51Bits
v.l4 += v.l3 >> 51
v.l3 = v.l3 & maskLow51Bits
// no additional carry
v.l4 = v.l4 & maskLow51Bits
return v
}
// Add sets v = a + b, and returns v.
func (v *Element) Add(a, b *Element) *Element {
v.l0 = a.l0 + b.l0
v.l1 = a.l1 + b.l1
v.l2 = a.l2 + b.l2
v.l3 = a.l3 + b.l3
v.l4 = a.l4 + b.l4
// Using the generic implementation here is actually faster than the
// assembly. Probably because the body of this function is so simple that
// the compiler can figure out better optimizations by inlining the carry
// propagation. TODO
return v.carryPropagateGeneric()
}
// Subtract sets v = a - b, and returns v.
func (v *Element) Subtract(a, b *Element) *Element {
// We first add 2 * p, to guarantee the subtraction won't underflow, and
// then subtract b (which can be up to 2^255 + 2^13 * 19).
v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0
v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1
v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2
v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3
v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4
return v.carryPropagate()
}
// Negate sets v = -a, and returns v.
func (v *Element) Negate(a *Element) *Element {
return v.Subtract(feZero, a)
}
// Invert sets v = 1/z mod p, and returns v.
//
// If z == 0, Invert returns v = 0.
func (v *Element) Invert(z *Element) *Element {
// Inversion is implemented as exponentiation with exponent p − 2. It uses the
// same sequence of 255 squarings and 11 multiplications as [Curve25519].
var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element
z2.Square(z) // 2
t.Square(&z2) // 4
t.Square(&t) // 8
z9.Multiply(&t, z) // 9
z11.Multiply(&z9, &z2) // 11
t.Square(&z11) // 22
z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0
t.Square(&z2_5_0) // 2^6 - 2^1
for i := 0; i < 4; i++ {
t.Square(&t) // 2^10 - 2^5
}
z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0
t.Square(&z2_10_0) // 2^11 - 2^1
for i := 0; i < 9; i++ {
t.Square(&t) // 2^20 - 2^10
}
z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0
t.Square(&z2_20_0) // 2^21 - 2^1
for i := 0; i < 19; i++ {
t.Square(&t) // 2^40 - 2^20
}
t.Multiply(&t, &z2_20_0) // 2^40 - 2^0
t.Square(&t) // 2^41 - 2^1
for i := 0; i < 9; i++ {
t.Square(&t) // 2^50 - 2^10
}
z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0
t.Square(&z2_50_0) // 2^51 - 2^1
for i := 0; i < 49; i++ {
t.Square(&t) // 2^100 - 2^50
}
z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0
t.Square(&z2_100_0) // 2^101 - 2^1
for i := 0; i < 99; i++ {
t.Square(&t) // 2^200 - 2^100
}
t.Multiply(&t, &z2_100_0) // 2^200 - 2^0
t.Square(&t) // 2^201 - 2^1
for i := 0; i < 49; i++ {
t.Square(&t) // 2^250 - 2^50
}
t.Multiply(&t, &z2_50_0) // 2^250 - 2^0
t.Square(&t) // 2^251 - 2^1
t.Square(&t) // 2^252 - 2^2
t.Square(&t) // 2^253 - 2^3
t.Square(&t) // 2^254 - 2^4
t.Square(&t) // 2^255 - 2^5
return v.Multiply(&t, &z11) // 2^255 - 21
}
// Set sets v = a, and returns v.
func (v *Element) Set(a *Element) *Element {
*v = *a
return v
}
// SetBytes sets v to x, which must be a 32-byte little-endian encoding.
//
// Consistent with RFC 7748, the most significant bit (the high bit of the
// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1)
// are accepted. Note that this is laxer than specified by RFC 8032.
func (v *Element) SetBytes(x []byte) *Element {
if len(x) != 32 {
panic("edwards25519: invalid field element input size")
}
// Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51).
v.l0 = binary.LittleEndian.Uint64(x[0:8])
v.l0 &= maskLow51Bits
// Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51).
v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3
v.l1 &= maskLow51Bits
// Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51).
v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6
v.l2 &= maskLow51Bits
// Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51).
v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1
v.l3 &= maskLow51Bits
// Bits 204:251 (bytes 24:32, bits 192:256, shift 12, mask 51).
// Note: not bytes 25:33, shift 4, to avoid overread.
v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12
v.l4 &= maskLow51Bits
return v
}
// Bytes returns the canonical 32-byte little-endian encoding of v.
func (v *Element) Bytes() []byte {
// This function is outlined to make the allocations inline in the caller
// rather than happen on the heap.
var out [32]byte
return v.bytes(&out)
}
func (v *Element) bytes(out *[32]byte) []byte {
t := *v
t.reduce()
var buf [8]byte
for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} {
bitsOffset := i * 51
binary.LittleEndian.PutUint64(buf[:], l<<uint(bitsOffset%8))
for i, bb := range buf {
off := bitsOffset/8 + i
if off >= len(out) {
break
}
out[off] |= bb
}
}
return out[:]
}
// Equal returns 1 if v and u are equal, and 0 otherwise.
func (v *Element) Equal(u *Element) int {
sa, sv := u.Bytes(), v.Bytes()
return subtle.ConstantTimeCompare(sa, sv)
}
// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise.
func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) }
// Select sets v to a if cond == 1, and to b if cond == 0.
func (v *Element) Select(a, b *Element, cond int) *Element {
m := mask64Bits(cond)
v.l0 = (m & a.l0) | (^m & b.l0)
v.l1 = (m & a.l1) | (^m & b.l1)
v.l2 = (m & a.l2) | (^m & b.l2)
v.l3 = (m & a.l3) | (^m & b.l3)
v.l4 = (m & a.l4) | (^m & b.l4)
return v
}
// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v.
func (v *Element) Swap(u *Element, cond int) {
m := mask64Bits(cond)
t := m & (v.l0 ^ u.l0)
v.l0 ^= t
u.l0 ^= t
t = m & (v.l1 ^ u.l1)
v.l1 ^= t
u.l1 ^= t
t = m & (v.l2 ^ u.l2)
v.l2 ^= t
u.l2 ^= t
t = m & (v.l3 ^ u.l3)
v.l3 ^= t
u.l3 ^= t
t = m & (v.l4 ^ u.l4)
v.l4 ^= t
u.l4 ^= t
}
// IsNegative returns 1 if v is negative, and 0 otherwise.
func (v *Element) IsNegative() int {
return int(v.Bytes()[0] & 1)
}
// Absolute sets v to |u|, and returns v.
func (v *Element) Absolute(u *Element) *Element {
return v.Select(new(Element).Negate(u), u, u.IsNegative())
}
// Multiply sets v = x * y, and returns v.
func (v *Element) Multiply(x, y *Element) *Element {
feMul(v, x, y)
return v
}
// Square sets v = x * x, and returns v.
func (v *Element) Square(x *Element) *Element {
feSquare(v, x)
return v
}
// Mult32 sets v = x * y, and returns v.
func (v *Element) Mult32(x *Element, y uint32) *Element {
x0lo, x0hi := mul51(x.l0, y)
x1lo, x1hi := mul51(x.l1, y)
x2lo, x2hi := mul51(x.l2, y)
x3lo, x3hi := mul51(x.l3, y)
x4lo, x4hi := mul51(x.l4, y)
v.l0 = x0lo + 19*x4hi // carried over per the reduction identity
v.l1 = x1lo + x0hi
v.l2 = x2lo + x1hi
v.l3 = x3lo + x2hi
v.l4 = x4lo + x3hi
// The hi portions are going to be only 32 bits, plus any previous excess,
// so we can skip the carry propagation.
return v
}
// mul51 returns lo + hi * 2⁵¹ = a * b.
func mul51(a uint64, b uint32) (lo uint64, hi uint64) {
mh, ml := bits.Mul64(a, uint64(b))
lo = ml & maskLow51Bits
hi = (mh << 13) | (ml >> 51)
return
}
// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3.
func (v *Element) Pow22523(x *Element) *Element {
var t0, t1, t2 Element
t0.Square(x) // x^2
t1.Square(&t0) // x^4
t1.Square(&t1) // x^8
t1.Multiply(x, &t1) // x^9
t0.Multiply(&t0, &t1) // x^11
t0.Square(&t0) // x^22
t0.Multiply(&t1, &t0) // x^31
t1.Square(&t0) // x^62
for i := 1; i < 5; i++ { // x^992
t1.Square(&t1)
}
t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1
t1.Square(&t0) // 2^11 - 2
for i := 1; i < 10; i++ { // 2^20 - 2^10
t1.Square(&t1)
}
t1.Multiply(&t1, &t0) // 2^20 - 1
t2.Square(&t1) // 2^21 - 2
for i := 1; i < 20; i++ { // 2^40 - 2^20
t2.Square(&t2)
}
t1.Multiply(&t2, &t1) // 2^40 - 1
t1.Square(&t1) // 2^41 - 2
for i := 1; i < 10; i++ { // 2^50 - 2^10
t1.Square(&t1)
}
t0.Multiply(&t1, &t0) // 2^50 - 1
t1.Square(&t0) // 2^51 - 2
for i := 1; i < 50; i++ { // 2^100 - 2^50
t1.Square(&t1)
}
t1.Multiply(&t1, &t0) // 2^100 - 1
t2.Square(&t1) // 2^101 - 2
for i := 1; i < 100; i++ { // 2^200 - 2^100
t2.Square(&t2)
}
t1.Multiply(&t2, &t1) // 2^200 - 1
t1.Square(&t1) // 2^201 - 2
for i := 1; i < 50; i++ { // 2^250 - 2^50
t1.Square(&t1)
}
t0.Multiply(&t1, &t0) // 2^250 - 1
t0.Square(&t0) // 2^251 - 2
t0.Square(&t0) // 2^252 - 4
return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3)
}
// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion.
var sqrtM1 = &Element{1718705420411056, 234908883556509,
2233514472574048, 2117202627021982, 765476049583133}
// SqrtRatio sets r to the non-negative square root of the ratio of u and v.
//
// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio
// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00,
// and returns r and 0.
func (r *Element) SqrtRatio(u, v *Element) (rr *Element, wasSquare int) {
var a, b Element
// r = (u * v3) * (u * v7)^((p-5)/8)
v2 := a.Square(v)
uv3 := b.Multiply(u, b.Multiply(v2, v))
uv7 := a.Multiply(uv3, a.Square(v2))
r.Multiply(uv3, r.Pow22523(uv7))
check := a.Multiply(v, a.Square(r)) // check = v * r^2
uNeg := b.Negate(u)
correctSignSqrt := check.Equal(u)
flippedSignSqrt := check.Equal(uNeg)
flippedSignSqrtI := check.Equal(uNeg.Multiply(uNeg, sqrtM1))
rPrime := b.Multiply(r, sqrtM1) // r_prime = SQRT_M1 * r
// r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r)
r.Select(rPrime, r, flippedSignSqrt|flippedSignSqrtI)
r.Absolute(r) // Choose the nonnegative square root.
return r, correctSignSqrt | flippedSignSqrt
}

View File

@ -1,16 +0,0 @@
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
package field
// feMul sets out = a * b. It works like feMulGeneric.
//
//go:noescape
func feMul(out *Element, a *Element, b *Element)
// feSquare sets out = a * a. It works like feSquareGeneric.
//
//go:noescape
func feSquare(out *Element, a *Element)

View File

@ -1,379 +0,0 @@
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
#include "textflag.h"
// func feMul(out *Element, a *Element, b *Element)
TEXT ·feMul(SB), NOSPLIT, $0-24
MOVQ a+8(FP), CX
MOVQ b+16(FP), BX
// r0 = a0×b0
MOVQ (CX), AX
MULQ (BX)
MOVQ AX, DI
MOVQ DX, SI
// r0 += 19×a1×b4
MOVQ 8(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 32(BX)
ADDQ AX, DI
ADCQ DX, SI
// r0 += 19×a2×b3
MOVQ 16(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 24(BX)
ADDQ AX, DI
ADCQ DX, SI
// r0 += 19×a3×b2
MOVQ 24(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 16(BX)
ADDQ AX, DI
ADCQ DX, SI
// r0 += 19×a4×b1
MOVQ 32(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 8(BX)
ADDQ AX, DI
ADCQ DX, SI
// r1 = a0×b1
MOVQ (CX), AX
MULQ 8(BX)
MOVQ AX, R9
MOVQ DX, R8
// r1 += a1×b0
MOVQ 8(CX), AX
MULQ (BX)
ADDQ AX, R9
ADCQ DX, R8
// r1 += 19×a2×b4
MOVQ 16(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 32(BX)
ADDQ AX, R9
ADCQ DX, R8
// r1 += 19×a3×b3
MOVQ 24(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 24(BX)
ADDQ AX, R9
ADCQ DX, R8
// r1 += 19×a4×b2
MOVQ 32(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 16(BX)
ADDQ AX, R9
ADCQ DX, R8
// r2 = a0×b2
MOVQ (CX), AX
MULQ 16(BX)
MOVQ AX, R11
MOVQ DX, R10
// r2 += a1×b1
MOVQ 8(CX), AX
MULQ 8(BX)
ADDQ AX, R11
ADCQ DX, R10
// r2 += a2×b0
MOVQ 16(CX), AX
MULQ (BX)
ADDQ AX, R11
ADCQ DX, R10
// r2 += 19×a3×b4
MOVQ 24(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 32(BX)
ADDQ AX, R11
ADCQ DX, R10
// r2 += 19×a4×b3
MOVQ 32(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 24(BX)
ADDQ AX, R11
ADCQ DX, R10
// r3 = a0×b3
MOVQ (CX), AX
MULQ 24(BX)
MOVQ AX, R13
MOVQ DX, R12
// r3 += a1×b2
MOVQ 8(CX), AX
MULQ 16(BX)
ADDQ AX, R13
ADCQ DX, R12
// r3 += a2×b1
MOVQ 16(CX), AX
MULQ 8(BX)
ADDQ AX, R13
ADCQ DX, R12
// r3 += a3×b0
MOVQ 24(CX), AX
MULQ (BX)
ADDQ AX, R13
ADCQ DX, R12
// r3 += 19×a4×b4
MOVQ 32(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 32(BX)
ADDQ AX, R13
ADCQ DX, R12
// r4 = a0×b4
MOVQ (CX), AX
MULQ 32(BX)
MOVQ AX, R15
MOVQ DX, R14
// r4 += a1×b3
MOVQ 8(CX), AX
MULQ 24(BX)
ADDQ AX, R15
ADCQ DX, R14
// r4 += a2×b2
MOVQ 16(CX), AX
MULQ 16(BX)
ADDQ AX, R15
ADCQ DX, R14
// r4 += a3×b1
MOVQ 24(CX), AX
MULQ 8(BX)
ADDQ AX, R15
ADCQ DX, R14
// r4 += a4×b0
MOVQ 32(CX), AX
MULQ (BX)
ADDQ AX, R15
ADCQ DX, R14
// First reduction chain
MOVQ $0x0007ffffffffffff, AX
SHLQ $0x0d, DI, SI
SHLQ $0x0d, R9, R8
SHLQ $0x0d, R11, R10
SHLQ $0x0d, R13, R12
SHLQ $0x0d, R15, R14
ANDQ AX, DI
IMUL3Q $0x13, R14, R14
ADDQ R14, DI
ANDQ AX, R9
ADDQ SI, R9
ANDQ AX, R11
ADDQ R8, R11
ANDQ AX, R13
ADDQ R10, R13
ANDQ AX, R15
ADDQ R12, R15
// Second reduction chain (carryPropagate)
MOVQ DI, SI
SHRQ $0x33, SI
MOVQ R9, R8
SHRQ $0x33, R8
MOVQ R11, R10
SHRQ $0x33, R10
MOVQ R13, R12
SHRQ $0x33, R12
MOVQ R15, R14
SHRQ $0x33, R14
ANDQ AX, DI
IMUL3Q $0x13, R14, R14
ADDQ R14, DI
ANDQ AX, R9
ADDQ SI, R9
ANDQ AX, R11
ADDQ R8, R11
ANDQ AX, R13
ADDQ R10, R13
ANDQ AX, R15
ADDQ R12, R15
// Store output
MOVQ out+0(FP), AX
MOVQ DI, (AX)
MOVQ R9, 8(AX)
MOVQ R11, 16(AX)
MOVQ R13, 24(AX)
MOVQ R15, 32(AX)
RET
// func feSquare(out *Element, a *Element)
TEXT ·feSquare(SB), NOSPLIT, $0-16
MOVQ a+8(FP), CX
// r0 = l0×l0
MOVQ (CX), AX
MULQ (CX)
MOVQ AX, SI
MOVQ DX, BX
// r0 += 38×l1×l4
MOVQ 8(CX), AX
IMUL3Q $0x26, AX, AX
MULQ 32(CX)
ADDQ AX, SI
ADCQ DX, BX
// r0 += 38×l2×l3
MOVQ 16(CX), AX
IMUL3Q $0x26, AX, AX
MULQ 24(CX)
ADDQ AX, SI
ADCQ DX, BX
// r1 = 2×l0×l1
MOVQ (CX), AX
SHLQ $0x01, AX
MULQ 8(CX)
MOVQ AX, R8
MOVQ DX, DI
// r1 += 38×l2×l4
MOVQ 16(CX), AX
IMUL3Q $0x26, AX, AX
MULQ 32(CX)
ADDQ AX, R8
ADCQ DX, DI
// r1 += 19×l3×l3
MOVQ 24(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 24(CX)
ADDQ AX, R8
ADCQ DX, DI
// r2 = 2×l0×l2
MOVQ (CX), AX
SHLQ $0x01, AX
MULQ 16(CX)
MOVQ AX, R10
MOVQ DX, R9
// r2 += l1×l1
MOVQ 8(CX), AX
MULQ 8(CX)
ADDQ AX, R10
ADCQ DX, R9
// r2 += 38×l3×l4
MOVQ 24(CX), AX
IMUL3Q $0x26, AX, AX
MULQ 32(CX)
ADDQ AX, R10
ADCQ DX, R9
// r3 = 2×l0×l3
MOVQ (CX), AX
SHLQ $0x01, AX
MULQ 24(CX)
MOVQ AX, R12
MOVQ DX, R11
// r3 += 2×l1×l2
MOVQ 8(CX), AX
IMUL3Q $0x02, AX, AX
MULQ 16(CX)
ADDQ AX, R12
ADCQ DX, R11
// r3 += 19×l4×l4
MOVQ 32(CX), AX
IMUL3Q $0x13, AX, AX
MULQ 32(CX)
ADDQ AX, R12
ADCQ DX, R11
// r4 = 2×l0×l4
MOVQ (CX), AX
SHLQ $0x01, AX
MULQ 32(CX)
MOVQ AX, R14
MOVQ DX, R13
// r4 += 2×l1×l3
MOVQ 8(CX), AX
IMUL3Q $0x02, AX, AX
MULQ 24(CX)
ADDQ AX, R14
ADCQ DX, R13
// r4 += l2×l2
MOVQ 16(CX), AX
MULQ 16(CX)
ADDQ AX, R14
ADCQ DX, R13
// First reduction chain
MOVQ $0x0007ffffffffffff, AX
SHLQ $0x0d, SI, BX
SHLQ $0x0d, R8, DI
SHLQ $0x0d, R10, R9
SHLQ $0x0d, R12, R11
SHLQ $0x0d, R14, R13
ANDQ AX, SI
IMUL3Q $0x13, R13, R13
ADDQ R13, SI
ANDQ AX, R8
ADDQ BX, R8
ANDQ AX, R10
ADDQ DI, R10
ANDQ AX, R12
ADDQ R9, R12
ANDQ AX, R14
ADDQ R11, R14
// Second reduction chain (carryPropagate)
MOVQ SI, BX
SHRQ $0x33, BX
MOVQ R8, DI
SHRQ $0x33, DI
MOVQ R10, R9
SHRQ $0x33, R9
MOVQ R12, R11
SHRQ $0x33, R11
MOVQ R14, R13
SHRQ $0x33, R13
ANDQ AX, SI
IMUL3Q $0x13, R13, R13
ADDQ R13, SI
ANDQ AX, R8
ADDQ BX, R8
ANDQ AX, R10
ADDQ DI, R10
ANDQ AX, R12
ADDQ R9, R12
ANDQ AX, R14
ADDQ R11, R14
// Store output
MOVQ out+0(FP), AX
MOVQ SI, (AX)
MOVQ R8, 8(AX)
MOVQ R10, 16(AX)
MOVQ R12, 24(AX)
MOVQ R14, 32(AX)
RET

View File

@ -1,12 +0,0 @@
// Copyright (c) 2019 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.
//go:build !amd64 || !gc || purego
// +build !amd64 !gc purego
package field
func feMul(v, x, y *Element) { feMulGeneric(v, x, y) }
func feSquare(v, x *Element) { feSquareGeneric(v, x) }

View File

@ -1,16 +0,0 @@
// Copyright (c) 2020 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.
//go:build arm64 && gc && !purego
// +build arm64,gc,!purego
package field
//go:noescape
func carryPropagate(v *Element)
func (v *Element) carryPropagate() *Element {
carryPropagate(v)
return v
}

View File

@ -1,43 +0,0 @@
// Copyright (c) 2020 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.
//go:build arm64 && gc && !purego
// +build arm64,gc,!purego
#include "textflag.h"
// carryPropagate works exactly like carryPropagateGeneric and uses the
// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but
// avoids loading R0-R4 twice and uses LDP and STP.
//
// See https://golang.org/issues/43145 for the main compiler issue.
//
// func carryPropagate(v *Element)
TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8
MOVD v+0(FP), R20
LDP 0(R20), (R0, R1)
LDP 16(R20), (R2, R3)
MOVD 32(R20), R4
AND $0x7ffffffffffff, R0, R10
AND $0x7ffffffffffff, R1, R11
AND $0x7ffffffffffff, R2, R12
AND $0x7ffffffffffff, R3, R13
AND $0x7ffffffffffff, R4, R14
ADD R0>>51, R11, R11
ADD R1>>51, R12, R12
ADD R2>>51, R13, R13
ADD R3>>51, R14, R14
// R4>>51 * 19 + R10 -> R10
LSR $51, R4, R21
MOVD $19, R22
MADD R22, R10, R21, R10
STP (R10, R11), 0(R20)
STP (R12, R13), 16(R20)
MOVD R14, 32(R20)
RET

View File

@ -1,12 +0,0 @@
// Copyright (c) 2021 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.
//go:build !arm64 || !gc || purego
// +build !arm64 !gc purego
package field
func (v *Element) carryPropagate() *Element {
return v.carryPropagateGeneric()
}

View File

@ -1,264 +0,0 @@
// Copyright (c) 2017 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 field
import "math/bits"
// uint128 holds a 128-bit number as two 64-bit limbs, for use with the
// bits.Mul64 and bits.Add64 intrinsics.
type uint128 struct {
lo, hi uint64
}
// mul64 returns a * b.
func mul64(a, b uint64) uint128 {
hi, lo := bits.Mul64(a, b)
return uint128{lo, hi}
}
// addMul64 returns v + a * b.
func addMul64(v uint128, a, b uint64) uint128 {
hi, lo := bits.Mul64(a, b)
lo, c := bits.Add64(lo, v.lo, 0)
hi, _ = bits.Add64(hi, v.hi, c)
return uint128{lo, hi}
}
// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits.
func shiftRightBy51(a uint128) uint64 {
return (a.hi << (64 - 51)) | (a.lo >> 51)
}
func feMulGeneric(v, a, b *Element) {
a0 := a.l0
a1 := a.l1
a2 := a.l2
a3 := a.l3
a4 := a.l4
b0 := b.l0
b1 := b.l1
b2 := b.l2
b3 := b.l3
b4 := b.l4
// Limb multiplication works like pen-and-paper columnar multiplication, but
// with 51-bit limbs instead of digits.
//
// a4 a3 a2 a1 a0 x
// b4 b3 b2 b1 b0 =
// ------------------------
// a4b0 a3b0 a2b0 a1b0 a0b0 +
// a4b1 a3b1 a2b1 a1b1 a0b1 +
// a4b2 a3b2 a2b2 a1b2 a0b2 +
// a4b3 a3b3 a2b3 a1b3 a0b3 +
// a4b4 a3b4 a2b4 a1b4 a0b4 =
// ----------------------------------------------
// r8 r7 r6 r5 r4 r3 r2 r1 r0
//
// We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to
// reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5,
// r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc.
//
// Reduction can be carried out simultaneously to multiplication. For
// example, we do not compute r5: whenever the result of a multiplication
// belongs to r5, like a1b4, we multiply it by 19 and add the result to r0.
//
// a4b0 a3b0 a2b0 a1b0 a0b0 +
// a3b1 a2b1 a1b1 a0b1 19×a4b1 +
// a2b2 a1b2 a0b2 19×a4b2 19×a3b2 +
// a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 +
// a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 =
// --------------------------------------
// r4 r3 r2 r1 r0
//
// Finally we add up the columns into wide, overlapping limbs.
a1_19 := a1 * 19
a2_19 := a2 * 19
a3_19 := a3 * 19
a4_19 := a4 * 19
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
r0 := mul64(a0, b0)
r0 = addMul64(r0, a1_19, b4)
r0 = addMul64(r0, a2_19, b3)
r0 = addMul64(r0, a3_19, b2)
r0 = addMul64(r0, a4_19, b1)
// r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2)
r1 := mul64(a0, b1)
r1 = addMul64(r1, a1, b0)
r1 = addMul64(r1, a2_19, b4)
r1 = addMul64(r1, a3_19, b3)
r1 = addMul64(r1, a4_19, b2)
// r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3)
r2 := mul64(a0, b2)
r2 = addMul64(r2, a1, b1)
r2 = addMul64(r2, a2, b0)
r2 = addMul64(r2, a3_19, b4)
r2 = addMul64(r2, a4_19, b3)
// r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4
r3 := mul64(a0, b3)
r3 = addMul64(r3, a1, b2)
r3 = addMul64(r3, a2, b1)
r3 = addMul64(r3, a3, b0)
r3 = addMul64(r3, a4_19, b4)
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
r4 := mul64(a0, b4)
r4 = addMul64(r4, a1, b3)
r4 = addMul64(r4, a2, b2)
r4 = addMul64(r4, a3, b1)
r4 = addMul64(r4, a4, b0)
// After the multiplication, we need to reduce (carry) the five coefficients
// to obtain a result with limbs that are at most slightly larger than 2⁵¹,
// to respect the Element invariant.
//
// Overall, the reduction works the same as carryPropagate, except with
// wider inputs: we take the carry for each coefficient by shifting it right
// by 51, and add it to the limb above it. The top carry is multiplied by 19
// according to the reduction identity and added to the lowest limb.
//
// The largest coefficient (r0) will be at most 111 bits, which guarantees
// that all carries are at most 111 - 51 = 60 bits, which fits in a uint64.
//
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
// r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²)
// r0 < (1 + 19 × 4) × 2⁵² × 2⁵²
// r0 < 2⁷ × 2⁵² × 2⁵²
// r0 < 2¹¹¹
//
// Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most
// 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and
// allows us to easily apply the reduction identity.
//
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
// r4 < 5 × 2⁵² × 2⁵²
// r4 < 2¹⁰⁷
//
c0 := shiftRightBy51(r0)
c1 := shiftRightBy51(r1)
c2 := shiftRightBy51(r2)
c3 := shiftRightBy51(r3)
c4 := shiftRightBy51(r4)
rr0 := r0.lo&maskLow51Bits + c4*19
rr1 := r1.lo&maskLow51Bits + c0
rr2 := r2.lo&maskLow51Bits + c1
rr3 := r3.lo&maskLow51Bits + c2
rr4 := r4.lo&maskLow51Bits + c3
// Now all coefficients fit into 64-bit registers but are still too large to
// be passed around as a Element. We therefore do one last carry chain,
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
*v = Element{rr0, rr1, rr2, rr3, rr4}
v.carryPropagate()
}
func feSquareGeneric(v, a *Element) {
l0 := a.l0
l1 := a.l1
l2 := a.l2
l3 := a.l3
l4 := a.l4
// Squaring works precisely like multiplication above, but thanks to its
// symmetry we get to group a few terms together.
//
// l4 l3 l2 l1 l0 x
// l4 l3 l2 l1 l0 =
// ------------------------
// l4l0 l3l0 l2l0 l1l0 l0l0 +
// l4l1 l3l1 l2l1 l1l1 l0l1 +
// l4l2 l3l2 l2l2 l1l2 l0l2 +
// l4l3 l3l3 l2l3 l1l3 l0l3 +
// l4l4 l3l4 l2l4 l1l4 l0l4 =
// ----------------------------------------------
// r8 r7 r6 r5 r4 r3 r2 r1 r0
//
// l4l0 l3l0 l2l0 l1l0 l0l0 +
// l3l1 l2l1 l1l1 l0l1 19×l4l1 +
// l2l2 l1l2 l0l2 19×l4l2 19×l3l2 +
// l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 +
// l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 =
// --------------------------------------
// r4 r3 r2 r1 r0
//
// With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with
// only three Mul64 and four Add64, instead of five and eight.
l0_2 := l0 * 2
l1_2 := l1 * 2
l1_38 := l1 * 38
l2_38 := l2 * 38
l3_38 := l3 * 38
l3_19 := l3 * 19
l4_19 := l4 * 19
// r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3)
r0 := mul64(l0, l0)
r0 = addMul64(r0, l1_38, l4)
r0 = addMul64(r0, l2_38, l3)
// r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3
r1 := mul64(l0_2, l1)
r1 = addMul64(r1, l2_38, l4)
r1 = addMul64(r1, l3_19, l3)
// r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4
r2 := mul64(l0_2, l2)
r2 = addMul64(r2, l1, l1)
r2 = addMul64(r2, l3_38, l4)
// r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4
r3 := mul64(l0_2, l3)
r3 = addMul64(r3, l1_2, l2)
r3 = addMul64(r3, l4_19, l4)
// r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2
r4 := mul64(l0_2, l4)
r4 = addMul64(r4, l1_2, l3)
r4 = addMul64(r4, l2, l2)
c0 := shiftRightBy51(r0)
c1 := shiftRightBy51(r1)
c2 := shiftRightBy51(r2)
c3 := shiftRightBy51(r3)
c4 := shiftRightBy51(r4)
rr0 := r0.lo&maskLow51Bits + c4*19
rr1 := r1.lo&maskLow51Bits + c0
rr2 := r2.lo&maskLow51Bits + c1
rr3 := r3.lo&maskLow51Bits + c2
rr4 := r4.lo&maskLow51Bits + c3
*v = Element{rr0, rr1, rr2, rr3, rr4}
v.carryPropagate()
}
// carryPropagate brings the limbs below 52 bits by applying the reduction
// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry. TODO inline
func (v *Element) carryPropagateGeneric() *Element {
c0 := v.l0 >> 51
c1 := v.l1 >> 51
c2 := v.l2 >> 51
c3 := v.l3 >> 51
c4 := v.l4 >> 51
v.l0 = v.l0&maskLow51Bits + c4*19
v.l1 = v.l1&maskLow51Bits + c0
v.l2 = v.l2&maskLow51Bits + c1
v.l3 = v.l3&maskLow51Bits + c2
v.l4 = v.l4&maskLow51Bits + c3
return v
}

View File

@ -1 +0,0 @@
b0c49ae9f59d233526f8934262c5bbbe14d4358d

View File

@ -1,19 +0,0 @@
#! /bin/bash
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
STD_PATH=src/crypto/ed25519/internal/edwards25519/field
LOCAL_PATH=curve25519/internal/field
LAST_SYNC_REF=$(cat $LOCAL_PATH/sync.checkpoint)
git fetch https://go.googlesource.com/go master
if git diff --quiet $LAST_SYNC_REF:$STD_PATH FETCH_HEAD:$STD_PATH; then
echo "No changes."
else
NEW_REF=$(git rev-parse FETCH_HEAD | tee $LOCAL_PATH/sync.checkpoint)
echo "Applying changes from $LAST_SYNC_REF to $NEW_REF..."
git diff $LAST_SYNC_REF:$STD_PATH FETCH_HEAD:$STD_PATH | \
git apply -3 --directory=$LOCAL_PATH
fi

View File

@ -1,71 +0,0 @@
// Copyright 2019 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
// https://ed25519.cr.yp.to/.
//
// These functions are also compatible with the “Ed25519” function defined in
// 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”.
//
// Beginning with Go 1.13, the functionality of this package was moved to the
// standard library as crypto/ed25519. This package only acts as a compatibility
// wrapper.
package ed25519
import (
"crypto/ed25519"
"io"
)
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
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.
//
// This type is an alias for crypto/ed25519's PublicKey type.
// See the crypto/ed25519 package for the methods on this type.
type PublicKey = ed25519.PublicKey
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
//
// This type is an alias for crypto/ed25519's PrivateKey type.
// See the crypto/ed25519 package for the methods on this type.
type PrivateKey = ed25519.PrivateKey
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
return ed25519.GenerateKey(rand)
}
// 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 {
return ed25519.NewKeyFromSeed(seed)
}
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
return ed25519.Sign(privateKey, message)
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
return ed25519.Verify(publicKey, message, sig)
}

View File

@ -3,11 +3,9 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !purego //go:build !purego
// +build !purego
// Package subtle implements functions that are often useful in cryptographic // Package alias implements memory aliasing tests.
// code but require careful thought to use correctly. package alias
package subtle // import "golang.org/x/crypto/internal/subtle"
import "unsafe" import "unsafe"

View File

@ -3,11 +3,9 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build purego //go:build purego
// +build purego
// Package subtle implements functions that are often useful in cryptographic // Package alias implements memory aliasing tests.
// code but require careful thought to use correctly. package alias
package subtle // import "golang.org/x/crypto/internal/subtle"
// This is the Google App Engine standard variant based on reflect // This is the Google App Engine standard variant based on reflect
// because the unsafe package and cgo are disallowed. // because the unsafe package and cgo are disallowed.

View File

@ -1,40 +0,0 @@
// Copyright 2019 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.
//go:build !go1.13
// +build !go1.13
package poly1305
// Generic fallbacks for the math/bits intrinsics, copied from
// src/math/bits/bits.go. They were added in Go 1.12, but Add64 and Sum64 had
// variable time fallbacks until Go 1.13.
func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) {
sum = x + y + carry
carryOut = ((x & y) | ((x | y) &^ sum)) >> 63
return
}
func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) {
diff = x - y - borrow
borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63
return
}
func bitsMul64(x, y uint64) (hi, lo uint64) {
const mask32 = 1<<32 - 1
x0 := x & mask32
x1 := x >> 32
y0 := y & mask32
y1 := y >> 32
w0 := x0 * y0
t := x1*y0 + w0>>32
w1 := t & mask32
w2 := t >> 32
w1 += x0 * y1
hi = x1*y1 + w2 + w1>>32
lo = x * y
return
}

View File

@ -1,22 +0,0 @@
// Copyright 2019 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.
//go:build go1.13
// +build go1.13
package poly1305
import "math/bits"
func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) {
return bits.Add64(x, y, carry)
}
func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) {
return bits.Sub64(x, y, borrow)
}
func bitsMul64(x, y uint64) (hi, lo uint64) {
return bits.Mul64(x, y)
}

View File

@ -2,8 +2,7 @@
// 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.
//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego //go:build (!amd64 && !ppc64le && !ppc64 && !s390x) || !gc || purego
// +build !amd64,!ppc64le,!s390x !gc purego
package poly1305 package poly1305

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc && !purego //go:build gc && !purego
// +build gc,!purego
package poly1305 package poly1305

View File

@ -1,109 +1,93 @@
// Copyright 2012 The Go Authors. All rights reserved. // Code generated by command: go run sum_amd64_asm.go -out ../sum_amd64.s -pkg poly1305. DO NOT EDIT.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc && !purego //go:build gc && !purego
// +build gc,!purego
#include "textflag.h" // func update(state *macState, msg []byte)
#define POLY1305_ADD(msg, h0, h1, h2) \
ADDQ 0(msg), h0; \
ADCQ 8(msg), h1; \
ADCQ $1, h2; \
LEAQ 16(msg), msg
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \
MOVQ r0, AX; \
MULQ h0; \
MOVQ AX, t0; \
MOVQ DX, t1; \
MOVQ r0, AX; \
MULQ h1; \
ADDQ AX, t1; \
ADCQ $0, DX; \
MOVQ r0, t2; \
IMULQ h2, t2; \
ADDQ DX, t2; \
\
MOVQ r1, AX; \
MULQ h0; \
ADDQ AX, t1; \
ADCQ $0, DX; \
MOVQ DX, h0; \
MOVQ r1, t3; \
IMULQ h2, t3; \
MOVQ r1, AX; \
MULQ h1; \
ADDQ AX, t2; \
ADCQ DX, t3; \
ADDQ h0, t2; \
ADCQ $0, t3; \
\
MOVQ t0, h0; \
MOVQ t1, h1; \
MOVQ t2, h2; \
ANDQ $3, h2; \
MOVQ t2, t0; \
ANDQ $0xFFFFFFFFFFFFFFFC, t0; \
ADDQ t0, h0; \
ADCQ t3, h1; \
ADCQ $0, h2; \
SHRQ $2, t3, t2; \
SHRQ $2, t3; \
ADDQ t2, h0; \
ADCQ t3, h1; \
ADCQ $0, h2
// func update(state *[7]uint64, msg []byte)
TEXT ·update(SB), $0-32 TEXT ·update(SB), $0-32
MOVQ state+0(FP), DI MOVQ state+0(FP), DI
MOVQ msg_base+8(FP), SI MOVQ msg_base+8(FP), SI
MOVQ msg_len+16(FP), R15 MOVQ msg_len+16(FP), R15
MOVQ (DI), R8
MOVQ 0(DI), R8 // h0 MOVQ 8(DI), R9
MOVQ 8(DI), R9 // h1 MOVQ 16(DI), R10
MOVQ 16(DI), R10 // h2 MOVQ 24(DI), R11
MOVQ 24(DI), R11 // r0 MOVQ 32(DI), R12
MOVQ 32(DI), R12 // r1 CMPQ R15, $0x10
CMPQ R15, $16
JB bytes_between_0_and_15 JB bytes_between_0_and_15
loop: loop:
POLY1305_ADD(SI, R8, R9, R10) ADDQ (SI), R8
ADCQ 8(SI), R9
ADCQ $0x01, R10
LEAQ 16(SI), SI
multiply: multiply:
POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14) MOVQ R11, AX
SUBQ $16, R15 MULQ R8
CMPQ R15, $16 MOVQ AX, BX
JAE loop MOVQ DX, CX
MOVQ R11, AX
MULQ R9
ADDQ AX, CX
ADCQ $0x00, DX
MOVQ R11, R13
IMULQ R10, R13
ADDQ DX, R13
MOVQ R12, AX
MULQ R8
ADDQ AX, CX
ADCQ $0x00, DX
MOVQ DX, R8
MOVQ R12, R14
IMULQ R10, R14
MOVQ R12, AX
MULQ R9
ADDQ AX, R13
ADCQ DX, R14
ADDQ R8, R13
ADCQ $0x00, R14
MOVQ BX, R8
MOVQ CX, R9
MOVQ R13, R10
ANDQ $0x03, R10
MOVQ R13, BX
ANDQ $-4, BX
ADDQ BX, R8
ADCQ R14, R9
ADCQ $0x00, R10
SHRQ $0x02, R14, R13
SHRQ $0x02, R14
ADDQ R13, R8
ADCQ R14, R9
ADCQ $0x00, R10
SUBQ $0x10, R15
CMPQ R15, $0x10
JAE loop
bytes_between_0_and_15: bytes_between_0_and_15:
TESTQ R15, R15 TESTQ R15, R15
JZ done JZ done
MOVQ $1, BX MOVQ $0x00000001, BX
XORQ CX, CX XORQ CX, CX
XORQ R13, R13 XORQ R13, R13
ADDQ R15, SI ADDQ R15, SI
flush_buffer: flush_buffer:
SHLQ $8, BX, CX SHLQ $0x08, BX, CX
SHLQ $8, BX SHLQ $0x08, BX
MOVB -1(SI), R13 MOVB -1(SI), R13
XORQ R13, BX XORQ R13, BX
DECQ SI DECQ SI
DECQ R15 DECQ R15
JNZ flush_buffer JNZ flush_buffer
ADDQ BX, R8 ADDQ BX, R8
ADCQ CX, R9 ADCQ CX, R9
ADCQ $0, R10 ADCQ $0x00, R10
MOVQ $16, R15 MOVQ $0x00000010, R15
JMP multiply JMP multiply
done: done:
MOVQ R8, 0(DI) MOVQ R8, (DI)
MOVQ R9, 8(DI) MOVQ R9, 8(DI)
MOVQ R10, 16(DI) MOVQ R10, 16(DI)
RET RET

View File

@ -7,7 +7,10 @@
package poly1305 package poly1305
import "encoding/binary" import (
"encoding/binary"
"math/bits"
)
// Poly1305 [RFC 7539] is a relatively simple algorithm: the authentication tag // Poly1305 [RFC 7539] is a relatively simple algorithm: the authentication tag
// for a 64 bytes message is approximately // for a 64 bytes message is approximately
@ -114,13 +117,13 @@ type uint128 struct {
} }
func mul64(a, b uint64) uint128 { func mul64(a, b uint64) uint128 {
hi, lo := bitsMul64(a, b) hi, lo := bits.Mul64(a, b)
return uint128{lo, hi} return uint128{lo, hi}
} }
func add128(a, b uint128) uint128 { func add128(a, b uint128) uint128 {
lo, c := bitsAdd64(a.lo, b.lo, 0) lo, c := bits.Add64(a.lo, b.lo, 0)
hi, c := bitsAdd64(a.hi, b.hi, c) hi, c := bits.Add64(a.hi, b.hi, c)
if c != 0 { if c != 0 {
panic("poly1305: unexpected overflow") panic("poly1305: unexpected overflow")
} }
@ -155,8 +158,8 @@ func updateGeneric(state *macState, msg []byte) {
// hide leading zeroes. For full chunks, that's 1 << 128, so we can just // hide leading zeroes. For full chunks, that's 1 << 128, so we can just
// add 1 to the most significant (2¹²⁸) limb, h2. // add 1 to the most significant (2¹²⁸) limb, h2.
if len(msg) >= TagSize { if len(msg) >= TagSize {
h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0) h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0)
h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(msg[8:16]), c) h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(msg[8:16]), c)
h2 += c + 1 h2 += c + 1
msg = msg[TagSize:] msg = msg[TagSize:]
@ -165,8 +168,8 @@ func updateGeneric(state *macState, msg []byte) {
copy(buf[:], msg) copy(buf[:], msg)
buf[len(msg)] = 1 buf[len(msg)] = 1
h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0) h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0)
h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(buf[8:16]), c) h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(buf[8:16]), c)
h2 += c h2 += c
msg = nil msg = nil
@ -219,9 +222,9 @@ func updateGeneric(state *macState, msg []byte) {
m3 := h2r1 m3 := h2r1
t0 := m0.lo t0 := m0.lo
t1, c := bitsAdd64(m1.lo, m0.hi, 0) t1, c := bits.Add64(m1.lo, m0.hi, 0)
t2, c := bitsAdd64(m2.lo, m1.hi, c) t2, c := bits.Add64(m2.lo, m1.hi, c)
t3, _ := bitsAdd64(m3.lo, m2.hi, c) t3, _ := bits.Add64(m3.lo, m2.hi, c)
// Now we have the result as 4 64-bit limbs, and we need to reduce it // Now we have the result as 4 64-bit limbs, and we need to reduce it
// modulo 2¹³⁰ - 5. The special shape of this Crandall prime lets us do // modulo 2¹³⁰ - 5. The special shape of this Crandall prime lets us do
@ -243,14 +246,14 @@ func updateGeneric(state *macState, msg []byte) {
// To add c * 5 to h, we first add cc = c * 4, and then add (cc >> 2) = c. // To add c * 5 to h, we first add cc = c * 4, and then add (cc >> 2) = c.
h0, c = bitsAdd64(h0, cc.lo, 0) h0, c = bits.Add64(h0, cc.lo, 0)
h1, c = bitsAdd64(h1, cc.hi, c) h1, c = bits.Add64(h1, cc.hi, c)
h2 += c h2 += c
cc = shiftRightBy2(cc) cc = shiftRightBy2(cc)
h0, c = bitsAdd64(h0, cc.lo, 0) h0, c = bits.Add64(h0, cc.lo, 0)
h1, c = bitsAdd64(h1, cc.hi, c) h1, c = bits.Add64(h1, cc.hi, c)
h2 += c h2 += c
// h2 is at most 3 + 1 + 1 = 5, making the whole of h at most // h2 is at most 3 + 1 + 1 = 5, making the whole of h at most
@ -287,9 +290,9 @@ func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) {
// in constant time, we compute t = h - (2¹³⁰ - 5), and select h as the // in constant time, we compute t = h - (2¹³⁰ - 5), and select h as the
// result if the subtraction underflows, and t otherwise. // result if the subtraction underflows, and t otherwise.
hMinusP0, b := bitsSub64(h0, p0, 0) hMinusP0, b := bits.Sub64(h0, p0, 0)
hMinusP1, b := bitsSub64(h1, p1, b) hMinusP1, b := bits.Sub64(h1, p1, b)
_, b = bitsSub64(h2, p2, b) _, b = bits.Sub64(h2, p2, b)
// h = h if h < p else h - p // h = h if h < p else h - p
h0 = select64(b, h0, hMinusP0) h0 = select64(b, h0, hMinusP0)
@ -301,8 +304,8 @@ func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) {
// //
// by just doing a wide addition with the 128 low bits of h and discarding // by just doing a wide addition with the 128 low bits of h and discarding
// the overflow. // the overflow.
h0, c := bitsAdd64(h0, s[0], 0) h0, c := bits.Add64(h0, s[0], 0)
h1, _ = bitsAdd64(h1, s[1], c) h1, _ = bits.Add64(h1, s[1], c)
binary.LittleEndian.PutUint64(out[0:8], h0) binary.LittleEndian.PutUint64(out[0:8], h0)
binary.LittleEndian.PutUint64(out[8:16], h1) binary.LittleEndian.PutUint64(out[8:16], h1)

View File

@ -2,8 +2,7 @@
// 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.
//go:build gc && !purego //go:build gc && !purego && (ppc64 || ppc64le)
// +build gc,!purego
package poly1305 package poly1305

View File

@ -2,16 +2,25 @@
// 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.
//go:build gc && !purego //go:build gc && !purego && (ppc64 || ppc64le)
// +build gc,!purego
#include "textflag.h" #include "textflag.h"
// This was ported from the amd64 implementation. // This was ported from the amd64 implementation.
#ifdef GOARCH_ppc64le
#define LE_MOVD MOVD
#define LE_MOVWZ MOVWZ
#define LE_MOVHZ MOVHZ
#else
#define LE_MOVD MOVDBR
#define LE_MOVWZ MOVWBR
#define LE_MOVHZ MOVHBR
#endif
#define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \ #define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \
MOVD (msg), t0; \ LE_MOVD (msg)( R0), t0; \
MOVD 8(msg), t1; \ LE_MOVD (msg)(R24), t1; \
MOVD $1, t2; \ MOVD $1, t2; \
ADDC t0, h0, h0; \ ADDC t0, h0, h0; \
ADDE t1, h1, h1; \ ADDE t1, h1, h1; \
@ -20,15 +29,14 @@
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3, t4, t5) \ #define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3, t4, t5) \
MULLD r0, h0, t0; \ MULLD r0, h0, t0; \
MULLD r0, h1, t4; \
MULHDU r0, h0, t1; \ MULHDU r0, h0, t1; \
MULLD r0, h1, t4; \
MULHDU r0, h1, t5; \ MULHDU r0, h1, t5; \
ADDC t4, t1, t1; \ ADDC t4, t1, t1; \
MULLD r0, h2, t2; \ MULLD r0, h2, t2; \
ADDZE t5; \
MULHDU r1, h0, t4; \ MULHDU r1, h0, t4; \
MULLD r1, h0, h0; \ MULLD r1, h0, h0; \
ADD t5, t2, t2; \ ADDE t5, t2, t2; \
ADDC h0, t1, t1; \ ADDC h0, t1, t1; \
MULLD h2, r1, t3; \ MULLD h2, r1, t3; \
ADDZE t4, h0; \ ADDZE t4, h0; \
@ -38,13 +46,11 @@
ADDE t5, t3, t3; \ ADDE t5, t3, t3; \
ADDC h0, t2, t2; \ ADDC h0, t2, t2; \
MOVD $-4, t4; \ MOVD $-4, t4; \
MOVD t0, h0; \
MOVD t1, h1; \
ADDZE t3; \ ADDZE t3; \
ANDCC $3, t2, h2; \ RLDICL $0, t2, $62, h2; \
AND t2, t4, t0; \ AND t2, t4, h0; \
ADDC t0, h0, h0; \ ADDC t0, h0, h0; \
ADDE t3, h1, h1; \ ADDE t3, t1, h1; \
SLD $62, t3, t4; \ SLD $62, t3, t4; \
SRD $2, t2; \ SRD $2, t2; \
ADDZE h2; \ ADDZE h2; \
@ -54,10 +60,6 @@
ADDE t3, h1, h1; \ ADDE t3, h1, h1; \
ADDZE h2 ADDZE h2
DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
GLOBL ·poly1305Mask<>(SB), RODATA, $16
// func update(state *[7]uint64, msg []byte) // func update(state *[7]uint64, msg []byte)
TEXT ·update(SB), $0-32 TEXT ·update(SB), $0-32
MOVD state+0(FP), R3 MOVD state+0(FP), R3
@ -70,12 +72,15 @@ TEXT ·update(SB), $0-32
MOVD 24(R3), R11 // r0 MOVD 24(R3), R11 // r0
MOVD 32(R3), R12 // r1 MOVD 32(R3), R12 // r1
MOVD $8, R24
CMP R5, $16 CMP R5, $16
BLT bytes_between_0_and_15 BLT bytes_between_0_and_15
loop: loop:
POLY1305_ADD(R4, R8, R9, R10, R20, R21, R22) POLY1305_ADD(R4, R8, R9, R10, R20, R21, R22)
PCALIGN $16
multiply: multiply:
POLY1305_MUL(R8, R9, R10, R11, R12, R16, R17, R18, R14, R20, R21) POLY1305_MUL(R8, R9, R10, R11, R12, R16, R17, R18, R14, R20, R21)
ADD $-16, R5 ADD $-16, R5
@ -97,7 +102,7 @@ flush_buffer:
// Greater than 8 -- load the rightmost remaining bytes in msg // Greater than 8 -- load the rightmost remaining bytes in msg
// and put into R17 (h1) // and put into R17 (h1)
MOVD (R4)(R21), R17 LE_MOVD (R4)(R21), R17
MOVD $16, R22 MOVD $16, R22
// Find the offset to those bytes // Find the offset to those bytes
@ -121,7 +126,7 @@ just1:
BLT less8 BLT less8
// Exactly 8 // Exactly 8
MOVD (R4), R16 LE_MOVD (R4), R16
CMP R17, $0 CMP R17, $0
@ -136,7 +141,7 @@ less8:
MOVD $0, R22 // shift count MOVD $0, R22 // shift count
CMP R5, $4 CMP R5, $4
BLT less4 BLT less4
MOVWZ (R4), R16 LE_MOVWZ (R4), R16
ADD $4, R4 ADD $4, R4
ADD $-4, R5 ADD $-4, R5
MOVD $32, R22 MOVD $32, R22
@ -144,7 +149,7 @@ less8:
less4: less4:
CMP R5, $2 CMP R5, $2
BLT less2 BLT less2
MOVHZ (R4), R21 LE_MOVHZ (R4), R21
SLD R22, R21, R21 SLD R22, R21, R21
OR R16, R21, R16 OR R16, R21, R16
ADD $16, R22 ADD $16, R22

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc && !purego //go:build gc && !purego
// +build gc,!purego
package poly1305 package poly1305

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc && !purego //go:build gc && !purego
// +build gc,!purego
#include "textflag.h" #include "textflag.h"

View File

@ -10,14 +10,15 @@
// for their specific task. If you are required to interoperate with OpenPGP // for their specific task. If you are required to interoperate with OpenPGP
// systems and need a maintained package, consider a community fork. // systems and need a maintained package, consider a community fork.
// See https://golang.org/issue/44226. // See https://golang.org/issue/44226.
package armor // import "golang.org/x/crypto/openpgp/armor" package armor
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"golang.org/x/crypto/openpgp/errors"
"io" "io"
"golang.org/x/crypto/openpgp/errors"
) )
// A Block represents an OpenPGP armored structure. // A Block represents an OpenPGP armored structure.
@ -156,7 +157,7 @@ func (r *openpgpReader) Read(p []byte) (n int, err error) {
n, err = r.b64Reader.Read(p) n, err = r.b64Reader.Read(p)
r.currentCRC = crc24(r.currentCRC, p[:n]) r.currentCRC = crc24(r.currentCRC, p[:n])
if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) { if err == io.EOF && r.lReader.crcSet && r.lReader.crc != r.currentCRC&crc24Mask {
return 0, ArmorCorrupt return 0, ArmorCorrupt
} }

View File

@ -16,7 +16,7 @@
// https://golang.org/issue/44226), and ElGamal in the OpenPGP ecosystem has // https://golang.org/issue/44226), and ElGamal in the OpenPGP ecosystem has
// compatibility and security issues (see https://eprint.iacr.org/2021/923). // compatibility and security issues (see https://eprint.iacr.org/2021/923).
// Moreover, this package doesn't protect against side-channel attacks. // Moreover, this package doesn't protect against side-channel attacks.
package elgamal // import "golang.org/x/crypto/openpgp/elgamal" package elgamal
import ( import (
"crypto/rand" "crypto/rand"

View File

@ -9,7 +9,7 @@
// for their specific task. If you are required to interoperate with OpenPGP // for their specific task. If you are required to interoperate with OpenPGP
// systems and need a maintained package, consider a community fork. // systems and need a maintained package, consider a community fork.
// See https://golang.org/issue/44226. // See https://golang.org/issue/44226.
package errors // import "golang.org/x/crypto/openpgp/errors" package errors
import ( import (
"strconv" "strconv"

View File

@ -61,7 +61,7 @@ type Key struct {
type KeyRing interface { type KeyRing interface {
// KeysById returns the set of keys that have the given key id. // KeysById returns the set of keys that have the given key id.
KeysById(id uint64) []Key KeysById(id uint64) []Key
// KeysByIdAndUsage returns the set of keys with the given id // KeysByIdUsage returns the set of keys with the given id
// that also meet the key usage given by requiredUsage. // that also meet the key usage given by requiredUsage.
// The requiredUsage is expressed as the bitwise-OR of // The requiredUsage is expressed as the bitwise-OR of
// packet.KeyFlag* values. // packet.KeyFlag* values.
@ -183,7 +183,7 @@ func (el EntityList) KeysById(id uint64) (keys []Key) {
return return
} }
// KeysByIdAndUsage returns the set of keys with the given id that also meet // KeysByIdUsage returns the set of keys with the given id that also meet
// the key usage given by requiredUsage. The requiredUsage is expressed as // the key usage given by requiredUsage. The requiredUsage is expressed as
// the bitwise-OR of packet.KeyFlag* values. // the bitwise-OR of packet.KeyFlag* values.
func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) { func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) {

View File

@ -60,7 +60,7 @@ func (c *Compressed) parse(r io.Reader) error {
return err return err
} }
// compressedWriterCloser represents the serialized compression stream // compressedWriteCloser represents the serialized compression stream
// header and the compressor. Its Close() method ensures that both the // header and the compressor. Its Close() method ensures that both the
// compressor and serialized stream header are closed. Its Write() // compressor and serialized stream header are closed. Its Write()
// method writes to the compressor. // method writes to the compressor.

View File

@ -7,7 +7,6 @@ package packet
import ( import (
"bytes" "bytes"
"io" "io"
"io/ioutil"
"golang.org/x/crypto/openpgp/errors" "golang.org/x/crypto/openpgp/errors"
) )
@ -26,7 +25,7 @@ type OpaquePacket struct {
} }
func (op *OpaquePacket) parse(r io.Reader) (err error) { func (op *OpaquePacket) parse(r io.Reader) (err error) {
op.Contents, err = ioutil.ReadAll(r) op.Contents, err = io.ReadAll(r)
return return
} }

View File

@ -10,7 +10,7 @@
// for their specific task. If you are required to interoperate with OpenPGP // for their specific task. If you are required to interoperate with OpenPGP
// systems and need a maintained package, consider a community fork. // systems and need a maintained package, consider a community fork.
// See https://golang.org/issue/44226. // See https://golang.org/issue/44226.
package packet // import "golang.org/x/crypto/openpgp/packet" package packet
import ( import (
"bufio" "bufio"

View File

@ -13,7 +13,6 @@ import (
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1"
"io" "io"
"io/ioutil"
"math/big" "math/big"
"strconv" "strconv"
"time" "time"
@ -133,7 +132,7 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
} }
} }
pk.encryptedData, err = ioutil.ReadAll(r) pk.encryptedData, err = io.ReadAll(r)
if err != nil { if err != nil {
return return
} }

View File

@ -236,7 +236,7 @@ func (w *seMDCWriter) Close() (err error) {
return w.w.Close() return w.w.Close()
} }
// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. // noOpCloser is like an io.NopCloser, but for an io.Writer.
type noOpCloser struct { type noOpCloser struct {
w io.Writer w io.Writer
} }

View File

@ -9,7 +9,6 @@ import (
"image" "image"
"image/jpeg" "image/jpeg"
"io" "io"
"io/ioutil"
) )
const UserAttrImageSubpacket = 1 const UserAttrImageSubpacket = 1
@ -56,7 +55,7 @@ func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute {
func (uat *UserAttribute) parse(r io.Reader) (err error) { func (uat *UserAttribute) parse(r io.Reader) (err error) {
// RFC 4880, section 5.13 // RFC 4880, section 5.13
b, err := ioutil.ReadAll(r) b, err := io.ReadAll(r)
if err != nil { if err != nil {
return return
} }

View File

@ -6,7 +6,6 @@ package packet
import ( import (
"io" "io"
"io/ioutil"
"strings" "strings"
) )
@ -66,7 +65,7 @@ func NewUserId(name, comment, email string) *UserId {
func (uid *UserId) parse(r io.Reader) (err error) { func (uid *UserId) parse(r io.Reader) (err error) {
// RFC 4880, section 5.11 // RFC 4880, section 5.11
b, err := ioutil.ReadAll(r) b, err := io.ReadAll(r)
if err != nil { if err != nil {
return return
} }

View File

@ -9,7 +9,7 @@
// for their specific task. If you are required to interoperate with OpenPGP // for their specific task. If you are required to interoperate with OpenPGP
// systems and need a maintained package, consider a community fork. // systems and need a maintained package, consider a community fork.
// See https://golang.org/issue/44226. // See https://golang.org/issue/44226.
package openpgp // import "golang.org/x/crypto/openpgp" package openpgp
import ( import (
"crypto" "crypto"

View File

@ -10,7 +10,7 @@
// for their specific task. If you are required to interoperate with OpenPGP // for their specific task. If you are required to interoperate with OpenPGP
// systems and need a maintained package, consider a community fork. // systems and need a maintained package, consider a community fork.
// See https://golang.org/issue/44226. // See https://golang.org/issue/44226.
package s2k // import "golang.org/x/crypto/openpgp/s2k" package s2k
import ( import (
"crypto" "crypto"
@ -268,7 +268,7 @@ func HashIdToString(id byte) (name string, ok bool) {
return "", false return "", false
} }
// HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. // HashToHashId returns an OpenPGP hash id which corresponds the given Hash.
func HashToHashId(h crypto.Hash) (id byte, ok bool) { func HashToHashId(h crypto.Hash) (id byte, ok bool) {
for _, m := range hashToHashIdMapping { for _, m := range hashToHashIdMapping {
if m.hash == h { if m.hash == h {

View File

@ -402,7 +402,7 @@ func (s signatureWriter) Close() error {
return s.encryptedData.Close() return s.encryptedData.Close()
} }
// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. // noOpCloser is like an io.NopCloser, but for an io.Writer.
// TODO: we have two of these in OpenPGP packages alone. This probably needs // TODO: we have two of these in OpenPGP packages alone. This probably needs
// to be promoted somewhere more common. // to be promoted somewhere more common.
type noOpCloser struct { type noOpCloser struct {

View File

@ -10,12 +10,13 @@
// References: // References:
// //
// [PROTOCOL.agent]: https://tools.ietf.org/html/draft-miller-ssh-agent-00 // [PROTOCOL.agent]: https://tools.ietf.org/html/draft-miller-ssh-agent-00
package agent // import "golang.org/x/crypto/ssh/agent" package agent
import ( import (
"bytes" "bytes"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"encoding/base64" "encoding/base64"
@ -26,7 +27,6 @@ import (
"math/big" "math/big"
"sync" "sync"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
@ -93,7 +93,7 @@ type ExtendedAgent interface {
type ConstraintExtension struct { type ConstraintExtension struct {
// ExtensionName consist of a UTF-8 string suffixed by the // ExtensionName consist of a UTF-8 string suffixed by the
// implementation domain following the naming scheme defined // implementation domain following the naming scheme defined
// in Section 4.2 of [RFC4251], e.g. "foo@example.com". // in Section 4.2 of RFC 4251, e.g. "foo@example.com".
ExtensionName string ExtensionName string
// ExtensionDetails contains the actual content of the extended // ExtensionDetails contains the actual content of the extended
// constraint. // constraint.
@ -141,9 +141,14 @@ const (
agentAddSmartcardKeyConstrained = 26 agentAddSmartcardKeyConstrained = 26
// 3.7 Key constraint identifiers // 3.7 Key constraint identifiers
agentConstrainLifetime = 1 agentConstrainLifetime = 1
agentConstrainConfirm = 2 agentConstrainConfirm = 2
agentConstrainExtension = 3 // Constraint extension identifier up to version 2 of the protocol. A
// backward incompatible change will be required if we want to add support
// for SSH_AGENT_CONSTRAIN_MAXSIGN which uses the same ID.
agentConstrainExtensionV00 = 3
// Constraint extension identifier in version 3 and later of the protocol.
agentConstrainExtension = 255
) )
// maxAgentResponseBytes is the maximum agent reply size that is accepted. This // maxAgentResponseBytes is the maximum agent reply size that is accepted. This
@ -205,7 +210,7 @@ type constrainLifetimeAgentMsg struct {
} }
type constrainExtensionAgentMsg struct { type constrainExtensionAgentMsg struct {
ExtensionName string `sshtype:"3"` ExtensionName string `sshtype:"255|3"`
ExtensionDetails []byte ExtensionDetails []byte
// Rest is a field used for parsing, not part of message // Rest is a field used for parsing, not part of message
@ -226,7 +231,9 @@ var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
type extensionAgentMsg struct { type extensionAgentMsg struct {
ExtensionType string `sshtype:"27"` ExtensionType string `sshtype:"27"`
Contents []byte // NOTE: this matches OpenSSH's PROTOCOL.agent, not the IETF draft [PROTOCOL.agent],
// so that it matches what OpenSSH actually implements in the wild.
Contents []byte `ssh:"rest"`
} }
// Key represents a protocol 2 public key as defined in // Key represents a protocol 2 public key as defined in
@ -729,7 +736,7 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string
if err != nil { if err != nil {
return err return err
} }
if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { if !bytes.Equal(cert.Key.Marshal(), signer.PublicKey().Marshal()) {
return errors.New("agent: signer and cert have different public key") return errors.New("agent: signer and cert have different public key")
} }

View File

@ -175,6 +175,15 @@ func (r *keyring) Add(key AddedKey) error {
p.expire = &t p.expire = &t
} }
// If we already have a Signer with the same public key, replace it with the
// new one.
for idx, k := range r.keys {
if bytes.Equal(k.signer.PublicKey().Marshal(), p.signer.PublicKey().Marshal()) {
r.keys[idx] = p
return nil
}
}
r.keys = append(r.keys, p) r.keys = append(r.keys, p)
return nil return nil

View File

@ -7,6 +7,7 @@ package agent
import ( import (
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"encoding/binary" "encoding/binary"
@ -16,11 +17,10 @@ import (
"log" "log"
"math/big" "math/big"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
// Server wraps an Agent and uses it to implement the agent side of // server wraps an Agent and uses it to implement the agent side of
// the SSH-agent, wire protocol. // the SSH-agent, wire protocol.
type server struct { type server struct {
agent Agent agent Agent
@ -208,7 +208,7 @@ func parseConstraints(constraints []byte) (lifetimeSecs uint32, confirmBeforeUse
case agentConstrainConfirm: case agentConstrainConfirm:
confirmBeforeUse = true confirmBeforeUse = true
constraints = constraints[1:] constraints = constraints[1:]
case agentConstrainExtension: case agentConstrainExtension, agentConstrainExtensionV00:
var msg constrainExtensionAgentMsg var msg constrainExtensionAgentMsg
if err = ssh.Unmarshal(constraints, &msg); err != nil { if err = ssh.Unmarshal(constraints, &msg); err != nil {
return 0, false, nil, err return 0, false, nil, err

View File

@ -16,8 +16,9 @@ import (
// Certificate algorithm names from [PROTOCOL.certkeys]. These values can appear // Certificate algorithm names from [PROTOCOL.certkeys]. These values can appear
// in Certificate.Type, PublicKey.Type, and ClientConfig.HostKeyAlgorithms. // in Certificate.Type, PublicKey.Type, and ClientConfig.HostKeyAlgorithms.
// Unlike key algorithm names, these are not passed to AlgorithmSigner and don't // Unlike key algorithm names, these are not passed to AlgorithmSigner nor
// appear in the Signature.Format field. // returned by MultiAlgorithmSigner and don't appear in the Signature.Format
// field.
const ( const (
CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com" CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com" CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
@ -251,14 +252,21 @@ type algorithmOpenSSHCertSigner struct {
// private key is held by signer. It returns an error if the public key in cert // private key is held by signer. It returns an error if the public key in cert
// doesn't match the key used by signer. // doesn't match the key used by signer.
func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) { func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) {
if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { if !bytes.Equal(cert.Key.Marshal(), signer.PublicKey().Marshal()) {
return nil, errors.New("ssh: signer and cert have different public key") return nil, errors.New("ssh: signer and cert have different public key")
} }
if algorithmSigner, ok := signer.(AlgorithmSigner); ok { switch s := signer.(type) {
case MultiAlgorithmSigner:
return &multiAlgorithmSigner{
AlgorithmSigner: &algorithmOpenSSHCertSigner{
&openSSHCertSigner{cert, signer}, s},
supportedAlgorithms: s.Algorithms(),
}, nil
case AlgorithmSigner:
return &algorithmOpenSSHCertSigner{ return &algorithmOpenSSHCertSigner{
&openSSHCertSigner{cert, signer}, algorithmSigner}, nil &openSSHCertSigner{cert, signer}, s}, nil
} else { default:
return &openSSHCertSigner{cert, signer}, nil return &openSSHCertSigner{cert, signer}, nil
} }
} }
@ -432,7 +440,9 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
} }
// SignCert signs the certificate with an authority, setting the Nonce, // SignCert signs the certificate with an authority, setting the Nonce,
// SignatureKey, and Signature fields. // SignatureKey, and Signature fields. If the authority implements the
// MultiAlgorithmSigner interface the first algorithm in the list is used. This
// is useful if you want to sign with a specific algorithm.
func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
c.Nonce = make([]byte, 32) c.Nonce = make([]byte, 32)
if _, err := io.ReadFull(rand, c.Nonce); err != nil { if _, err := io.ReadFull(rand, c.Nonce); err != nil {
@ -440,8 +450,20 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
} }
c.SignatureKey = authority.PublicKey() c.SignatureKey = authority.PublicKey()
// Default to KeyAlgoRSASHA512 for ssh-rsa signers. if v, ok := authority.(MultiAlgorithmSigner); ok {
if v, ok := authority.(AlgorithmSigner); ok && v.PublicKey().Type() == KeyAlgoRSA { if len(v.Algorithms()) == 0 {
return errors.New("the provided authority has no signature algorithm")
}
// Use the first algorithm in the list.
sig, err := v.SignWithAlgorithm(rand, c.bytesForSigning(), v.Algorithms()[0])
if err != nil {
return err
}
c.Signature = sig
return nil
} else if v, ok := authority.(AlgorithmSigner); ok && v.PublicKey().Type() == KeyAlgoRSA {
// Default to KeyAlgoRSASHA512 for ssh-rsa signers.
// TODO: consider using KeyAlgoRSASHA256 as default.
sig, err := v.SignWithAlgorithm(rand, c.bytesForSigning(), KeyAlgoRSASHA512) sig, err := v.SignWithAlgorithm(rand, c.bytesForSigning(), KeyAlgoRSASHA512)
if err != nil { if err != nil {
return err return err

View File

@ -187,9 +187,11 @@ type channel struct {
pending *buffer pending *buffer
extPending *buffer extPending *buffer
// windowMu protects myWindow, the flow-control window. // windowMu protects myWindow, the flow-control window, and myConsumed,
windowMu sync.Mutex // the number of bytes consumed since we last increased myWindow
myWindow uint32 windowMu sync.Mutex
myWindow uint32
myConsumed uint32
// writeMu serializes calls to mux.conn.writePacket() and // writeMu serializes calls to mux.conn.writePacket() and
// protects sentClose and packetPool. This mutex must be // protects sentClose and packetPool. This mutex must be
@ -332,14 +334,24 @@ func (ch *channel) handleData(packet []byte) error {
return nil return nil
} }
func (c *channel) adjustWindow(n uint32) error { func (c *channel) adjustWindow(adj uint32) error {
c.windowMu.Lock() c.windowMu.Lock()
// Since myWindow is managed on our side, and can never exceed // Since myConsumed and myWindow are managed on our side, and can never
// the initial window setting, we don't worry about overflow. // exceed the initial window setting, we don't worry about overflow.
c.myWindow += uint32(n) c.myConsumed += adj
var sendAdj uint32
if (channelWindowSize-c.myWindow > 3*c.maxIncomingPayload) ||
(c.myWindow < channelWindowSize/2) {
sendAdj = c.myConsumed
c.myConsumed = 0
c.myWindow += sendAdj
}
c.windowMu.Unlock() c.windowMu.Unlock()
if sendAdj == 0 {
return nil
}
return c.sendMessage(windowAdjustMsg{ return c.sendMessage(windowAdjustMsg{
AdditionalBytes: uint32(n), AdditionalBytes: sendAdj,
}) })
} }

View File

@ -15,7 +15,6 @@ import (
"fmt" "fmt"
"hash" "hash"
"io" "io"
"io/ioutil"
"golang.org/x/crypto/chacha20" "golang.org/x/crypto/chacha20"
"golang.org/x/crypto/internal/poly1305" "golang.org/x/crypto/internal/poly1305"
@ -97,13 +96,13 @@ func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream,
// are not supported and will not be negotiated, even if explicitly requested in // are not supported and will not be negotiated, even if explicitly requested in
// ClientConfig.Crypto.Ciphers. // ClientConfig.Crypto.Ciphers.
var cipherModes = map[string]*cipherMode{ var cipherModes = map[string]*cipherMode{
// Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms // Ciphers from RFC 4344, which introduced many CTR-based ciphers. Algorithms
// are defined in the order specified in the RFC. // are defined in the order specified in the RFC.
"aes128-ctr": {16, aes.BlockSize, streamCipherMode(0, newAESCTR)}, "aes128-ctr": {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
"aes192-ctr": {24, aes.BlockSize, streamCipherMode(0, newAESCTR)}, "aes192-ctr": {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
"aes256-ctr": {32, aes.BlockSize, streamCipherMode(0, newAESCTR)}, "aes256-ctr": {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
// Ciphers from RFC4345, which introduces security-improved arcfour ciphers. // Ciphers from RFC 4345, which introduces security-improved arcfour ciphers.
// They are defined in the order specified in the RFC. // They are defined in the order specified in the RFC.
"arcfour128": {16, 0, streamCipherMode(1536, newRC4)}, "arcfour128": {16, 0, streamCipherMode(1536, newRC4)},
"arcfour256": {32, 0, streamCipherMode(1536, newRC4)}, "arcfour256": {32, 0, streamCipherMode(1536, newRC4)},
@ -111,11 +110,12 @@ var cipherModes = map[string]*cipherMode{
// Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol. // Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol.
// Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and // Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and
// RC4) has problems with weak keys, and should be used with caution." // RC4) has problems with weak keys, and should be used with caution."
// RFC4345 introduces improved versions of Arcfour. // RFC 4345 introduces improved versions of Arcfour.
"arcfour": {16, 0, streamCipherMode(0, newRC4)}, "arcfour": {16, 0, streamCipherMode(0, newRC4)},
// AEAD ciphers // AEAD ciphers
gcmCipherID: {16, 12, newGCMCipher}, gcm128CipherID: {16, 12, newGCMCipher},
gcm256CipherID: {32, 12, newGCMCipher},
chacha20Poly1305ID: {64, 0, newChaCha20Cipher}, chacha20Poly1305ID: {64, 0, newChaCha20Cipher},
// CBC mode is insecure and so is not included in the default config. // CBC mode is insecure and so is not included in the default config.
@ -497,7 +497,7 @@ func (c *cbcCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error)
// data, to make distinguishing between // data, to make distinguishing between
// failing MAC and failing length check more // failing MAC and failing length check more
// difficult. // difficult.
io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage)) io.CopyN(io.Discard, r, int64(c.oracleCamouflage))
} }
} }
return p, err return p, err
@ -642,7 +642,7 @@ const chacha20Poly1305ID = "chacha20-poly1305@openssh.com"
// //
// https://tools.ietf.org/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00 // https://tools.ietf.org/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00
// //
// the methods here also implement padding, which RFC4253 Section 6 // the methods here also implement padding, which RFC 4253 Section 6
// also requires of stream ciphers. // also requires of stream ciphers.
type chacha20Poly1305Cipher struct { type chacha20Poly1305Cipher struct {
lengthKey [32]byte lengthKey [32]byte

View File

@ -82,7 +82,7 @@ func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan
if err := conn.clientHandshake(addr, &fullConf); err != nil { if err := conn.clientHandshake(addr, &fullConf); err != nil {
c.Close() c.Close()
return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err) return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %w", err)
} }
conn.mux = newMux(conn.transport) conn.mux = newMux(conn.transport)
return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil

View File

@ -71,7 +71,13 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
for auth := AuthMethod(new(noneAuth)); auth != nil; { for auth := AuthMethod(new(noneAuth)); auth != nil; {
ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions) ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions)
if err != nil { if err != nil {
return err // On disconnect, return error immediately
if _, ok := err.(*disconnectMsg); ok {
return err
}
// We return the error later if there is no other method left to
// try.
ok = authFailure
} }
if ok == authSuccess { if ok == authSuccess {
// success // success
@ -101,6 +107,12 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
} }
} }
} }
if auth == nil && err != nil {
// We have an error and there are no other authentication methods to
// try, so we return it.
return err
}
} }
return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried) return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried)
} }
@ -217,21 +229,45 @@ func (cb publicKeyCallback) method() string {
return "publickey" return "publickey"
} }
func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (as AlgorithmSigner, algo string) { func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (MultiAlgorithmSigner, string, error) {
var as MultiAlgorithmSigner
keyFormat := signer.PublicKey().Type() keyFormat := signer.PublicKey().Type()
// Like in sendKexInit, if the public key implements AlgorithmSigner we // If the signer implements MultiAlgorithmSigner we use the algorithms it
// assume it supports all algorithms, otherwise only the key format one. // support, if it implements AlgorithmSigner we assume it supports all
as, ok := signer.(AlgorithmSigner) // algorithms, otherwise only the key format one.
if !ok { switch s := signer.(type) {
return algorithmSignerWrapper{signer}, keyFormat case MultiAlgorithmSigner:
as = s
case AlgorithmSigner:
as = &multiAlgorithmSigner{
AlgorithmSigner: s,
supportedAlgorithms: algorithmsForKeyFormat(underlyingAlgo(keyFormat)),
}
default:
as = &multiAlgorithmSigner{
AlgorithmSigner: algorithmSignerWrapper{signer},
supportedAlgorithms: []string{underlyingAlgo(keyFormat)},
}
}
getFallbackAlgo := func() (string, error) {
// Fallback to use if there is no "server-sig-algs" extension or a
// common algorithm cannot be found. We use the public key format if the
// MultiAlgorithmSigner supports it, otherwise we return an error.
if !contains(as.Algorithms(), underlyingAlgo(keyFormat)) {
return "", fmt.Errorf("ssh: no common public key signature algorithm, server only supports %q for key type %q, signer only supports %v",
underlyingAlgo(keyFormat), keyFormat, as.Algorithms())
}
return keyFormat, nil
} }
extPayload, ok := extensions["server-sig-algs"] extPayload, ok := extensions["server-sig-algs"]
if !ok { if !ok {
// If there is no "server-sig-algs" extension, fall back to the key // If there is no "server-sig-algs" extension use the fallback
// format algorithm. // algorithm.
return as, keyFormat algo, err := getFallbackAlgo()
return as, algo, err
} }
// The server-sig-algs extension only carries underlying signature // The server-sig-algs extension only carries underlying signature
@ -245,15 +281,22 @@ func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (as Alg
} }
} }
keyAlgos := algorithmsForKeyFormat(keyFormat) // Filter algorithms based on those supported by MultiAlgorithmSigner.
var keyAlgos []string
for _, algo := range algorithmsForKeyFormat(keyFormat) {
if contains(as.Algorithms(), underlyingAlgo(algo)) {
keyAlgos = append(keyAlgos, algo)
}
}
algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos) algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos)
if err != nil { if err != nil {
// If there is no overlap, try the key anyway with the key format // If there is no overlap, return the fallback algorithm to support
// algorithm, to support servers that fail to list all supported // servers that fail to list all supported algorithms.
// algorithms. algo, err := getFallbackAlgo()
return as, keyFormat return as, algo, err
} }
return as, algo return as, algo, nil
} }
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) { func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) {
@ -267,14 +310,39 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
return authFailure, nil, err return authFailure, nil, err
} }
var methods []string var methods []string
for _, signer := range signers { var errSigAlgo error
pub := signer.PublicKey()
as, algo := pickSignatureAlgorithm(signer, extensions)
origSignersLen := len(signers)
for idx := 0; idx < len(signers); idx++ {
signer := signers[idx]
pub := signer.PublicKey()
as, algo, err := pickSignatureAlgorithm(signer, extensions)
if err != nil && errSigAlgo == nil {
// If we cannot negotiate a signature algorithm store the first
// error so we can return it to provide a more meaningful message if
// no other signers work.
errSigAlgo = err
continue
}
ok, err := validateKey(pub, algo, user, c) ok, err := validateKey(pub, algo, user, c)
if err != nil { if err != nil {
return authFailure, nil, err return authFailure, nil, err
} }
// OpenSSH 7.2-7.7 advertises support for rsa-sha2-256 and rsa-sha2-512
// in the "server-sig-algs" extension but doesn't support these
// algorithms for certificate authentication, so if the server rejects
// the key try to use the obtained algorithm as if "server-sig-algs" had
// not been implemented if supported from the algorithm signer.
if !ok && idx < origSignersLen && isRSACert(algo) && algo != CertAlgoRSAv01 {
if contains(as.Algorithms(), KeyAlgoRSA) {
// We retry using the compat algorithm after all signers have
// been tried normally.
signers = append(signers, &multiAlgorithmSigner{
AlgorithmSigner: as,
supportedAlgorithms: []string{KeyAlgoRSA},
})
}
}
if !ok { if !ok {
continue continue
} }
@ -317,22 +385,12 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
// contain the "publickey" method, do not attempt to authenticate with any // contain the "publickey" method, do not attempt to authenticate with any
// other keys. According to RFC 4252 Section 7, the latter can occur when // other keys. According to RFC 4252 Section 7, the latter can occur when
// additional authentication methods are required. // additional authentication methods are required.
if success == authSuccess || !containsMethod(methods, cb.method()) { if success == authSuccess || !contains(methods, cb.method()) {
return success, methods, err return success, methods, err
} }
} }
return authFailure, methods, nil return authFailure, methods, errSigAlgo
}
func containsMethod(methods []string, method string) bool {
for _, m := range methods {
if m == method {
return true
}
}
return false
} }
// validateKey validates the key provided is acceptable to the server. // validateKey validates the key provided is acceptable to the server.
@ -350,10 +408,10 @@ func validateKey(key PublicKey, algo string, user string, c packetConn) (bool, e
return false, err return false, err
} }
return confirmKeyAck(key, algo, c) return confirmKeyAck(key, c)
} }
func confirmKeyAck(key PublicKey, algo string, c packetConn) (bool, error) { func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
pubKey := key.Marshal() pubKey := key.Marshal()
for { for {
@ -371,7 +429,15 @@ func confirmKeyAck(key PublicKey, algo string, c packetConn) (bool, error) {
if err := Unmarshal(packet, &msg); err != nil { if err := Unmarshal(packet, &msg); err != nil {
return false, err return false, err
} }
if msg.Algo != algo || !bytes.Equal(msg.PubKey, pubKey) { // According to RFC 4252 Section 7 the algorithm in
// SSH_MSG_USERAUTH_PK_OK should match that of the request but some
// servers send the key type instead. OpenSSH allows any algorithm
// that matches the public key, so we do the same.
// https://github.com/openssh/openssh-portable/blob/86bdd385/sshconnect2.c#L709
if !contains(algorithmsForKeyFormat(key.Type()), msg.Algo) {
return false, nil
}
if !bytes.Equal(msg.PubKey, pubKey) {
return false, nil return false, nil
} }
return true, nil return true, nil
@ -489,6 +555,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
} }
gotMsgExtInfo := false gotMsgExtInfo := false
gotUserAuthInfoRequest := false
for { for {
packet, err := c.readPacket() packet, err := c.readPacket()
if err != nil { if err != nil {
@ -519,6 +586,9 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
if msg.PartialSuccess { if msg.PartialSuccess {
return authPartialSuccess, msg.Methods, nil return authPartialSuccess, msg.Methods, nil
} }
if !gotUserAuthInfoRequest {
return authFailure, msg.Methods, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
}
return authFailure, msg.Methods, nil return authFailure, msg.Methods, nil
case msgUserAuthSuccess: case msgUserAuthSuccess:
return authSuccess, nil, nil return authSuccess, nil, nil
@ -530,6 +600,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
if err := Unmarshal(packet, &msg); err != nil { if err := Unmarshal(packet, &msg); err != nil {
return authFailure, nil, err return authFailure, nil, err
} }
gotUserAuthInfoRequest = true
// Manually unpack the prompt/echo pairs. // Manually unpack the prompt/echo pairs.
rest := msg.Prompts rest := msg.Prompts

View File

@ -27,7 +27,7 @@ const (
// supportedCiphers lists ciphers we support but might not recommend. // supportedCiphers lists ciphers we support but might not recommend.
var supportedCiphers = []string{ var supportedCiphers = []string{
"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-ctr", "aes192-ctr", "aes256-ctr",
"aes128-gcm@openssh.com", "aes128-gcm@openssh.com", gcm256CipherID,
chacha20Poly1305ID, chacha20Poly1305ID,
"arcfour256", "arcfour128", "arcfour", "arcfour256", "arcfour128", "arcfour",
aes128cbcID, aes128cbcID,
@ -36,7 +36,7 @@ var supportedCiphers = []string{
// preferredCiphers specifies the default preference for ciphers. // preferredCiphers specifies the default preference for ciphers.
var preferredCiphers = []string{ var preferredCiphers = []string{
"aes128-gcm@openssh.com", "aes128-gcm@openssh.com", gcm256CipherID,
chacha20Poly1305ID, chacha20Poly1305ID,
"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-ctr", "aes192-ctr", "aes256-ctr",
} }
@ -48,7 +48,8 @@ var supportedKexAlgos = []string{
// P384 and P521 are not constant-time yet, but since we don't // P384 and P521 are not constant-time yet, but since we don't
// reuse ephemeral keys, using them for ECDH should be OK. // reuse ephemeral keys, using them for ECDH should be OK.
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
kexAlgoDH14SHA256, kexAlgoDH14SHA1, kexAlgoDH1SHA1, kexAlgoDH14SHA256, kexAlgoDH16SHA512, kexAlgoDH14SHA1,
kexAlgoDH1SHA1,
} }
// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden // serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
@ -58,8 +59,9 @@ var serverForbiddenKexAlgos = map[string]struct{}{
kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
} }
// preferredKexAlgos specifies the default preference for key-exchange algorithms // preferredKexAlgos specifies the default preference for key-exchange
// in preference order. // algorithms in preference order. The diffie-hellman-group16-sha512 algorithm
// is disabled by default because it is a bit slower than the others.
var preferredKexAlgos = []string{ var preferredKexAlgos = []string{
kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH, kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
@ -69,12 +71,12 @@ var preferredKexAlgos = []string{
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods // supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
// of authenticating servers) in preference order. // of authenticating servers) in preference order.
var supportedHostKeyAlgos = []string{ var supportedHostKeyAlgos = []string{
CertAlgoRSASHA512v01, CertAlgoRSASHA256v01, CertAlgoRSASHA256v01, CertAlgoRSASHA512v01,
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
KeyAlgoRSASHA512, KeyAlgoRSASHA256, KeyAlgoRSASHA256, KeyAlgoRSASHA512,
KeyAlgoRSA, KeyAlgoDSA, KeyAlgoRSA, KeyAlgoDSA,
KeyAlgoED25519, KeyAlgoED25519,
@ -84,7 +86,7 @@ var supportedHostKeyAlgos = []string{
// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed // This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
// because they have reached the end of their useful life. // because they have reached the end of their useful life.
var supportedMACs = []string{ var supportedMACs = []string{
"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", "hmac-sha1-96",
} }
var supportedCompressions = []string{compressionNone} var supportedCompressions = []string{compressionNone}
@ -118,6 +120,33 @@ func algorithmsForKeyFormat(keyFormat string) []string {
} }
} }
// isRSA returns whether algo is a supported RSA algorithm, including certificate
// algorithms.
func isRSA(algo string) bool {
algos := algorithmsForKeyFormat(KeyAlgoRSA)
return contains(algos, underlyingAlgo(algo))
}
func isRSACert(algo string) bool {
_, ok := certKeyAlgoNames[algo]
if !ok {
return false
}
return isRSA(algo)
}
// supportedPubKeyAuthAlgos specifies the supported client public key
// authentication algorithms. Note that this doesn't include certificate types
// since those use the underlying algorithm. This list is sent to the client if
// it supports the server-sig-algs extension. Order is irrelevant.
var supportedPubKeyAuthAlgos = []string{
KeyAlgoED25519,
KeyAlgoSKED25519, KeyAlgoSKECDSA256,
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA,
KeyAlgoDSA,
}
// unexpectedMessageError results when the SSH message that we received didn't // unexpectedMessageError results when the SSH message that we received didn't
// match what we wanted. // match what we wanted.
func unexpectedMessageError(expected, got uint8) error { func unexpectedMessageError(expected, got uint8) error {
@ -149,21 +178,22 @@ type directionAlgorithms struct {
// rekeyBytes returns a rekeying intervals in bytes. // rekeyBytes returns a rekeying intervals in bytes.
func (a *directionAlgorithms) rekeyBytes() int64 { func (a *directionAlgorithms) rekeyBytes() int64 {
// According to RFC4344 block ciphers should rekey after // According to RFC 4344 block ciphers should rekey after
// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is // 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
// 128. // 128.
switch a.Cipher { switch a.Cipher {
case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcmCipherID, aes128cbcID: case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcm128CipherID, gcm256CipherID, aes128cbcID:
return 16 * (1 << 32) return 16 * (1 << 32)
} }
// For others, stick with RFC4253 recommendation to rekey after 1 Gb of data. // For others, stick with RFC 4253 recommendation to rekey after 1 Gb of data.
return 1 << 30 return 1 << 30
} }
var aeadCiphers = map[string]bool{ var aeadCiphers = map[string]bool{
gcmCipherID: true, gcm128CipherID: true,
gcm256CipherID: true,
chacha20Poly1305ID: true, chacha20Poly1305ID: true,
} }
@ -246,16 +276,16 @@ type Config struct {
// unspecified, a size suitable for the chosen cipher is used. // unspecified, a size suitable for the chosen cipher is used.
RekeyThreshold uint64 RekeyThreshold uint64
// The allowed key exchanges algorithms. If unspecified then a // The allowed key exchanges algorithms. If unspecified then a default set
// default set of algorithms is used. // of algorithms is used. Unsupported values are silently ignored.
KeyExchanges []string KeyExchanges []string
// The allowed cipher algorithms. If unspecified then a sensible // The allowed cipher algorithms. If unspecified then a sensible default is
// default is used. // used. Unsupported values are silently ignored.
Ciphers []string Ciphers []string
// The allowed MAC algorithms. If unspecified then a sensible default // The allowed MAC algorithms. If unspecified then a sensible default is
// is used. // used. Unsupported values are silently ignored.
MACs []string MACs []string
} }
@ -272,7 +302,7 @@ func (c *Config) SetDefaults() {
var ciphers []string var ciphers []string
for _, c := range c.Ciphers { for _, c := range c.Ciphers {
if cipherModes[c] != nil { if cipherModes[c] != nil {
// reject the cipher if we have no cipherModes definition // Ignore the cipher if we have no cipherModes definition.
ciphers = append(ciphers, c) ciphers = append(ciphers, c)
} }
} }
@ -281,10 +311,26 @@ func (c *Config) SetDefaults() {
if c.KeyExchanges == nil { if c.KeyExchanges == nil {
c.KeyExchanges = preferredKexAlgos c.KeyExchanges = preferredKexAlgos
} }
var kexs []string
for _, k := range c.KeyExchanges {
if kexAlgoMap[k] != nil {
// Ignore the KEX if we have no kexAlgoMap definition.
kexs = append(kexs, k)
}
}
c.KeyExchanges = kexs
if c.MACs == nil { if c.MACs == nil {
c.MACs = supportedMACs c.MACs = supportedMACs
} }
var macs []string
for _, m := range c.MACs {
if macModes[m] != nil {
// Ignore the MAC if we have no macModes definition.
macs = append(macs, m)
}
}
c.MACs = macs
if c.RekeyThreshold == 0 { if c.RekeyThreshold == 0 {
// cipher specific default // cipher specific default

View File

@ -52,7 +52,7 @@ type Conn interface {
// SendRequest sends a global request, and returns the // SendRequest sends a global request, and returns the
// reply. If wantReply is true, it returns the response status // reply. If wantReply is true, it returns the response status
// and payload. See also RFC4254, section 4. // and payload. See also RFC 4254, section 4.
SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error)
// OpenChannel tries to open an channel. If the request is // OpenChannel tries to open an channel. If the request is
@ -97,7 +97,7 @@ func (c *connection) Close() error {
return c.sshConn.conn.Close() return c.sshConn.conn.Close()
} }
// sshconn provides net.Conn metadata, but disallows direct reads and // sshConn provides net.Conn metadata, but disallows direct reads and
// writes. // writes.
type sshConn struct { type sshConn struct {
conn net.Conn conn net.Conn

View File

@ -13,10 +13,11 @@ others.
References: References:
[PROTOCOL]: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL?rev=HEAD
[PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD
[SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
This package does not fall under the stability promise of the Go language itself, This package does not fall under the stability promise of the Go language itself,
so its API may be changed when pressing needs arise. so its API may be changed when pressing needs arise.
*/ */
package ssh // import "golang.org/x/crypto/ssh" package ssh

View File

@ -11,6 +11,7 @@ import (
"io" "io"
"log" "log"
"net" "net"
"strings"
"sync" "sync"
) )
@ -34,6 +35,16 @@ type keyingTransport interface {
// direction will be effected if a msgNewKeys message is sent // direction will be effected if a msgNewKeys message is sent
// or received. // or received.
prepareKeyChange(*algorithms, *kexResult) error prepareKeyChange(*algorithms, *kexResult) error
// setStrictMode sets the strict KEX mode, notably triggering
// sequence number resets on sending or receiving msgNewKeys.
// If the sequence number is already > 1 when setStrictMode
// is called, an error is returned.
setStrictMode() error
// setInitialKEXDone indicates to the transport that the initial key exchange
// was completed
setInitialKEXDone()
} }
// handshakeTransport implements rekeying on top of a keyingTransport // handshakeTransport implements rekeying on top of a keyingTransport
@ -50,6 +61,10 @@ type handshakeTransport struct {
// connection. // connection.
hostKeys []Signer hostKeys []Signer
// publicKeyAuthAlgorithms is non-empty if we are the server. In that case,
// it contains the supported client public key authentication algorithms.
publicKeyAuthAlgorithms []string
// hostKeyAlgorithms is non-empty if we are the client. In that case, // hostKeyAlgorithms is non-empty if we are the client. In that case,
// we accept these key types from the server as host key. // we accept these key types from the server as host key.
hostKeyAlgorithms []string hostKeyAlgorithms []string
@ -58,11 +73,13 @@ type handshakeTransport struct {
incoming chan []byte incoming chan []byte
readError error readError error
mu sync.Mutex mu sync.Mutex
writeError error writeError error
sentInitPacket []byte sentInitPacket []byte
sentInitMsg *kexInitMsg sentInitMsg *kexInitMsg
pendingPackets [][]byte // Used when a key exchange is in progress. pendingPackets [][]byte // Used when a key exchange is in progress.
writePacketsLeft uint32
writeBytesLeft int64
// If the read loop wants to schedule a kex, it pings this // If the read loop wants to schedule a kex, it pings this
// channel, and the write loop will send out a kex // channel, and the write loop will send out a kex
@ -71,7 +88,8 @@ type handshakeTransport struct {
// If the other side requests or confirms a kex, its kexInit // If the other side requests or confirms a kex, its kexInit
// packet is sent here for the write loop to find it. // packet is sent here for the write loop to find it.
startKex chan *pendingKex startKex chan *pendingKex
kexLoopDone chan struct{} // closed (with writeError non-nil) when kexLoop exits
// data for host key checking // data for host key checking
hostKeyCallback HostKeyCallback hostKeyCallback HostKeyCallback
@ -86,14 +104,16 @@ type handshakeTransport struct {
// Algorithms agreed in the last key exchange. // Algorithms agreed in the last key exchange.
algorithms *algorithms algorithms *algorithms
// Counters exclusively owned by readLoop.
readPacketsLeft uint32 readPacketsLeft uint32
readBytesLeft int64 readBytesLeft int64
writePacketsLeft uint32
writeBytesLeft int64
// The session ID or nil if first kex did not complete yet. // The session ID or nil if first kex did not complete yet.
sessionID []byte sessionID []byte
// strictMode indicates if the other side of the handshake indicated
// that we should be following the strict KEX protocol restrictions.
strictMode bool
} }
type pendingKex struct { type pendingKex struct {
@ -108,7 +128,8 @@ func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion,
clientVersion: clientVersion, clientVersion: clientVersion,
incoming: make(chan []byte, chanSize), incoming: make(chan []byte, chanSize),
requestKex: make(chan struct{}, 1), requestKex: make(chan struct{}, 1),
startKex: make(chan *pendingKex, 1), startKex: make(chan *pendingKex),
kexLoopDone: make(chan struct{}),
config: config, config: config,
} }
@ -139,6 +160,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport { func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport {
t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion)
t.hostKeys = config.hostKeys t.hostKeys = config.hostKeys
t.publicKeyAuthAlgorithms = config.PublicKeyAuthAlgorithms
go t.readLoop() go t.readLoop()
go t.kexLoop() go t.kexLoop()
return t return t
@ -201,7 +223,10 @@ func (t *handshakeTransport) readLoop() {
close(t.incoming) close(t.incoming)
break break
} }
if p[0] == msgIgnore || p[0] == msgDebug { // If this is the first kex, and strict KEX mode is enabled,
// we don't ignore any messages, as they may be used to manipulate
// the packet sequence numbers.
if !(t.sessionID == nil && t.strictMode) && (p[0] == msgIgnore || p[0] == msgDebug) {
continue continue
} }
t.incoming <- p t.incoming <- p
@ -340,16 +365,17 @@ write:
t.mu.Unlock() t.mu.Unlock()
} }
// drain startKex channel. We don't service t.requestKex
// because nobody does blocking sends there.
go func() {
for init := range t.startKex {
init.done <- t.writeError
}
}()
// Unblock reader. // Unblock reader.
t.conn.Close() t.conn.Close()
// drain startKex channel. We don't service t.requestKex
// because nobody does blocking sends there.
for request := range t.startKex {
request.done <- t.getWriteError()
}
// Mark that the loop is done so that Close can return.
close(t.kexLoopDone)
} }
// The protocol uses uint32 for packet counters, so we can't let them // The protocol uses uint32 for packet counters, so we can't let them
@ -432,6 +458,11 @@ func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) {
return successPacket, nil return successPacket, nil
} }
const (
kexStrictClient = "kex-strict-c-v00@openssh.com"
kexStrictServer = "kex-strict-s-v00@openssh.com"
)
// sendKexInit sends a key change message. // sendKexInit sends a key change message.
func (t *handshakeTransport) sendKexInit() error { func (t *handshakeTransport) sendKexInit() error {
t.mu.Lock() t.mu.Lock()
@ -445,7 +476,6 @@ func (t *handshakeTransport) sendKexInit() error {
} }
msg := &kexInitMsg{ msg := &kexInitMsg{
KexAlgos: t.config.KeyExchanges,
CiphersClientServer: t.config.Ciphers, CiphersClientServer: t.config.Ciphers,
CiphersServerClient: t.config.Ciphers, CiphersServerClient: t.config.Ciphers,
MACsClientServer: t.config.MACs, MACsClientServer: t.config.MACs,
@ -455,36 +485,55 @@ func (t *handshakeTransport) sendKexInit() error {
} }
io.ReadFull(rand.Reader, msg.Cookie[:]) io.ReadFull(rand.Reader, msg.Cookie[:])
// We mutate the KexAlgos slice, in order to add the kex-strict extension algorithm,
// and possibly to add the ext-info extension algorithm. Since the slice may be the
// user owned KeyExchanges, we create our own slice in order to avoid using user
// owned memory by mistake.
msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+2) // room for kex-strict and ext-info
msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...)
isServer := len(t.hostKeys) > 0 isServer := len(t.hostKeys) > 0
if isServer { if isServer {
for _, k := range t.hostKeys { for _, k := range t.hostKeys {
// If k is an AlgorithmSigner, presume it supports all signature algorithms // If k is a MultiAlgorithmSigner, we restrict the signature
// associated with the key format. (Ideally AlgorithmSigner would have a // algorithms. If k is a AlgorithmSigner, presume it supports all
// method to advertise supported algorithms, but it doesn't. This means that // signature algorithms associated with the key format. If k is not
// adding support for a new algorithm is a breaking change, as we will // an AlgorithmSigner, we can only assume it only supports the
// immediately negotiate it even if existing implementations don't support // algorithms that matches the key format. (This means that Sign
// it. If that ever happens, we'll have to figure something out.) // can't pick a different default).
// If k is not an AlgorithmSigner, we can only assume it only supports the
// algorithms that matches the key format. (This means that Sign can't pick
// a different default.)
keyFormat := k.PublicKey().Type() keyFormat := k.PublicKey().Type()
if _, ok := k.(AlgorithmSigner); ok {
switch s := k.(type) {
case MultiAlgorithmSigner:
for _, algo := range algorithmsForKeyFormat(keyFormat) {
if contains(s.Algorithms(), underlyingAlgo(algo)) {
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, algo)
}
}
case AlgorithmSigner:
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, algorithmsForKeyFormat(keyFormat)...) msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, algorithmsForKeyFormat(keyFormat)...)
} else { default:
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat) msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat)
} }
} }
if t.sessionID == nil {
msg.KexAlgos = append(msg.KexAlgos, kexStrictServer)
}
} else { } else {
msg.ServerHostKeyAlgos = t.hostKeyAlgorithms msg.ServerHostKeyAlgos = t.hostKeyAlgorithms
// As a client we opt in to receiving SSH_MSG_EXT_INFO so we know what // As a client we opt in to receiving SSH_MSG_EXT_INFO so we know what
// algorithms the server supports for public key authentication. See RFC // algorithms the server supports for public key authentication. See RFC
// 8308, Section 2.1. // 8308, Section 2.1.
//
// We also send the strict KEX mode extension algorithm, in order to opt
// into the strict KEX mode.
if firstKeyExchange := t.sessionID == nil; firstKeyExchange { if firstKeyExchange := t.sessionID == nil; firstKeyExchange {
msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+1)
msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...)
msg.KexAlgos = append(msg.KexAlgos, "ext-info-c") msg.KexAlgos = append(msg.KexAlgos, "ext-info-c")
msg.KexAlgos = append(msg.KexAlgos, kexStrictClient)
} }
} }
packet := Marshal(msg) packet := Marshal(msg)
@ -545,7 +594,16 @@ func (t *handshakeTransport) writePacket(p []byte) error {
} }
func (t *handshakeTransport) Close() error { func (t *handshakeTransport) Close() error {
return t.conn.Close() // Close the connection. This should cause the readLoop goroutine to wake up
// and close t.startKex, which will shut down kexLoop if running.
err := t.conn.Close()
// Wait for the kexLoop goroutine to complete.
// At that point we know that the readLoop goroutine is complete too,
// because kexLoop itself waits for readLoop to close the startKex channel.
<-t.kexLoopDone
return err
} }
func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
@ -581,6 +639,13 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
return err return err
} }
if t.sessionID == nil && ((isClient && contains(serverInit.KexAlgos, kexStrictServer)) || (!isClient && contains(clientInit.KexAlgos, kexStrictClient))) {
t.strictMode = true
if err := t.conn.setStrictMode(); err != nil {
return err
}
}
// We don't send FirstKexFollows, but we handle receiving it. // We don't send FirstKexFollows, but we handle receiving it.
// //
// RFC 4253 section 7 defines the kex and the agreement method for // RFC 4253 section 7 defines the kex and the agreement method for
@ -615,7 +680,8 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
return err return err
} }
if t.sessionID == nil { firstKeyExchange := t.sessionID == nil
if firstKeyExchange {
t.sessionID = result.H t.sessionID = result.H
} }
result.SessionID = t.sessionID result.SessionID = t.sessionID
@ -626,12 +692,41 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
return err return err
} }
// On the server side, after the first SSH_MSG_NEWKEYS, send a SSH_MSG_EXT_INFO
// message with the server-sig-algs extension if the client supports it. See
// RFC 8308, Sections 2.4 and 3.1, and [PROTOCOL], Section 1.9.
if !isClient && firstKeyExchange && contains(clientInit.KexAlgos, "ext-info-c") {
supportedPubKeyAuthAlgosList := strings.Join(t.publicKeyAuthAlgorithms, ",")
extInfo := &extInfoMsg{
NumExtensions: 2,
Payload: make([]byte, 0, 4+15+4+len(supportedPubKeyAuthAlgosList)+4+16+4+1),
}
extInfo.Payload = appendInt(extInfo.Payload, len("server-sig-algs"))
extInfo.Payload = append(extInfo.Payload, "server-sig-algs"...)
extInfo.Payload = appendInt(extInfo.Payload, len(supportedPubKeyAuthAlgosList))
extInfo.Payload = append(extInfo.Payload, supportedPubKeyAuthAlgosList...)
extInfo.Payload = appendInt(extInfo.Payload, len("ping@openssh.com"))
extInfo.Payload = append(extInfo.Payload, "ping@openssh.com"...)
extInfo.Payload = appendInt(extInfo.Payload, 1)
extInfo.Payload = append(extInfo.Payload, "0"...)
if err := t.conn.writePacket(Marshal(extInfo)); err != nil {
return err
}
}
if packet, err := t.conn.readPacket(); err != nil { if packet, err := t.conn.readPacket(); err != nil {
return err return err
} else if packet[0] != msgNewKeys { } else if packet[0] != msgNewKeys {
return unexpectedMessageError(msgNewKeys, packet[0]) return unexpectedMessageError(msgNewKeys, packet[0])
} }
if firstKeyExchange {
// Indicates to the transport that the first key exchange is completed
// after receiving SSH_MSG_NEWKEYS.
t.conn.setInitialKEXDone()
}
return nil return nil
} }
@ -654,9 +749,16 @@ func (a algorithmSignerWrapper) SignWithAlgorithm(rand io.Reader, data []byte, a
func pickHostKey(hostKeys []Signer, algo string) AlgorithmSigner { func pickHostKey(hostKeys []Signer, algo string) AlgorithmSigner {
for _, k := range hostKeys { for _, k := range hostKeys {
if s, ok := k.(MultiAlgorithmSigner); ok {
if !contains(s.Algorithms(), underlyingAlgo(algo)) {
continue
}
}
if algo == k.PublicKey().Type() { if algo == k.PublicKey().Type() {
return algorithmSignerWrapper{k} return algorithmSignerWrapper{k}
} }
k, ok := k.(AlgorithmSigner) k, ok := k.(AlgorithmSigner)
if !ok { if !ok {
continue continue

View File

@ -23,6 +23,7 @@ const (
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
kexAlgoDH14SHA256 = "diffie-hellman-group14-sha256" kexAlgoDH14SHA256 = "diffie-hellman-group14-sha256"
kexAlgoDH16SHA512 = "diffie-hellman-group16-sha512"
kexAlgoECDH256 = "ecdh-sha2-nistp256" kexAlgoECDH256 = "ecdh-sha2-nistp256"
kexAlgoECDH384 = "ecdh-sha2-nistp384" kexAlgoECDH384 = "ecdh-sha2-nistp384"
kexAlgoECDH521 = "ecdh-sha2-nistp521" kexAlgoECDH521 = "ecdh-sha2-nistp521"
@ -430,6 +431,17 @@ func init() {
hashFunc: crypto.SHA256, hashFunc: crypto.SHA256,
} }
// This is the group called diffie-hellman-group16-sha512 in RFC
// 8268 and Oakley Group 16 in RFC 3526.
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF", 16)
kexAlgoMap[kexAlgoDH16SHA512] = &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),
hashFunc: crypto.SHA512,
}
kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()} kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()} kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}

View File

@ -11,13 +11,16 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/md5" "crypto/md5"
"crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha256" "crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/asn1" "encoding/asn1"
"encoding/base64" "encoding/base64"
"encoding/binary"
"encoding/hex" "encoding/hex"
"encoding/pem" "encoding/pem"
"errors" "errors"
@ -26,7 +29,6 @@ import (
"math/big" "math/big"
"strings" "strings"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ssh/internal/bcrypt_pbkdf" "golang.org/x/crypto/ssh/internal/bcrypt_pbkdf"
) )
@ -184,7 +186,7 @@ func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey
return "", nil, nil, "", nil, io.EOF return "", nil, nil, "", nil, io.EOF
} }
// ParseAuthorizedKeys parses a public key from an authorized_keys // ParseAuthorizedKey parses a public key from an authorized_keys
// file used in OpenSSH according to the sshd(8) manual page. // file used in OpenSSH according to the sshd(8) manual page.
func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) { func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) {
for len(in) > 0 { for len(in) > 0 {
@ -295,6 +297,18 @@ func MarshalAuthorizedKey(key PublicKey) []byte {
return b.Bytes() return b.Bytes()
} }
// MarshalPrivateKey returns a PEM block with the private key serialized in the
// OpenSSH format.
func MarshalPrivateKey(key crypto.PrivateKey, comment string) (*pem.Block, error) {
return marshalOpenSSHPrivateKey(key, comment, unencryptedOpenSSHMarshaler)
}
// MarshalPrivateKeyWithPassphrase returns a PEM block holding the encrypted
// private key serialized in the OpenSSH format.
func MarshalPrivateKeyWithPassphrase(key crypto.PrivateKey, comment string, passphrase []byte) (*pem.Block, error) {
return marshalOpenSSHPrivateKey(key, comment, passphraseProtectedOpenSSHMarshaler(passphrase))
}
// PublicKey represents a public key using an unspecified algorithm. // PublicKey represents a public key using an unspecified algorithm.
// //
// Some PublicKeys provided by this package also implement CryptoPublicKey. // Some PublicKeys provided by this package also implement CryptoPublicKey.
@ -321,7 +335,7 @@ type CryptoPublicKey interface {
// A Signer can create signatures that verify against a public key. // A Signer can create signatures that verify against a public key.
// //
// Some Signers provided by this package also implement AlgorithmSigner. // Some Signers provided by this package also implement MultiAlgorithmSigner.
type Signer interface { type Signer interface {
// PublicKey returns the associated PublicKey. // PublicKey returns the associated PublicKey.
PublicKey() PublicKey PublicKey() PublicKey
@ -336,9 +350,9 @@ type Signer interface {
// An AlgorithmSigner is a Signer that also supports specifying an algorithm to // An AlgorithmSigner is a Signer that also supports specifying an algorithm to
// use for signing. // use for signing.
// //
// An AlgorithmSigner can't advertise the algorithms it supports, so it should // An AlgorithmSigner can't advertise the algorithms it supports, unless it also
// be prepared to be invoked with every algorithm supported by the public key // implements MultiAlgorithmSigner, so it should be prepared to be invoked with
// format. // every algorithm supported by the public key format.
type AlgorithmSigner interface { type AlgorithmSigner interface {
Signer Signer
@ -349,6 +363,75 @@ type AlgorithmSigner interface {
SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error)
} }
// MultiAlgorithmSigner is an AlgorithmSigner that also reports the algorithms
// supported by that signer.
type MultiAlgorithmSigner interface {
AlgorithmSigner
// Algorithms returns the available algorithms in preference order. The list
// must not be empty, and it must not include certificate types.
Algorithms() []string
}
// NewSignerWithAlgorithms returns a signer restricted to the specified
// algorithms. The algorithms must be set in preference order. The list must not
// be empty, and it must not include certificate types. An error is returned if
// the specified algorithms are incompatible with the public key type.
func NewSignerWithAlgorithms(signer AlgorithmSigner, algorithms []string) (MultiAlgorithmSigner, error) {
if len(algorithms) == 0 {
return nil, errors.New("ssh: please specify at least one valid signing algorithm")
}
var signerAlgos []string
supportedAlgos := algorithmsForKeyFormat(underlyingAlgo(signer.PublicKey().Type()))
if s, ok := signer.(*multiAlgorithmSigner); ok {
signerAlgos = s.Algorithms()
} else {
signerAlgos = supportedAlgos
}
for _, algo := range algorithms {
if !contains(supportedAlgos, algo) {
return nil, fmt.Errorf("ssh: algorithm %q is not supported for key type %q",
algo, signer.PublicKey().Type())
}
if !contains(signerAlgos, algo) {
return nil, fmt.Errorf("ssh: algorithm %q is restricted for the provided signer", algo)
}
}
return &multiAlgorithmSigner{
AlgorithmSigner: signer,
supportedAlgorithms: algorithms,
}, nil
}
type multiAlgorithmSigner struct {
AlgorithmSigner
supportedAlgorithms []string
}
func (s *multiAlgorithmSigner) Algorithms() []string {
return s.supportedAlgorithms
}
func (s *multiAlgorithmSigner) isAlgorithmSupported(algorithm string) bool {
if algorithm == "" {
algorithm = underlyingAlgo(s.PublicKey().Type())
}
for _, algo := range s.supportedAlgorithms {
if algorithm == algo {
return true
}
}
return false
}
func (s *multiAlgorithmSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
if !s.isAlgorithmSupported(algorithm) {
return nil, fmt.Errorf("ssh: algorithm %q is not supported: %v", algorithm, s.supportedAlgorithms)
}
return s.AlgorithmSigner.SignWithAlgorithm(rand, data, algorithm)
}
type rsaPublicKey rsa.PublicKey type rsaPublicKey rsa.PublicKey
func (r *rsaPublicKey) Type() string { func (r *rsaPublicKey) Type() string {
@ -405,7 +488,49 @@ func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
h := hash.New() h := hash.New()
h.Write(data) h.Write(data)
digest := h.Sum(nil) digest := h.Sum(nil)
return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), hash, digest, sig.Blob)
// Signatures in PKCS1v15 must match the key's modulus in
// length. However with SSH, some signers provide RSA
// signatures which are missing the MSB 0's of the bignum
// represented. With ssh-rsa signatures, this is encouraged by
// the spec (even though e.g. OpenSSH will give the full
// length unconditionally). With rsa-sha2-* signatures, the
// verifier is allowed to support these, even though they are
// out of spec. See RFC 4253 Section 6.6 for ssh-rsa and RFC
// 8332 Section 3 for rsa-sha2-* details.
//
// In practice:
// * OpenSSH always allows "short" signatures:
// https://github.com/openssh/openssh-portable/blob/V_9_8_P1/ssh-rsa.c#L526
// but always generates padded signatures:
// https://github.com/openssh/openssh-portable/blob/V_9_8_P1/ssh-rsa.c#L439
//
// * PuTTY versions 0.81 and earlier will generate short
// signatures for all RSA signature variants. Note that
// PuTTY is embedded in other software, such as WinSCP and
// FileZilla. At the time of writing, a patch has been
// applied to PuTTY to generate padded signatures for
// rsa-sha2-*, but not yet released:
// https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=a5bcf3d384e1bf15a51a6923c3724cbbee022d8e
//
// * SSH.NET versions 2024.0.0 and earlier will generate short
// signatures for all RSA signature variants, fixed in 2024.1.0:
// https://github.com/sshnet/SSH.NET/releases/tag/2024.1.0
//
// As a result, we pad these up to the key size by inserting
// leading 0's.
//
// Note that support for short signatures with rsa-sha2-* may
// be removed in the future due to such signatures not being
// allowed by the spec.
blob := sig.Blob
keySize := (*rsa.PublicKey)(r).Size()
if len(blob) < keySize {
padded := make([]byte, keySize)
copy(padded[keySize-len(blob):], blob)
blob = padded
}
return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), hash, digest, blob)
} }
func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey { func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey {
@ -512,6 +637,10 @@ func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
return k.SignWithAlgorithm(rand, data, k.PublicKey().Type()) return k.SignWithAlgorithm(rand, data, k.PublicKey().Type())
} }
func (k *dsaPrivateKey) Algorithms() []string {
return []string{k.PublicKey().Type()}
}
func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
if algorithm != "" && algorithm != k.PublicKey().Type() { if algorithm != "" && algorithm != k.PublicKey().Type() {
return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
@ -817,6 +946,10 @@ func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error {
return errors.New("ssh: signature did not verify") return errors.New("ssh: signature did not verify")
} }
func (k *skECDSAPublicKey) CryptoPublicKey() crypto.PublicKey {
return &k.PublicKey
}
type skEd25519PublicKey struct { type skEd25519PublicKey struct {
// application is a URL-like string, typically "ssh:" for SSH. // application is a URL-like string, typically "ssh:" for SSH.
// see openssh/PROTOCOL.u2f for details. // see openssh/PROTOCOL.u2f for details.
@ -913,6 +1046,10 @@ func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error {
return nil return nil
} }
func (k *skEd25519PublicKey) CryptoPublicKey() crypto.PublicKey {
return k.PublicKey
}
// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, // NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
// *ecdsa.PrivateKey or any other crypto.Signer and returns a // *ecdsa.PrivateKey or any other crypto.Signer and returns a
// corresponding Signer instance. ECDSA keys must use P-256, P-384 or // corresponding Signer instance. ECDSA keys must use P-256, P-384 or
@ -961,13 +1098,16 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
return s.SignWithAlgorithm(rand, data, s.pubKey.Type()) return s.SignWithAlgorithm(rand, data, s.pubKey.Type())
} }
func (s *wrappedSigner) Algorithms() []string {
return algorithmsForKeyFormat(s.pubKey.Type())
}
func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
if algorithm == "" { if algorithm == "" {
algorithm = s.pubKey.Type() algorithm = s.pubKey.Type()
} }
supportedAlgos := algorithmsForKeyFormat(s.pubKey.Type()) if !contains(s.Algorithms(), algorithm) {
if !contains(supportedAlgos, algorithm) {
return nil, fmt.Errorf("ssh: unsupported signature algorithm %q for key format %q", algorithm, s.pubKey.Type()) return nil, fmt.Errorf("ssh: unsupported signature algorithm %q for key format %q", algorithm, s.pubKey.Type())
} }
@ -1087,9 +1227,9 @@ func (*PassphraseMissingError) Error() string {
return "ssh: this private key is passphrase protected" return "ssh: this private key is passphrase protected"
} }
// ParseRawPrivateKey returns a private key from a PEM encoded private key. It // ParseRawPrivateKey returns a private key from a PEM encoded private key. It supports
// supports RSA (PKCS#1), PKCS#8, DSA (OpenSSL), and ECDSA private keys. If the // RSA, DSA, ECDSA, and Ed25519 private keys in PKCS#1, PKCS#8, OpenSSL, and OpenSSH
// private key is encrypted, it will return a PassphraseMissingError. // formats. If the private key is encrypted, it will return a PassphraseMissingError.
func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
block, _ := pem.Decode(pemBytes) block, _ := pem.Decode(pemBytes)
if block == nil { if block == nil {
@ -1142,16 +1282,27 @@ func ParseRawPrivateKeyWithPassphrase(pemBytes, passphrase []byte) (interface{},
return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err) return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err)
} }
var result interface{}
switch block.Type { switch block.Type {
case "RSA PRIVATE KEY": case "RSA PRIVATE KEY":
return x509.ParsePKCS1PrivateKey(buf) result, err = x509.ParsePKCS1PrivateKey(buf)
case "EC PRIVATE KEY": case "EC PRIVATE KEY":
return x509.ParseECPrivateKey(buf) result, err = x509.ParseECPrivateKey(buf)
case "DSA PRIVATE KEY": case "DSA PRIVATE KEY":
return ParseDSAPrivateKey(buf) result, err = ParseDSAPrivateKey(buf)
default: default:
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) err = fmt.Errorf("ssh: unsupported key type %q", block.Type)
} }
// Because of deficiencies in the format, DecryptPEMBlock does not always
// detect an incorrect password. In these cases decrypted DER bytes is
// random noise. If the parsing of the key returns an asn1.StructuralError
// we return x509.IncorrectPasswordError.
if _, ok := err.(asn1.StructuralError); ok {
return nil, x509.IncorrectPasswordError
}
return result, err
} }
// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as // ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as
@ -1241,28 +1392,106 @@ func passphraseProtectedOpenSSHKey(passphrase []byte) openSSHDecryptFunc {
} }
} }
func unencryptedOpenSSHMarshaler(privKeyBlock []byte) ([]byte, string, string, string, error) {
key := generateOpenSSHPadding(privKeyBlock, 8)
return key, "none", "none", "", nil
}
func passphraseProtectedOpenSSHMarshaler(passphrase []byte) openSSHEncryptFunc {
return func(privKeyBlock []byte) ([]byte, string, string, string, error) {
salt := make([]byte, 16)
if _, err := rand.Read(salt); err != nil {
return nil, "", "", "", err
}
opts := struct {
Salt []byte
Rounds uint32
}{salt, 16}
// Derive key to encrypt the private key block.
k, err := bcrypt_pbkdf.Key(passphrase, salt, int(opts.Rounds), 32+aes.BlockSize)
if err != nil {
return nil, "", "", "", err
}
// Add padding matching the block size of AES.
keyBlock := generateOpenSSHPadding(privKeyBlock, aes.BlockSize)
// Encrypt the private key using the derived secret.
dst := make([]byte, len(keyBlock))
key, iv := k[:32], k[32:]
block, err := aes.NewCipher(key)
if err != nil {
return nil, "", "", "", err
}
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(dst, keyBlock)
return dst, "aes256-ctr", "bcrypt", string(Marshal(opts)), nil
}
}
const privateKeyAuthMagic = "openssh-key-v1\x00"
type openSSHDecryptFunc func(CipherName, KdfName, KdfOpts string, PrivKeyBlock []byte) ([]byte, error) type openSSHDecryptFunc func(CipherName, KdfName, KdfOpts string, PrivKeyBlock []byte) ([]byte, error)
type openSSHEncryptFunc func(PrivKeyBlock []byte) (ProtectedKeyBlock []byte, cipherName, kdfName, kdfOptions string, err error)
type openSSHEncryptedPrivateKey struct {
CipherName string
KdfName string
KdfOpts string
NumKeys uint32
PubKey []byte
PrivKeyBlock []byte
}
type openSSHPrivateKey struct {
Check1 uint32
Check2 uint32
Keytype string
Rest []byte `ssh:"rest"`
}
type openSSHRSAPrivateKey struct {
N *big.Int
E *big.Int
D *big.Int
Iqmp *big.Int
P *big.Int
Q *big.Int
Comment string
Pad []byte `ssh:"rest"`
}
type openSSHEd25519PrivateKey struct {
Pub []byte
Priv []byte
Comment string
Pad []byte `ssh:"rest"`
}
type openSSHECDSAPrivateKey struct {
Curve string
Pub []byte
D *big.Int
Comment string
Pad []byte `ssh:"rest"`
}
// parseOpenSSHPrivateKey parses an OpenSSH private key, using the decrypt // parseOpenSSHPrivateKey parses an OpenSSH private key, using the decrypt
// function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used // function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used
// as the decrypt function to parse an unencrypted private key. See // as the decrypt function to parse an unencrypted private key. See
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key. // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key.
func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.PrivateKey, error) { func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.PrivateKey, error) {
const magic = "openssh-key-v1\x00" if len(key) < len(privateKeyAuthMagic) || string(key[:len(privateKeyAuthMagic)]) != privateKeyAuthMagic {
if len(key) < len(magic) || string(key[:len(magic)]) != magic {
return nil, errors.New("ssh: invalid openssh private key format") return nil, errors.New("ssh: invalid openssh private key format")
} }
remaining := key[len(magic):] remaining := key[len(privateKeyAuthMagic):]
var w struct {
CipherName string
KdfName string
KdfOpts string
NumKeys uint32
PubKey []byte
PrivKeyBlock []byte
}
var w openSSHEncryptedPrivateKey
if err := Unmarshal(remaining, &w); err != nil { if err := Unmarshal(remaining, &w); err != nil {
return nil, err return nil, err
} }
@ -1284,13 +1513,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
return nil, err return nil, err
} }
pk1 := struct { var pk1 openSSHPrivateKey
Check1 uint32
Check2 uint32
Keytype string
Rest []byte `ssh:"rest"`
}{}
if err := Unmarshal(privKeyBlock, &pk1); err != nil || pk1.Check1 != pk1.Check2 { if err := Unmarshal(privKeyBlock, &pk1); err != nil || pk1.Check1 != pk1.Check2 {
if w.CipherName != "none" { if w.CipherName != "none" {
return nil, x509.IncorrectPasswordError return nil, x509.IncorrectPasswordError
@ -1300,18 +1523,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
switch pk1.Keytype { switch pk1.Keytype {
case KeyAlgoRSA: case KeyAlgoRSA:
// https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773 var key openSSHRSAPrivateKey
key := struct {
N *big.Int
E *big.Int
D *big.Int
Iqmp *big.Int
P *big.Int
Q *big.Int
Comment string
Pad []byte `ssh:"rest"`
}{}
if err := Unmarshal(pk1.Rest, &key); err != nil { if err := Unmarshal(pk1.Rest, &key); err != nil {
return nil, err return nil, err
} }
@ -1337,13 +1549,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
return pk, nil return pk, nil
case KeyAlgoED25519: case KeyAlgoED25519:
key := struct { var key openSSHEd25519PrivateKey
Pub []byte
Priv []byte
Comment string
Pad []byte `ssh:"rest"`
}{}
if err := Unmarshal(pk1.Rest, &key); err != nil { if err := Unmarshal(pk1.Rest, &key); err != nil {
return nil, err return nil, err
} }
@ -1360,14 +1566,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
copy(pk, key.Priv) copy(pk, key.Priv)
return &pk, nil return &pk, nil
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
key := struct { var key openSSHECDSAPrivateKey
Curve string
Pub []byte
D *big.Int
Comment string
Pad []byte `ssh:"rest"`
}{}
if err := Unmarshal(pk1.Rest, &key); err != nil { if err := Unmarshal(pk1.Rest, &key); err != nil {
return nil, err return nil, err
} }
@ -1415,6 +1614,131 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
} }
} }
func marshalOpenSSHPrivateKey(key crypto.PrivateKey, comment string, encrypt openSSHEncryptFunc) (*pem.Block, error) {
var w openSSHEncryptedPrivateKey
var pk1 openSSHPrivateKey
// Random check bytes.
var check uint32
if err := binary.Read(rand.Reader, binary.BigEndian, &check); err != nil {
return nil, err
}
pk1.Check1 = check
pk1.Check2 = check
w.NumKeys = 1
// Use a []byte directly on ed25519 keys.
if k, ok := key.(*ed25519.PrivateKey); ok {
key = *k
}
switch k := key.(type) {
case *rsa.PrivateKey:
E := new(big.Int).SetInt64(int64(k.PublicKey.E))
// Marshal public key:
// E and N are in reversed order in the public and private key.
pubKey := struct {
KeyType string
E *big.Int
N *big.Int
}{
KeyAlgoRSA,
E, k.PublicKey.N,
}
w.PubKey = Marshal(pubKey)
// Marshal private key.
key := openSSHRSAPrivateKey{
N: k.PublicKey.N,
E: E,
D: k.D,
Iqmp: k.Precomputed.Qinv,
P: k.Primes[0],
Q: k.Primes[1],
Comment: comment,
}
pk1.Keytype = KeyAlgoRSA
pk1.Rest = Marshal(key)
case ed25519.PrivateKey:
pub := make([]byte, ed25519.PublicKeySize)
priv := make([]byte, ed25519.PrivateKeySize)
copy(pub, k[32:])
copy(priv, k)
// Marshal public key.
pubKey := struct {
KeyType string
Pub []byte
}{
KeyAlgoED25519, pub,
}
w.PubKey = Marshal(pubKey)
// Marshal private key.
key := openSSHEd25519PrivateKey{
Pub: pub,
Priv: priv,
Comment: comment,
}
pk1.Keytype = KeyAlgoED25519
pk1.Rest = Marshal(key)
case *ecdsa.PrivateKey:
var curve, keyType string
switch name := k.Curve.Params().Name; name {
case "P-256":
curve = "nistp256"
keyType = KeyAlgoECDSA256
case "P-384":
curve = "nistp384"
keyType = KeyAlgoECDSA384
case "P-521":
curve = "nistp521"
keyType = KeyAlgoECDSA521
default:
return nil, errors.New("ssh: unhandled elliptic curve " + name)
}
pub := elliptic.Marshal(k.Curve, k.PublicKey.X, k.PublicKey.Y)
// Marshal public key.
pubKey := struct {
KeyType string
Curve string
Pub []byte
}{
keyType, curve, pub,
}
w.PubKey = Marshal(pubKey)
// Marshal private key.
key := openSSHECDSAPrivateKey{
Curve: curve,
Pub: pub,
D: k.D,
Comment: comment,
}
pk1.Keytype = keyType
pk1.Rest = Marshal(key)
default:
return nil, fmt.Errorf("ssh: unsupported key type %T", k)
}
var err error
// Add padding and encrypt the key if necessary.
w.PrivKeyBlock, w.CipherName, w.KdfName, w.KdfOpts, err = encrypt(Marshal(pk1))
if err != nil {
return nil, err
}
b := Marshal(w)
block := &pem.Block{
Type: "OPENSSH PRIVATE KEY",
Bytes: append([]byte(privateKeyAuthMagic), b...),
}
return block, nil
}
func checkOpenSSHKeyPadding(pad []byte) error { func checkOpenSSHKeyPadding(pad []byte) error {
for i, b := range pad { for i, b := range pad {
if int(b) != i+1 { if int(b) != i+1 {
@ -1424,6 +1748,13 @@ func checkOpenSSHKeyPadding(pad []byte) error {
return nil return nil
} }
func generateOpenSSHPadding(block []byte, blockSize int) []byte {
for i, l := 0, len(block); (l+i)%blockSize != 0; i++ {
block = append(block, byte(i+1))
}
return block
}
// FingerprintLegacyMD5 returns the user presentation of the key's // FingerprintLegacyMD5 returns the user presentation of the key's
// fingerprint as described by RFC 4716 section 4. // fingerprint as described by RFC 4716 section 4.
func FingerprintLegacyMD5(pubKey PublicKey) string { func FingerprintLegacyMD5(pubKey PublicKey) string {

View File

@ -142,7 +142,7 @@ func keyEq(a, b ssh.PublicKey) bool {
return bytes.Equal(a.Marshal(), b.Marshal()) return bytes.Equal(a.Marshal(), b.Marshal())
} }
// IsAuthorityForHost can be used as a callback in ssh.CertChecker // IsHostAuthority can be used as a callback in ssh.CertChecker
func (db *hostKeyDB) IsHostAuthority(remote ssh.PublicKey, address string) bool { func (db *hostKeyDB) IsHostAuthority(remote ssh.PublicKey, address string) bool {
h, p, err := net.SplitHostPort(address) h, p, err := net.SplitHostPort(address)
if err != nil { if err != nil {

View File

@ -10,6 +10,7 @@ import (
"crypto/hmac" "crypto/hmac"
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"crypto/sha512"
"hash" "hash"
) )
@ -46,9 +47,15 @@ func (t truncatingMAC) Size() int {
func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() }
var macModes = map[string]*macMode{ var macModes = map[string]*macMode{
"hmac-sha2-512-etm@openssh.com": {64, true, func(key []byte) hash.Hash {
return hmac.New(sha512.New, key)
}},
"hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash { "hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash {
return hmac.New(sha256.New, key) return hmac.New(sha256.New, key)
}}, }},
"hmac-sha2-512": {64, false, func(key []byte) hash.Hash {
return hmac.New(sha512.New, key)
}},
"hmac-sha2-256": {32, false, func(key []byte) hash.Hash { "hmac-sha2-256": {32, false, func(key []byte) hash.Hash {
return hmac.New(sha256.New, key) return hmac.New(sha256.New, key)
}}, }},

View File

@ -68,7 +68,7 @@ type kexInitMsg struct {
// See RFC 4253, section 8. // See RFC 4253, section 8.
// Diffie-Helman // Diffie-Hellman
const msgKexDHInit = 30 const msgKexDHInit = 30
type kexDHInitMsg struct { type kexDHInitMsg struct {
@ -349,6 +349,20 @@ type userAuthGSSAPIError struct {
LanguageTag string LanguageTag string
} }
// Transport layer OpenSSH extension. See [PROTOCOL], section 1.9
const msgPing = 192
type pingMsg struct {
Data string `sshtype:"192"`
}
// Transport layer OpenSSH extension. See [PROTOCOL], section 1.9
const msgPong = 193
type pongMsg struct {
Data string `sshtype:"193"`
}
// typeTags returns the possible type bytes for the given reflect.Type, which // typeTags returns the possible type bytes for the given reflect.Type, which
// should be a struct. The possible values are separated by a '|' character. // should be a struct. The possible values are separated by a '|' character.
func typeTags(structType reflect.Type) (tags []byte) { func typeTags(structType reflect.Type) (tags []byte) {

View File

@ -231,6 +231,12 @@ func (m *mux) onePacket() error {
return m.handleChannelOpen(packet) return m.handleChannelOpen(packet)
case msgGlobalRequest, msgRequestSuccess, msgRequestFailure: case msgGlobalRequest, msgRequestSuccess, msgRequestFailure:
return m.handleGlobalPacket(packet) return m.handleGlobalPacket(packet)
case msgPing:
var msg pingMsg
if err := Unmarshal(packet, &msg); err != nil {
return fmt.Errorf("failed to unmarshal ping@openssh.com message: %w", err)
}
return m.sendMessage(pongMsg(msg))
} }
// assume a channel packet. // assume a channel packet.

View File

@ -64,12 +64,27 @@ type ServerConfig struct {
// Config contains configuration shared between client and server. // Config contains configuration shared between client and server.
Config Config
// PublicKeyAuthAlgorithms specifies the supported client public key
// authentication algorithms. Note that this should not include certificate
// types since those use the underlying algorithm. This list is sent to the
// client if it supports the server-sig-algs extension. Order is irrelevant.
// If unspecified then a default set of algorithms is used.
PublicKeyAuthAlgorithms []string
hostKeys []Signer hostKeys []Signer
// NoClientAuth is true if clients are allowed to connect without // NoClientAuth is true if clients are allowed to connect without
// authenticating. // authenticating.
// To determine NoClientAuth at runtime, set NoClientAuth to true
// and the optional NoClientAuthCallback to a non-nil value.
NoClientAuth bool NoClientAuth bool
// NoClientAuthCallback, if non-nil, is called when a user
// attempts to authenticate with auth method "none".
// NoClientAuth must also be set to true for this be used, or
// this func is unused.
NoClientAuthCallback func(ConnMetadata) (*Permissions, error)
// MaxAuthTries specifies the maximum number of authentication attempts // MaxAuthTries specifies the maximum number of authentication attempts
// permitted per connection. If set to a negative number, the number of // permitted per connection. If set to a negative number, the number of
// attempts are unlimited. If set to zero, the number of attempts are limited // attempts are unlimited. If set to zero, the number of attempts are limited
@ -134,7 +149,7 @@ func (s *ServerConfig) AddHostKey(key Signer) {
} }
// cachedPubKey contains the results of querying whether a public key is // cachedPubKey contains the results of querying whether a public key is
// acceptable for a user. // acceptable for a user. This is a FIFO cache.
type cachedPubKey struct { type cachedPubKey struct {
user string user string
pubKeyData []byte pubKeyData []byte
@ -142,7 +157,13 @@ type cachedPubKey struct {
perms *Permissions perms *Permissions
} }
const maxCachedPubKeys = 16 // maxCachedPubKeys is the number of cache entries we store.
//
// Due to consistent misuse of the PublicKeyCallback API, we have reduced this
// to 1, such that the only key in the cache is the most recently seen one. This
// forces the behavior that the last call to PublicKeyCallback will always be
// with the key that is used for authentication.
const maxCachedPubKeys = 1
// pubKeyCache caches tests for public keys. Since SSH clients // pubKeyCache caches tests for public keys. Since SSH clients
// will query whether a public key is acceptable before attempting to // will query whether a public key is acceptable before attempting to
@ -164,9 +185,10 @@ func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
// add adds the given tuple to the cache. // add adds the given tuple to the cache.
func (c *pubKeyCache) add(candidate cachedPubKey) { func (c *pubKeyCache) add(candidate cachedPubKey) {
if len(c.keys) < maxCachedPubKeys { if len(c.keys) >= maxCachedPubKeys {
c.keys = append(c.keys, candidate) c.keys = c.keys[1:]
} }
c.keys = append(c.keys, candidate)
} }
// ServerConn is an authenticated SSH connection, as seen from the // ServerConn is an authenticated SSH connection, as seen from the
@ -193,9 +215,20 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha
if fullConf.MaxAuthTries == 0 { if fullConf.MaxAuthTries == 0 {
fullConf.MaxAuthTries = 6 fullConf.MaxAuthTries = 6
} }
if len(fullConf.PublicKeyAuthAlgorithms) == 0 {
fullConf.PublicKeyAuthAlgorithms = supportedPubKeyAuthAlgos
} else {
for _, algo := range fullConf.PublicKeyAuthAlgorithms {
if !contains(supportedPubKeyAuthAlgos, algo) {
c.Close()
return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo)
}
}
}
// Check if the config contains any unsupported key exchanges // Check if the config contains any unsupported key exchanges
for _, kex := range fullConf.KeyExchanges { for _, kex := range fullConf.KeyExchanges {
if _, ok := serverForbiddenKexAlgos[kex]; ok { if _, ok := serverForbiddenKexAlgos[kex]; ok {
c.Close()
return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex) return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex)
} }
} }
@ -283,15 +316,6 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
return perms, err return perms, err
} }
func isAcceptableAlgo(algo string) bool {
switch algo {
case KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519,
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
return true
}
return false
}
func checkSourceAddress(addr net.Addr, sourceAddrs string) error { func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
if addr == nil { if addr == nil {
return errors.New("ssh: no address known for client, but source-address match required") return errors.New("ssh: no address known for client, but source-address match required")
@ -322,7 +346,7 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr) return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
} }
func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *connection, func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, token []byte, s *connection,
sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) { sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) {
gssAPIServer := gssapiConfig.Server gssAPIServer := gssapiConfig.Server
defer gssAPIServer.DeleteSecContext() defer gssAPIServer.DeleteSecContext()
@ -332,7 +356,7 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c
outToken []byte outToken []byte
needContinue bool needContinue bool
) )
outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(firstToken) outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(token)
if err != nil { if err != nil {
return err, nil, nil return err, nil, nil
} }
@ -354,6 +378,7 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c
if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil { if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
return nil, nil, err return nil, nil, err
} }
token = userAuthGSSAPITokenReq.Token
} }
packet, err := s.transport.readPacket() packet, err := s.transport.readPacket()
if err != nil { if err != nil {
@ -371,6 +396,25 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c
return authErr, perms, nil return authErr, perms, nil
} }
// isAlgoCompatible checks if the signature format is compatible with the
// selected algorithm taking into account edge cases that occur with old
// clients.
func isAlgoCompatible(algo, sigFormat string) bool {
// Compatibility for old clients.
//
// For certificate authentication with OpenSSH 7.2-7.7 signature format can
// be rsa-sha2-256 or rsa-sha2-512 for the algorithm
// ssh-rsa-cert-v01@openssh.com.
//
// With gpg-agent < 2.2.6 the algorithm can be rsa-sha2-256 or rsa-sha2-512
// for signature format ssh-rsa.
if isRSA(algo) && isRSA(sigFormat) {
return true
}
// Standard case: the underlying algorithm must match the signature format.
return underlyingAlgo(algo) == sigFormat
}
// ServerAuthError represents server authentication errors and is // ServerAuthError represents server authentication errors and is
// sometimes returned by NewServerConn. It appends any authentication // sometimes returned by NewServerConn. It appends any authentication
// errors that may occur, and is returned if all of the authentication // errors that may occur, and is returned if all of the authentication
@ -389,6 +433,35 @@ func (l ServerAuthError) Error() string {
return "[" + strings.Join(errs, ", ") + "]" return "[" + strings.Join(errs, ", ") + "]"
} }
// ServerAuthCallbacks defines server-side authentication callbacks.
type ServerAuthCallbacks struct {
// PasswordCallback behaves like [ServerConfig.PasswordCallback].
PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
// PublicKeyCallback behaves like [ServerConfig.PublicKeyCallback].
PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
// KeyboardInteractiveCallback behaves like [ServerConfig.KeyboardInteractiveCallback].
KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
// GSSAPIWithMICConfig behaves like [ServerConfig.GSSAPIWithMICConfig].
GSSAPIWithMICConfig *GSSAPIWithMICConfig
}
// PartialSuccessError can be returned by any of the [ServerConfig]
// authentication callbacks to indicate to the client that authentication has
// partially succeeded, but further steps are required.
type PartialSuccessError struct {
// Next defines the authentication callbacks to apply to further steps. The
// available methods communicated to the client are based on the non-nil
// ServerAuthCallbacks fields.
Next ServerAuthCallbacks
}
func (p *PartialSuccessError) Error() string {
return "ssh: authenticated with partial success"
}
// ErrNoAuth is the error value returned if no // ErrNoAuth is the error value returned if no
// authentication method has been passed yet. This happens as a normal // authentication method has been passed yet. This happens as a normal
// part of the authentication loop, since the client first tries // part of the authentication loop, since the client first tries
@ -396,14 +469,42 @@ func (l ServerAuthError) Error() string {
// It is returned in ServerAuthError.Errors from NewServerConn. // It is returned in ServerAuthError.Errors from NewServerConn.
var ErrNoAuth = errors.New("ssh: no auth passed yet") var ErrNoAuth = errors.New("ssh: no auth passed yet")
// BannerError is an error that can be returned by authentication handlers in
// ServerConfig to send a banner message to the client.
type BannerError struct {
Err error
Message string
}
func (b *BannerError) Unwrap() error {
return b.Err
}
func (b *BannerError) Error() string {
if b.Err == nil {
return b.Message
}
return b.Err.Error()
}
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
sessionID := s.transport.getSessionID() sessionID := s.transport.getSessionID()
var cache pubKeyCache var cache pubKeyCache
var perms *Permissions var perms *Permissions
authFailures := 0 authFailures := 0
noneAuthCount := 0
var authErrs []error var authErrs []error
var displayedBanner bool var displayedBanner bool
partialSuccessReturned := false
// Set the initial authentication callbacks from the config. They can be
// changed if a PartialSuccessError is returned.
authConfig := ServerAuthCallbacks{
PasswordCallback: config.PasswordCallback,
PublicKeyCallback: config.PublicKeyCallback,
KeyboardInteractiveCallback: config.KeyboardInteractiveCallback,
GSSAPIWithMICConfig: config.GSSAPIWithMICConfig,
}
userAuthLoop: userAuthLoop:
for { for {
@ -416,8 +517,8 @@ userAuthLoop:
if err := s.transport.writePacket(Marshal(discMsg)); err != nil { if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
return nil, err return nil, err
} }
authErrs = append(authErrs, discMsg)
return nil, discMsg return nil, &ServerAuthError{Errors: authErrs}
} }
var userAuthReq userAuthRequestMsg var userAuthReq userAuthRequestMsg
@ -434,6 +535,11 @@ userAuthLoop:
return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service) return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
} }
if s.user != userAuthReq.User && partialSuccessReturned {
return nil, fmt.Errorf("ssh: client changed the user after a partial success authentication, previous user %q, current user %q",
s.user, userAuthReq.User)
}
s.user = userAuthReq.User s.user = userAuthReq.User
if !displayedBanner && config.BannerCallback != nil { if !displayedBanner && config.BannerCallback != nil {
@ -454,16 +560,18 @@ userAuthLoop:
switch userAuthReq.Method { switch userAuthReq.Method {
case "none": case "none":
if config.NoClientAuth { noneAuthCount++
authErr = nil // We don't allow none authentication after a partial success
} // response.
if config.NoClientAuth && !partialSuccessReturned {
// allow initial attempt of 'none' without penalty if config.NoClientAuthCallback != nil {
if authFailures == 0 { perms, authErr = config.NoClientAuthCallback(s)
authFailures-- } else {
authErr = nil
}
} }
case "password": case "password":
if config.PasswordCallback == nil { if authConfig.PasswordCallback == nil {
authErr = errors.New("ssh: password auth not configured") authErr = errors.New("ssh: password auth not configured")
break break
} }
@ -477,17 +585,17 @@ userAuthLoop:
return nil, parseError(msgUserAuthRequest) return nil, parseError(msgUserAuthRequest)
} }
perms, authErr = config.PasswordCallback(s, password) perms, authErr = authConfig.PasswordCallback(s, password)
case "keyboard-interactive": case "keyboard-interactive":
if config.KeyboardInteractiveCallback == nil { if authConfig.KeyboardInteractiveCallback == nil {
authErr = errors.New("ssh: keyboard-interactive auth not configured") authErr = errors.New("ssh: keyboard-interactive auth not configured")
break break
} }
prompter := &sshClientKeyboardInteractive{s} prompter := &sshClientKeyboardInteractive{s}
perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge) perms, authErr = authConfig.KeyboardInteractiveCallback(s, prompter.Challenge)
case "publickey": case "publickey":
if config.PublicKeyCallback == nil { if authConfig.PublicKeyCallback == nil {
authErr = errors.New("ssh: publickey auth not configured") authErr = errors.New("ssh: publickey auth not configured")
break break
} }
@ -502,7 +610,7 @@ userAuthLoop:
return nil, parseError(msgUserAuthRequest) return nil, parseError(msgUserAuthRequest)
} }
algo := string(algoBytes) algo := string(algoBytes)
if !isAcceptableAlgo(algo) { if !contains(config.PublicKeyAuthAlgorithms, underlyingAlgo(algo)) {
authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo) authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
break break
} }
@ -521,11 +629,18 @@ userAuthLoop:
if !ok { if !ok {
candidate.user = s.user candidate.user = s.user
candidate.pubKeyData = pubKeyData candidate.pubKeyData = pubKeyData
candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey) candidate.perms, candidate.result = authConfig.PublicKeyCallback(s, pubKey)
if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" { _, isPartialSuccessError := candidate.result.(*PartialSuccessError)
candidate.result = checkSourceAddress(
if (candidate.result == nil || isPartialSuccessError) &&
candidate.perms != nil &&
candidate.perms.CriticalOptions != nil &&
candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
if err := checkSourceAddress(
s.RemoteAddr(), s.RemoteAddr(),
candidate.perms.CriticalOptions[sourceAddressCriticalOption]) candidate.perms.CriticalOptions[sourceAddressCriticalOption]); err != nil {
candidate.result = err
}
} }
cache.add(candidate) cache.add(candidate)
} }
@ -537,8 +652,8 @@ userAuthLoop:
if len(payload) > 0 { if len(payload) > 0 {
return nil, parseError(msgUserAuthRequest) return nil, parseError(msgUserAuthRequest)
} }
_, isPartialSuccessError := candidate.result.(*PartialSuccessError)
if candidate.result == nil { if candidate.result == nil || isPartialSuccessError {
okMsg := userAuthPubKeyOkMsg{ okMsg := userAuthPubKeyOkMsg{
Algo: algo, Algo: algo,
PubKey: pubKeyData, PubKey: pubKeyData,
@ -554,17 +669,26 @@ userAuthLoop:
if !ok || len(payload) > 0 { if !ok || len(payload) > 0 {
return nil, parseError(msgUserAuthRequest) return nil, parseError(msgUserAuthRequest)
} }
// Ensure the declared public key algo is compatible with the
// decoded one. This check will ensure we don't accept e.g.
// ssh-rsa-cert-v01@openssh.com algorithm with ssh-rsa public
// key type. The algorithm and public key type must be
// consistent: both must be certificate algorithms, or neither.
if !contains(algorithmsForKeyFormat(pubKey.Type()), algo) {
authErr = fmt.Errorf("ssh: public key type %q not compatible with selected algorithm %q",
pubKey.Type(), algo)
break
}
// Ensure the public key algo and signature algo // Ensure the public key algo and signature algo
// are supported. Compare the private key // are supported. Compare the private key
// algorithm name that corresponds to algo with // algorithm name that corresponds to algo with
// sig.Format. This is usually the same, but // sig.Format. This is usually the same, but
// for certs, the names differ. // for certs, the names differ.
if !isAcceptableAlgo(sig.Format) { if !contains(config.PublicKeyAuthAlgorithms, sig.Format) {
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format) authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
break break
} }
if underlyingAlgo(algo) != sig.Format { if !isAlgoCompatible(algo, sig.Format) {
authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo) authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo)
break break
} }
@ -579,11 +703,11 @@ userAuthLoop:
perms = candidate.perms perms = candidate.perms
} }
case "gssapi-with-mic": case "gssapi-with-mic":
if config.GSSAPIWithMICConfig == nil { if authConfig.GSSAPIWithMICConfig == nil {
authErr = errors.New("ssh: gssapi-with-mic auth not configured") authErr = errors.New("ssh: gssapi-with-mic auth not configured")
break break
} }
gssapiConfig := config.GSSAPIWithMICConfig gssapiConfig := authConfig.GSSAPIWithMICConfig
userAuthRequestGSSAPI, err := parseGSSAPIPayload(userAuthReq.Payload) userAuthRequestGSSAPI, err := parseGSSAPIPayload(userAuthReq.Payload)
if err != nil { if err != nil {
return nil, parseError(msgUserAuthRequest) return nil, parseError(msgUserAuthRequest)
@ -635,53 +759,86 @@ userAuthLoop:
config.AuthLogCallback(s, userAuthReq.Method, authErr) config.AuthLogCallback(s, userAuthReq.Method, authErr)
} }
var bannerErr *BannerError
if errors.As(authErr, &bannerErr) {
if bannerErr.Message != "" {
bannerMsg := &userAuthBannerMsg{
Message: bannerErr.Message,
}
if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
return nil, err
}
}
}
if authErr == nil { if authErr == nil {
break userAuthLoop break userAuthLoop
} }
authFailures++ var failureMsg userAuthFailureMsg
if config.MaxAuthTries > 0 && authFailures >= config.MaxAuthTries {
// If we have hit the max attempts, don't bother sending the if partialSuccess, ok := authErr.(*PartialSuccessError); ok {
// final SSH_MSG_USERAUTH_FAILURE message, since there are // After a partial success error we don't allow changing the user
// no more authentication methods which can be attempted, // name and execute the NoClientAuthCallback.
// and this message may cause the client to re-attempt partialSuccessReturned = true
// authentication while we send the disconnect message.
// Continue, and trigger the disconnect at the start of // In case a partial success is returned, the server may send
// the loop. // a new set of authentication methods.
// authConfig = partialSuccess.Next
// The SSH specification is somewhat confusing about this,
// RFC 4252 Section 5.1 requires each authentication failure // Reset pubkey cache, as the new PublicKeyCallback might
// be responded to with a respective SSH_MSG_USERAUTH_FAILURE // accept a different set of public keys.
// message, but Section 4 says the server should disconnect cache = pubKeyCache{}
// after some number of attempts, but it isn't explicit which
// message should take precedence (i.e. should there be a failure // Send back a partial success message to the user.
// message than a disconnect message, or if we are going to failureMsg.PartialSuccess = true
// disconnect, should we only send that message.) } else {
// // Allow initial attempt of 'none' without penalty.
// Either way, OpenSSH disconnects immediately after the last if authFailures > 0 || userAuthReq.Method != "none" || noneAuthCount != 1 {
// failed authnetication attempt, and given they are typically authFailures++
// considered the golden implementation it seems reasonable }
// to match that behavior. if config.MaxAuthTries > 0 && authFailures >= config.MaxAuthTries {
continue // If we have hit the max attempts, don't bother sending the
// final SSH_MSG_USERAUTH_FAILURE message, since there are
// no more authentication methods which can be attempted,
// and this message may cause the client to re-attempt
// authentication while we send the disconnect message.
// Continue, and trigger the disconnect at the start of
// the loop.
//
// The SSH specification is somewhat confusing about this,
// RFC 4252 Section 5.1 requires each authentication failure
// be responded to with a respective SSH_MSG_USERAUTH_FAILURE
// message, but Section 4 says the server should disconnect
// after some number of attempts, but it isn't explicit which
// message should take precedence (i.e. should there be a failure
// message than a disconnect message, or if we are going to
// disconnect, should we only send that message.)
//
// Either way, OpenSSH disconnects immediately after the last
// failed authentication attempt, and given they are typically
// considered the golden implementation it seems reasonable
// to match that behavior.
continue
}
} }
var failureMsg userAuthFailureMsg if authConfig.PasswordCallback != nil {
if config.PasswordCallback != nil {
failureMsg.Methods = append(failureMsg.Methods, "password") failureMsg.Methods = append(failureMsg.Methods, "password")
} }
if config.PublicKeyCallback != nil { if authConfig.PublicKeyCallback != nil {
failureMsg.Methods = append(failureMsg.Methods, "publickey") failureMsg.Methods = append(failureMsg.Methods, "publickey")
} }
if config.KeyboardInteractiveCallback != nil { if authConfig.KeyboardInteractiveCallback != nil {
failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive") failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
} }
if config.GSSAPIWithMICConfig != nil && config.GSSAPIWithMICConfig.Server != nil && if authConfig.GSSAPIWithMICConfig != nil && authConfig.GSSAPIWithMICConfig.Server != nil &&
config.GSSAPIWithMICConfig.AllowLogin != nil { authConfig.GSSAPIWithMICConfig.AllowLogin != nil {
failureMsg.Methods = append(failureMsg.Methods, "gssapi-with-mic") failureMsg.Methods = append(failureMsg.Methods, "gssapi-with-mic")
} }
if len(failureMsg.Methods) == 0 { if len(failureMsg.Methods) == 0 {
return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") return nil, errors.New("ssh: no authentication methods available")
} }
if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil { if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil {

View File

@ -13,7 +13,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"sync" "sync"
) )
@ -124,7 +123,7 @@ type Session struct {
// output and error. // output and error.
// //
// If either is nil, Run connects the corresponding file // If either is nil, Run connects the corresponding file
// descriptor to an instance of ioutil.Discard. There is a // descriptor to an instance of io.Discard. There is a
// fixed amount of buffering that is shared for the two streams. // fixed amount of buffering that is shared for the two streams.
// If either blocks it may eventually cause the remote // If either blocks it may eventually cause the remote
// command to block. // command to block.
@ -506,7 +505,7 @@ func (s *Session) stdout() {
return return
} }
if s.Stdout == nil { if s.Stdout == nil {
s.Stdout = ioutil.Discard s.Stdout = io.Discard
} }
s.copyFuncs = append(s.copyFuncs, func() error { s.copyFuncs = append(s.copyFuncs, func() error {
_, err := io.Copy(s.Stdout, s.ch) _, err := io.Copy(s.Stdout, s.ch)
@ -519,7 +518,7 @@ func (s *Session) stderr() {
return return
} }
if s.Stderr == nil { if s.Stderr == nil {
s.Stderr = ioutil.Discard s.Stderr = io.Discard
} }
s.copyFuncs = append(s.copyFuncs, func() error { s.copyFuncs = append(s.copyFuncs, func() error {
_, err := io.Copy(s.Stderr, s.ch.Stderr()) _, err := io.Copy(s.Stderr, s.ch.Stderr())

View File

@ -5,6 +5,7 @@
package ssh package ssh
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -332,6 +333,40 @@ func (l *tcpListener) Addr() net.Addr {
return l.laddr return l.laddr
} }
// DialContext initiates a connection to the addr from the remote host.
//
// The provided Context must be non-nil. If the context expires before the
// connection is complete, an error is returned. Once successfully connected,
// any expiration of the context will not affect the connection.
//
// See func Dial for additional information.
func (c *Client) DialContext(ctx context.Context, n, addr string) (net.Conn, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
type connErr struct {
conn net.Conn
err error
}
ch := make(chan connErr)
go func() {
conn, err := c.Dial(n, addr)
select {
case ch <- connErr{conn, err}:
case <-ctx.Done():
if conn != nil {
conn.Close()
}
}
}()
select {
case res := <-ch:
return res.conn, res.err
case <-ctx.Done():
return nil, ctx.Err()
}
}
// Dial initiates a connection to the addr from the remote host. // Dial initiates a connection to the addr from the remote host.
// The resulting connection has a zero LocalAddr() and RemoteAddr(). // The resulting connection has a zero LocalAddr() and RemoteAddr().
func (c *Client) Dial(n, addr string) (net.Conn, error) { func (c *Client) Dial(n, addr string) (net.Conn, error) {

View File

@ -17,7 +17,8 @@ import (
const debugTransport = false const debugTransport = false
const ( const (
gcmCipherID = "aes128-gcm@openssh.com" gcm128CipherID = "aes128-gcm@openssh.com"
gcm256CipherID = "aes256-gcm@openssh.com"
aes128cbcID = "aes128-cbc" aes128cbcID = "aes128-cbc"
tripledescbcID = "3des-cbc" tripledescbcID = "3des-cbc"
) )
@ -48,6 +49,9 @@ type transport struct {
rand io.Reader rand io.Reader
isClient bool isClient bool
io.Closer io.Closer
strictMode bool
initialKEXDone bool
} }
// packetCipher represents a combination of SSH encryption/MAC // packetCipher represents a combination of SSH encryption/MAC
@ -73,6 +77,18 @@ type connectionState struct {
pendingKeyChange chan packetCipher pendingKeyChange chan packetCipher
} }
func (t *transport) setStrictMode() error {
if t.reader.seqNum != 1 {
return errors.New("ssh: sequence number != 1 when strict KEX mode requested")
}
t.strictMode = true
return nil
}
func (t *transport) setInitialKEXDone() {
t.initialKEXDone = true
}
// prepareKeyChange sets up key material for a keychange. The key changes in // prepareKeyChange sets up key material for a keychange. The key changes in
// both directions are triggered by reading and writing a msgNewKey packet // both directions are triggered by reading and writing a msgNewKey packet
// respectively. // respectively.
@ -111,11 +127,12 @@ func (t *transport) printPacket(p []byte, write bool) {
// Read and decrypt next packet. // Read and decrypt next packet.
func (t *transport) readPacket() (p []byte, err error) { func (t *transport) readPacket() (p []byte, err error) {
for { for {
p, err = t.reader.readPacket(t.bufReader) p, err = t.reader.readPacket(t.bufReader, t.strictMode)
if err != nil { if err != nil {
break break
} }
if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) { // in strict mode we pass through DEBUG and IGNORE packets only during the initial KEX
if len(p) == 0 || (t.strictMode && !t.initialKEXDone) || (p[0] != msgIgnore && p[0] != msgDebug) {
break break
} }
} }
@ -126,7 +143,7 @@ func (t *transport) readPacket() (p []byte, err error) {
return p, err return p, err
} }
func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { func (s *connectionState) readPacket(r *bufio.Reader, strictMode bool) ([]byte, error) {
packet, err := s.packetCipher.readCipherPacket(s.seqNum, r) packet, err := s.packetCipher.readCipherPacket(s.seqNum, r)
s.seqNum++ s.seqNum++
if err == nil && len(packet) == 0 { if err == nil && len(packet) == 0 {
@ -139,6 +156,9 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
select { select {
case cipher := <-s.pendingKeyChange: case cipher := <-s.pendingKeyChange:
s.packetCipher = cipher s.packetCipher = cipher
if strictMode {
s.seqNum = 0
}
default: default:
return nil, errors.New("ssh: got bogus newkeys message") return nil, errors.New("ssh: got bogus newkeys message")
} }
@ -169,10 +189,10 @@ func (t *transport) writePacket(packet []byte) error {
if debugTransport { if debugTransport {
t.printPacket(packet, true) t.printPacket(packet, true)
} }
return t.writer.writePacket(t.bufWriter, t.rand, packet) return t.writer.writePacket(t.bufWriter, t.rand, packet, t.strictMode)
} }
func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte, strictMode bool) error {
changeKeys := len(packet) > 0 && packet[0] == msgNewKeys changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet) err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet)
@ -187,6 +207,9 @@ func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []
select { select {
case cipher := <-s.pendingKeyChange: case cipher := <-s.pendingKeyChange:
s.packetCipher = cipher s.packetCipher = cipher
if strictMode {
s.seqNum = 0
}
default: default:
panic("ssh: no key material for msgNewKeys") panic("ssh: no key material for msgNewKeys")
} }

4
vendor/golang.org/x/net/LICENSE generated vendored
View File

@ -1,4 +1,4 @@
Copyright (c) 2009 The Go Authors. All rights reserved. Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are
@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the in the documentation and/or other materials provided with the
distribution. distribution.
* Neither the name of Google Inc. nor the names of its * Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from contributors may be used to endorse or promote products derived from
this software without specific prior written permission. this software without specific prior written permission.

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.7 //go:build go1.7
// +build go1.7
package context package context

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.9 //go:build go1.9
// +build go1.9
package context package context

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.7 //go:build !go1.7
// +build !go1.7
package context package context

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.9 //go:build !go1.9
// +build !go1.9
package context package context

View File

@ -289,7 +289,7 @@ func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter,
case AuthMethodNotRequired: case AuthMethodNotRequired:
return nil return nil
case AuthMethodUsernamePassword: case AuthMethodUsernamePassword:
if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 { if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) > 255 {
return errors.New("invalid username/password") return errors.New("invalid username/password")
} }
b := []byte{authUsernamePasswordVersion} b := []byte{authUsernamePasswordVersion}

View File

@ -137,9 +137,7 @@ func (p *PerHost) AddNetwork(net *net.IPNet) {
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of // AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
// "example.com" matches "example.com" and all of its subdomains. // "example.com" matches "example.com" and all of its subdomains.
func (p *PerHost) AddZone(zone string) { func (p *PerHost) AddZone(zone string) {
if strings.HasSuffix(zone, ".") { zone = strings.TrimSuffix(zone, ".")
zone = zone[:len(zone)-1]
}
if !strings.HasPrefix(zone, ".") { if !strings.HasPrefix(zone, ".") {
zone = "." + zone zone = "." + zone
} }
@ -148,8 +146,6 @@ func (p *PerHost) AddZone(zone string) {
// AddHost specifies a host name that will use the bypass proxy. // AddHost specifies a host name that will use the bypass proxy.
func (p *PerHost) AddHost(host string) { func (p *PerHost) AddHost(host string) {
if strings.HasSuffix(host, ".") { host = strings.TrimSuffix(host, ".")
host = host[:len(host)-1]
}
p.bypassHosts = append(p.bypassHosts, host) p.bypassHosts = append(p.bypassHosts, host)
} }

12
vendor/modules.txt vendored
View File

@ -284,16 +284,14 @@ github.com/xanzy/ssh-agent
# github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 # github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
## explicit; go 1.15 ## explicit; go 1.15
github.com/xo/terminfo github.com/xo/terminfo
# golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa # golang.org/x/crypto v0.31.0
## explicit; go 1.17 ## explicit; go 1.20
golang.org/x/crypto/blowfish golang.org/x/crypto/blowfish
golang.org/x/crypto/cast5 golang.org/x/crypto/cast5
golang.org/x/crypto/chacha20 golang.org/x/crypto/chacha20
golang.org/x/crypto/curve25519 golang.org/x/crypto/curve25519
golang.org/x/crypto/curve25519/internal/field golang.org/x/crypto/internal/alias
golang.org/x/crypto/ed25519
golang.org/x/crypto/internal/poly1305 golang.org/x/crypto/internal/poly1305
golang.org/x/crypto/internal/subtle
golang.org/x/crypto/openpgp golang.org/x/crypto/openpgp
golang.org/x/crypto/openpgp/armor golang.org/x/crypto/openpgp/armor
golang.org/x/crypto/openpgp/elgamal golang.org/x/crypto/openpgp/elgamal
@ -308,8 +306,8 @@ golang.org/x/crypto/ssh/knownhosts
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/exp/constraints golang.org/x/exp/constraints
golang.org/x/exp/slices golang.org/x/exp/slices
# golang.org/x/net v0.7.0 # golang.org/x/net v0.33.0
## explicit; go 1.17 ## explicit; go 1.18
golang.org/x/net/context golang.org/x/net/context
golang.org/x/net/internal/socks golang.org/x/net/internal/socks
golang.org/x/net/proxy golang.org/x/net/proxy