Source File
cipher.go
Belonging Package
golang.org/x/crypto/ssh
package ssh
import (
)
const (
packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher.
maxPacket = 256 * 1024
)
type noneCipher struct{}
func ( noneCipher) (, []byte) {
copy(, )
}
func (, []byte) (cipher.Stream, error) {
, := aes.NewCipher()
if != nil {
return nil,
}
return cipher.NewCTR(, ), nil
}
func (, []byte) (cipher.Stream, error) {
return rc4.NewCipher()
}
type cipherMode struct {
keySize int
ivSize int
create func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error)
}
func ( int, func(, []byte) (cipher.Stream, error)) func(, []byte, []byte, directionAlgorithms) (packetCipher, error) {
return func(, , []byte, directionAlgorithms) (packetCipher, error) {
, := (, )
if != nil {
return nil,
}
var []byte
if > 0 {
= make([]byte, 512)
}
for := ; > 0; {
:=
if > len() {
= len()
}
.XORKeyStream([:], [:])
-=
}
:= macModes[.MAC].new()
return &streamPacketCipher{
mac: ,
etm: macModes[.MAC].etm,
macResult: make([]byte, .Size()),
cipher: ,
}, nil
}
}
"aes128-ctr": {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
"aes192-ctr": {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
"aes256-ctr": {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
"arcfour128": {16, 0, streamCipherMode(1536, newRC4)},
"arcfour256": {32, 0, streamCipherMode(1536, newRC4)},
"arcfour": {16, 0, streamCipherMode(0, newRC4)},
gcmCipherID: {16, 12, newGCMCipher},
chacha20Poly1305ID: {64, 0, newChaCha20Cipher},
aes128cbcID: {16, aes.BlockSize, newAESCBCCipher},
tripledescbcID: {24, des.BlockSize, newTripleDESCBCCipher},
}
const prefixLen = 5
prefix [prefixLen]byte
seqNumBytes [4]byte
padding [2 * packetSizeMultiple]byte
packetData []byte
macResult []byte
}
func ( *streamPacketCipher) ( uint32, io.Reader) ([]byte, error) {
if , := io.ReadFull(, .prefix[:]); != nil {
return nil,
}
var [1]byte
if .mac != nil && .etm {
copy([:], .prefix[4:5])
.cipher.XORKeyStream(.prefix[4:5], .prefix[4:5])
} else {
.cipher.XORKeyStream(.prefix[:], .prefix[:])
}
:= binary.BigEndian.Uint32(.prefix[0:4])
:= uint32(.prefix[4])
var uint32
if .mac != nil {
.mac.Reset()
binary.BigEndian.PutUint32(.seqNumBytes[:], )
.mac.Write(.seqNumBytes[:])
if .etm {
.mac.Write(.prefix[:4])
.mac.Write([:])
} else {
.mac.Write(.prefix[:])
}
= uint32(.mac.Size())
}
if <= +1 {
return nil, errors.New("ssh: invalid packet length, packet too small")
}
if > maxPacket {
return nil, errors.New("ssh: invalid packet length, packet too large")
}
if uint32(cap(.packetData)) < -1+ {
.packetData = make([]byte, -1+)
} else {
.packetData = .packetData[:-1+]
}
if , := io.ReadFull(, .packetData); != nil {
return nil,
}
:= .packetData[-1:]
:= .packetData[:-1]
if .mac != nil && .etm {
.mac.Write()
}
.cipher.XORKeyStream(, )
if .mac != nil {
if !.etm {
.mac.Write()
}
.macResult = .mac.Sum(.macResult[:0])
if subtle.ConstantTimeCompare(.macResult, ) != 1 {
return nil, errors.New("ssh: MAC failure")
}
}
return .packetData[:--1], nil
}
= 4
}
:= packetSizeMultiple - (prefixLen+len()-)%packetSizeMultiple
if < 4 {
+= packetSizeMultiple
}
:= len() + 1 +
binary.BigEndian.PutUint32(.prefix[:], uint32())
.prefix[4] = byte()
:= .padding[:]
if , := io.ReadFull(, ); != nil {
return
}
if .mac != nil {
.mac.Reset()
binary.BigEndian.PutUint32(.seqNumBytes[:], )
.mac.Write(.seqNumBytes[:])
.cipher.XORKeyStream(.prefix[:], .prefix[:])
}
.cipher.XORKeyStream(, )
.cipher.XORKeyStream(, )
.mac.Write()
.mac.Write()
}
if , := .Write(.prefix[:]); != nil {
return
}
if , := .Write(); != nil {
return
}
if , := .Write(); != nil {
return
}
if .mac != nil {
.macResult = .mac.Sum(.macResult[:0])
if , := .Write(.macResult); != nil {
return
}
}
return nil
}
type gcmCipher struct {
aead cipher.AEAD
prefix [4]byte
iv []byte
buf []byte
}
func (, , []byte, directionAlgorithms) (packetCipher, error) {
, := aes.NewCipher()
if != nil {
return nil,
}
, := cipher.NewGCM()
if != nil {
return nil,
}
return &gcmCipher{
aead: ,
iv: ,
}, nil
}
const gcmTagSize = 16
:= byte(packetSizeMultiple - (1+len())%packetSizeMultiple)
if < 4 {
+= packetSizeMultiple
}
:= uint32(len() + int() + 1)
binary.BigEndian.PutUint32(.prefix[:], )
if , := .Write(.prefix[:]); != nil {
return
}
if cap(.buf) < int() {
.buf = make([]byte, )
} else {
.buf = .buf[:]
}
.buf[0] =
copy(.buf[1:], )
if , := io.ReadFull(, .buf[1+len():]); != nil {
return
}
.buf = .aead.Seal(.buf[:0], .iv, .buf, .prefix[:])
if , := .Write(.buf); != nil {
return
}
.incIV()
return nil
}
func ( *gcmCipher) () {
for := 4 + 7; >= 4; -- {
.iv[]++
if .iv[] != 0 {
break
}
}
}
func ( *gcmCipher) ( uint32, io.Reader) ([]byte, error) {
if , := io.ReadFull(, .prefix[:]); != nil {
return nil,
}
:= binary.BigEndian.Uint32(.prefix[:])
if > maxPacket {
return nil, errors.New("ssh: max packet length exceeded")
}
if cap(.buf) < int(+gcmTagSize) {
.buf = make([]byte, +gcmTagSize)
} else {
.buf = .buf[:+gcmTagSize]
}
if , := io.ReadFull(, .buf); != nil {
return nil,
}
, := .aead.Open(.buf[:0], .iv, .buf, .prefix[:])
if != nil {
return nil,
}
.incIV()
:= [0]
seqNumBytes [4]byte
packetData []byte
macResult []byte
oracleCamouflage uint32
}
func ( cipher.Block, , , []byte, directionAlgorithms) (packetCipher, error) {
:= &cbcCipher{
mac: macModes[.MAC].new(),
decrypter: cipher.NewCBCDecrypter(, ),
encrypter: cipher.NewCBCEncrypter(, ),
packetData: make([]byte, 1024),
}
if .mac != nil {
.macSize = uint32(.mac.Size())
}
return , nil
}
func (, , []byte, directionAlgorithms) (packetCipher, error) {
, := aes.NewCipher()
if != nil {
return nil,
}
, := newCBCCipher(, , , , )
if != nil {
return nil,
}
return , nil
}
func (, , []byte, directionAlgorithms) (packetCipher, error) {
, := des.NewTripleDESCipher()
if != nil {
return nil,
}
, := newCBCCipher(, , , , )
if != nil {
return nil,
}
return , nil
}
func (, int) uint32 {
if > {
return uint32()
}
return uint32()
}
const (
cbcMinPacketSizeMultiple = 8
cbcMinPacketSize = 16
cbcMinPaddingSize = 4
)
if (+4)%maxUInt32(cbcMinPacketSizeMultiple, ) != 0 {
return nil, cbcError("ssh: invalid packet length multiple")
}
:= uint32([4])
if < cbcMinPaddingSize || <= +1 {
return nil, cbcError("ssh: invalid packet length")
}
:= 4 +
:= -
:= + .macSize
.packetData = make([]byte, )
copy(.packetData, )
} else {
.packetData = .packetData[:]
}
, := io.ReadFull(, .packetData[:])
if != nil {
return nil,
}
.oracleCamouflage -= uint32()
:= .packetData[:]
.decrypter.CryptBlocks(, )
:= .packetData[:]
if .mac != nil {
.mac.Reset()
binary.BigEndian.PutUint32(.seqNumBytes[:], )
.mac.Write(.seqNumBytes[:])
.mac.Write(.packetData[:])
.macResult = .mac.Sum(.macResult[:0])
if subtle.ConstantTimeCompare(.macResult, ) != 1 {
return nil, cbcError("ssh: MAC failure")
}
}
return .packetData[prefixLen:], nil
}
func ( *cbcCipher) ( uint32, io.Writer, io.Reader, []byte) error {
:= maxUInt32(cbcMinPacketSizeMultiple, .encrypter.BlockSize())
:= + .macSize
if uint32(cap(.packetData)) < {
.packetData = make([]byte, , )
} else {
.packetData = .packetData[:]
}
:= .packetData
= [1:]
copy(, )
.packetData = .mac.Sum(.packetData)
}
.encrypter.CryptBlocks(.packetData[:], .packetData[:])
if , := .Write(.packetData); != nil {
return
}
return nil
}
const chacha20Poly1305ID = "chacha20-poly1305@openssh.com"
type chacha20Poly1305Cipher struct {
lengthKey [32]byte
contentKey [32]byte
buf []byte
}
func (, , []byte, directionAlgorithms) (packetCipher, error) {
if len() != 64 {
panic(len())
}
:= &chacha20Poly1305Cipher{
buf: make([]byte, 256),
}
copy(.contentKey[:], [:32])
copy(.lengthKey[:], [32:])
return , nil
}
func ( *chacha20Poly1305Cipher) ( uint32, io.Reader) ([]byte, error) {
:= make([]byte, 12)
binary.BigEndian.PutUint32([8:], )
, := chacha20.NewUnauthenticatedCipher(.contentKey[:], )
if != nil {
return nil,
}
var , [32]byte
.XORKeyStream([:], [:])
.XORKeyStream([:], [:]) // skip the next 32 bytes
:= .buf[:4]
if , := io.ReadFull(, ); != nil {
return nil,
}
var [4]byte
, := chacha20.NewUnauthenticatedCipher(.lengthKey[:], )
if != nil {
return nil,
}
.XORKeyStream([:], )
:= binary.BigEndian.Uint32([:])
if > maxPacket {
return nil, errors.New("ssh: invalid packet length, packet too large")
}
:= 4 +
:= + poly1305.TagSize
if uint32(cap(.buf)) < {
.buf = make([]byte, )
copy(.buf[:], )
} else {
.buf = .buf[:]
}
if , := io.ReadFull(, .buf[4:]); != nil {
return nil,
}
var [poly1305.TagSize]byte
copy([:], .buf[:])
if !poly1305.Verify(&, .buf[:], &) {
return nil, errors.New("ssh: MAC failure")
}
:= .buf[4:]
.XORKeyStream(, )
:= [0]
return nil, fmt.Errorf("ssh: illegal padding %d", )
}
if int()+1 >= len() {
return nil, fmt.Errorf("ssh: padding %d too large", )
}
= [1 : len()-int()]
return , nil
}
func ( *chacha20Poly1305Cipher) ( uint32, io.Writer, io.Reader, []byte) error {
:= make([]byte, 12)
binary.BigEndian.PutUint32([8:], )
, := chacha20.NewUnauthenticatedCipher(.contentKey[:], )
if != nil {
return
}
var , [32]byte
.XORKeyStream([:], [:])
.XORKeyStream([:], [:]) // skip the next 32 bytes
const = 8
:= - (1+len())%
if < 4 {
+=
}
:= 4 + 1 + len() + + poly1305.TagSize
if cap(.buf) < {
.buf = make([]byte, )
} else {
.buf = .buf[:]
}
binary.BigEndian.PutUint32(.buf, uint32(1+len()+))
, := chacha20.NewUnauthenticatedCipher(.lengthKey[:], )
if != nil {
return
}
.XORKeyStream(.buf, .buf[:4])
.buf[4] = byte()
copy(.buf[5:], )
:= 5 + len() +
if , := io.ReadFull(, .buf[5+len():]); != nil {
return
}
.XORKeyStream(.buf[4:], .buf[4:])
var [poly1305.TagSize]byte
poly1305.Sum(&, .buf[:], &)
copy(.buf[:], [:])
if , := .Write(.buf); != nil {
return
}
return nil
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |