Go &引用;运行时错误:切片边界超出范围“;在加密聊天中

Go &引用;运行时错误:切片边界超出范围“;在加密聊天中,go,encryption,server,slice,nacl-cryptography,Go,Encryption,Server,Slice,Nacl Cryptography,更新:多亏了peterSO,错误似乎是随机字节(读取为字符串)将包含“\n”,这将导致换行和错误。 问题是两者都不是 io.Copy(conn, bytes.NewReader(encrypted)) 也不是 工作。 有人知道如何将chipertext写入康涅狄格州吗 原创帖子: 聊天程序由一个服务器和两个客户端组成。它使用TLS和NaCl进行(端到端)加密。在3/4的情况下,它可以工作,但有时我会出错: panic: runtime error: slice bounds out of ra

更新:多亏了peterSO,错误似乎是随机字节(读取为字符串)将包含“\n”,这将导致换行和错误。 问题是两者都不是

io.Copy(conn, bytes.NewReader(encrypted))
也不是

工作。 有人知道如何将chipertext写入康涅狄格州吗

原创帖子: 聊天程序由一个服务器和两个客户端组成。它使用TLS和NaCl进行(端到端)加密。在3/4的情况下,它可以工作,但有时我会出错:

panic: runtime error: slice bounds out of range

goroutine 34 [running]:
main.handleConnection(0x600a60, 0xc04246c000)
    path-to/client.go:79 
+0x3a6
created by main.main
    path-to/client.go:44 
+0x436
exit status 2
第44行呼叫

go handleConnection(conn)
第79行是“解密”行:

完整的代码在下面。由于它在没有加密的情况下可以完美地工作,并且加密的测试实现也可以工作,所以我要指出客户端-服务器-客户端之间的传输。通常切片的长度不应该改变,因为输出应该保持不变

客户阅读:

package main

import (
    "bufio"
    crypto_rand "crypto/rand"
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "net"
    "os"

    "golang.org/x/crypto/nacl/box"
)

func main() {
    cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
    if err != nil {
        log.Fatalln("Unable to load cert", err)
    }

    clientCACert, err := ioutil.ReadFile("cert.pem")
    if err != nil {
        log.Fatal("Unable to open cert", err)
    }

    clientCertPool := x509.NewCertPool()
    clientCertPool.AppendCertsFromPEM(clientCACert)

    conf := &tls.Config{
        Certificates: []tls.Certificate{cert},
        RootCAs:      clientCertPool,
        //InsecureSkipVerify: true,
    }

    conn, err := tls.Dial("tcp", "localhost:443", conf)
    if err != nil {
        log.Fatal(err)
    }

    defer conn.Close()
    go handleConnection(conn)
    for {
        stdin := bufio.NewReader(os.Stdin)
        textIn, err := stdin.ReadBytes('\n')
        if err != nil {
            fmt.Println(err)
        }
        var nonce [24]byte
        if _, err := io.ReadFull(crypto_rand.Reader, nonce[:]); err != nil {
            panic(err)
        }
        senderPrivateKey := readKey("localPrivate")
        recipientPublicKey := readKey("remotePublic")
        encrypted := box.Seal(nonce[:], textIn, &nonce, recipientPublicKey, senderPrivateKey)
        text := BytesToString(encrypted)
        fmt.Fprintf(conn, text+"\n")
    }

}

func handleConnection(conn net.Conn) {
    defer conn.Close()
    input := bufio.NewScanner(conn)
    for input.Scan() {
        senderPublicKey := readKey("localPublic")
        recipientPrivateKey := readKey("remotePrivate")
        var decryptNonce [24]byte
        encrypted := input.Bytes()
        copy(decryptNonce[:], encrypted[:24])
        decrypted, ok := box.Open(nil, encrypted[24:], &decryptNonce, senderPublicKey, recipientPrivateKey)
        if !ok {
            fmt.Println("decryption error")
        }
        fmt.Println(BytesToString(decrypted))
    }
}

//BytesToString converts []byte to str
func BytesToString(data []byte) string {
    return string(data[:])
}

//Read the keys from file, pass filename without .ending
func readKey(name string) (prv *[32]byte) {
    prv = new([32]byte)
    f, err := os.Open(name + ".key")
    if err != nil {
        panic(err)
    }
    _, err = io.ReadFull(f, prv[:])
    if err != nil {
        panic(err)
    }

    return
}
服务器端:

package main

import (
    "bufio"
    "crypto/tls"
    "fmt"
    "log"
    "net"
)


type client chan<- string // an outgoing message channel

var (
    entering = make(chan client)
    leaving  = make(chan client)
    messages = make(chan string) // all incoming client messages
)

// Broadcast incoming message to all clients' outgoing message channels.
func broadcaster() {
    clients := make(map[client]bool) // all connected clients
    for {
        select {
        case msg := <-messages:
            for cli := range clients {
                cli <- msg
            }
        case cli := <-entering:
            clients[cli] = true
        case cli := <-leaving:
            delete(clients, cli)
            close(cli)
        }
    }
}

func handleConn(conn net.Conn) {
    ch := make(chan string) // outgoing client messages
    go clientWriter(conn, ch)

    //who := conn.RemoteAddr().String()
    entering <- ch
    //messages <- who + " has arrived"
    input := bufio.NewScanner(conn)
    for input.Scan() {
        messages <- input.Text()
    }
    //messages <- who + " has left"
    leaving <- ch
    conn.Close()
}

func clientWriter(conn net.Conn, ch <-chan string) {
    for msg := range ch {
        fmt.Fprintln(conn, msg)
    }
}




func main() {
    cer, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
    if err != nil {
        log.Println(err)
        return
    }

    config := &tls.Config{
        Certificates: []tls.Certificate{cer},
        //PFS, this will reject client with RSA certificates
        CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
        //Force it server side
        PreferServerCipherSuites: true,
        //Force TLS Version
        MinVersion: tls.VersionTLS12}

    listener, err := tls.Listen("tcp", "localhost:443", config)
    if err != nil {
        log.Fatal(err)
    }

    go broadcaster()
    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Print(err)
            continue
        }
        go handleConn(conn)
    }
}
主程序包
进口(
“布菲奥”
“加密/tls”
“fmt”
“日志”
“净额”
)

键入client chan没有明显的原因,您希望
len(input.Bytes())>=24
。当它不是时:
panic:runtime错误:切片界限超出范围

比如说,

package main

func main() {
    /*
       var decryptNonce [24]byte
       encrypted := input.Bytes()
       copy(decryptNonce[:], encrypted[:24])
       decrypted, ok := box.Open(nil, encrypted[24:], &decryptNonce, senderPublicKey, recipientPrivateKey)
       if !ok {
           fmt.Println("decryption error")
       }
    */

    var decryptNonce [24]byte
    encrypted := make([]byte, 23, 24) // len(input.Bytes()) < 24
    copy(decryptNonce[:], encrypted[:24])
    // panic: runtime error: slice bounds out of range
    _ = encrypted[24:]
}

Halux9000评论:

这很可能是原因。但是
len(input.Bytes())>=24
当通过生成
input.Bytes()
时,应为true

var nonce [24]byte
if _, err := io.ReadFull(crypto_rand.Reader, nonce[:]); err != nil {
  panic(err)
}
senderPrivateKey := readKey("localPrivate")
recipientPublicKey := readKey("remotePublic")
encrypted := box.Seal(nonce[:], textIn, &nonce, recipientPublicKey, senderPrivateKey)
text := BytesToString(encrypted)
fmt.Fprintf(conn, text+"\n")
没有传输的加密是有效的。那么它在哪里呢/ 变了

我不相信你的“应该”论点。我相信这个节目

如果您有随机或加密的字节,那么其中一些将是换行符。我计算了前24个字节(nonce)中具有换行符的行的预期百分比为8.966%,并通过实验证实了这一点

package main

import (
    "bytes"
    "crypto/rand"
    "fmt"
    "io"
)

var nonce [24]byte

func expected() float64 {
    e := 0.0
    for range nonce {
        e += (float64(len(nonce)) - e) / 256
    }
    return e * 100 / float64(len(nonce))
}

func actual() float64 {
    a, n := 0, 1024*1024
    for i := 0; i < n; i++ {
        if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
            panic(err)
        }
        if bytes.IndexByte(nonce[:], '\n') >= 0 {
            a++
        }
    }
    return float64(a*100) / float64(n)
}

func main() {
    fmt.Printf("expected: %.3f%%\n", expected())
    fmt.Printf("actual:   %.3f%%\n", actual())
}

Halux9000评论:

你能建议另一种向conn发送密文字节的方法吗

您需要一个对消息内容不敏感的更健壮的消息协议。例如,使用消息内容长度作为消息内容的前缀

一个简单的例子

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "io"
)

func main() {
    // Connection
    var conn = new(bytes.Buffer)

    {
        // Server
        buf := make([]byte, 0, 2+64*1024)
        msgLen := uint16(16)
        buf = buf[0 : 2+msgLen]
        binary.BigEndian.PutUint16(buf[0:2], msgLen)
        for i := uint16(0); i < msgLen; i++ {
            buf[2+i] = byte(i)
        }
        fmt.Println(msgLen, len(buf), buf)
        n, err := conn.Write(buf)
        if err != nil {
            fmt.Println(n, err)
            return
        }
    }

    {
        // Client
        buf := make([]byte, 0, 2+64*1024)
        n, err := io.ReadFull(conn, buf[:2])
        if err != nil {
            fmt.Println(n, err)
            return
        }
        msgLen := binary.BigEndian.Uint16(buf[0:2])
        buf = buf[0 : 2+msgLen]
        n, err = io.ReadFull(conn, buf[2:2+msgLen])
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(msgLen, len(buf), buf)
    }
}

你确定恐慌发生在你设置的
解密的那一行,而不是上面的那一行吗?如果
encrypted[:24]
可以,那么
encrypted[24:][/code>也应该可以。根据VS code,这是带有“decrypted”的行。Go也计算从1开始的行,不是吗?我想,问题在于每个片段的末尾,因为加密的[:24]是有效的。也许
code
fmt.Fprintf(conn,text+“\n”)
code
是问题所在,因为它添加了(需要的)新行来打印?编辑:在上面的代码中是第74行(不是79行),因为我删除了一些与本文无关的评论。谢谢!这很可能是原因。但是当通过
var nonce[24]byte生成input.Bytes()时,
len(input.Bytes())>=24
应该为真,如果u,err:=io.ReadFull(crypto_rand.Reader,nonce[:]);呃!=nil{panic(err)}senderPrivateKey:=readKey(“localPrivate”)recipientPublicKey:=readKey(“remotePublic”)加密:=box.Seal(nonce[:],textIn,&nonce,recipientPublicKey,senderPrivateKey)text:=BytesToString(加密)fmt.Fprintf(conn,text+“\n”)
(请参见上文以获得更好的可读性)。没有传输的加密是有效的。那么它在哪里被缩短/改变了呢?谢谢你的努力。我完全同意。然而,从os.Stdin独立的加密/解密不会产生这种错误。这是golang的示例实现,添加了os.Stdin输入。。。你能指出我代码中的错误吗?问题是我在传输之前将cipthertext转换为字符串,并且字符串包含“\n”(对于[]字节来说这似乎不是问题)?你能建议另一种向conn发送密文字节的方法吗?
var nonce [24]byte
if _, err := io.ReadFull(crypto_rand.Reader, nonce[:]); err != nil {
  panic(err)
}
senderPrivateKey := readKey("localPrivate")
recipientPublicKey := readKey("remotePublic")
encrypted := box.Seal(nonce[:], textIn, &nonce, recipientPublicKey, senderPrivateKey)
text := BytesToString(encrypted)
fmt.Fprintf(conn, text+"\n")
package main

import (
    "bytes"
    "crypto/rand"
    "fmt"
    "io"
)

var nonce [24]byte

func expected() float64 {
    e := 0.0
    for range nonce {
        e += (float64(len(nonce)) - e) / 256
    }
    return e * 100 / float64(len(nonce))
}

func actual() float64 {
    a, n := 0, 1024*1024
    for i := 0; i < n; i++ {
        if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
            panic(err)
        }
        if bytes.IndexByte(nonce[:], '\n') >= 0 {
            a++
        }
    }
    return float64(a*100) / float64(n)
}

func main() {
    fmt.Printf("expected: %.3f%%\n", expected())
    fmt.Printf("actual:   %.3f%%\n", actual())
}
$ go run newlines.go
expected: 8.966%
actual:   8.943%
$ go run newlines.go
expected: 8.966%
actual:   8.956%
$ go run newlines.go
expected: 8.966%
actual:   8.976%
$ go run newlines.go
expected: 8.966%
actual:   8.992%
$
package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "io"
)

func main() {
    // Connection
    var conn = new(bytes.Buffer)

    {
        // Server
        buf := make([]byte, 0, 2+64*1024)
        msgLen := uint16(16)
        buf = buf[0 : 2+msgLen]
        binary.BigEndian.PutUint16(buf[0:2], msgLen)
        for i := uint16(0); i < msgLen; i++ {
            buf[2+i] = byte(i)
        }
        fmt.Println(msgLen, len(buf), buf)
        n, err := conn.Write(buf)
        if err != nil {
            fmt.Println(n, err)
            return
        }
    }

    {
        // Client
        buf := make([]byte, 0, 2+64*1024)
        n, err := io.ReadFull(conn, buf[:2])
        if err != nil {
            fmt.Println(n, err)
            return
        }
        msgLen := binary.BigEndian.Uint16(buf[0:2])
        buf = buf[0 : 2+msgLen]
        n, err = io.ReadFull(conn, buf[2:2+msgLen])
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(msgLen, len(buf), buf)
    }
}
16 18 [0 16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
16 18 [0 16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]