├── README ├── cmd ├── server │ └── main.go └── testclient │ └── main.go └── keyless.go /README: -------------------------------------------------------------------------------- 1 | go-keyless: a client for https://github.com/cloudflare/keyless 2 | 3 | My goal at the moment is just to implement the functionality required for 4 | 'testclient' to run. Injecting into crypto/tls is a larger, different project. 5 | 6 | To play with: 7 | 8 | Download and build keyless. Currently, Go can't talk to a keyless server 9 | because crypto/tls doesn't support ECDHE-ECDSA-AES256-GCM-SHA384 and 10 | ECDHE-RSA-AES256-GCM-SHA384, the only two ciphers keyless accepts. If you want 11 | to play, you need to patch your local keyless to include support for more 12 | ciphers. The easiest two to change to are ECDHE-ECDSA-AES128-GCM-SHA256 and 13 | ECDHE-RSA-AES128-GCM-SHA256. (256 -> 128, 384 -> 256). 14 | 15 | Start the keyless demo server 16 | 17 | keyless$ make run PORT=2047 18 | 19 | Run the test client. You'll need to point it at the test certificates that come 20 | with keyless. It runs some Pings(), a Decrypt(), and a Sign(). 21 | 22 | testclient$ go run main.go -server=localhost -port=2047 \ 23 | -client-cert=$KEYLESS/testing/client-cert/ecdsa/ecdsa-client.pem \ 24 | -client-key=$KEYLESS/testing/client-cert/ecdsa/ecdsa-client-key.pem \ 25 | -ca-file=$KEYLESS/testing/CAs/testca-keyserver.pem \ 26 | -private-key=$KEYLESS/testing/keys/private.key 27 | 28 | There's also a sample server included in cmd/server. 29 | -------------------------------------------------------------------------------- /cmd/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/x509" 7 | "encoding/binary" 8 | "encoding/pem" 9 | "flag" 10 | "io" 11 | "io/ioutil" 12 | "log" 13 | "net" 14 | "os" 15 | "path/filepath" 16 | "strconv" 17 | "sync" 18 | 19 | "github.com/dgryski/go-keyless" 20 | ) 21 | 22 | type lockedWriter struct { 23 | io.WriteCloser 24 | sync.Mutex 25 | Err error 26 | } 27 | 28 | func (l *lockedWriter) Write(b []byte) (int, error) { 29 | l.Lock() 30 | n, e := l.WriteCloser.Write(b) 31 | if e != nil { 32 | if l.Err == nil { 33 | l.Err = e 34 | } 35 | 36 | l.Close() 37 | } 38 | l.Unlock() 39 | return n, e 40 | } 41 | 42 | func handleRequests(conn io.ReadWriteCloser, keys map[[32]byte]*rsa.PrivateKey) { 43 | 44 | defer conn.Close() 45 | lwriter := &lockedWriter{WriteCloser: conn} 46 | 47 | for { 48 | 49 | var header [8]byte 50 | 51 | // FIXME(dgryski): need another timeout on these reads 52 | _, err := io.ReadFull(conn, header[:]) 53 | if err != nil { 54 | log.Println("error reading header:", err) 55 | 56 | // partial read -- unknown connection state 57 | break 58 | } 59 | 60 | rlen := binary.BigEndian.Uint16(header[2:]) 61 | 62 | response := make([]byte, rlen+8) 63 | copy(response, header[:]) 64 | 65 | _, err = io.ReadFull(conn, response[8:]) 66 | if err != nil { 67 | log.Println("error reading body:", err) 68 | // partial read -- unknown connection state 69 | break 70 | } 71 | 72 | go func() { 73 | 74 | p, op, params, err := keyless.UnpackRequest(response) 75 | if err != nil { 76 | writeErrorResponse(conn, p, op, err.(keyless.ErrCode), err, params) 77 | return 78 | } 79 | 80 | defer func() { 81 | if r := recover(); r != nil { 82 | writeErrorResponse(lwriter, p, op, keyless.ErrInternal, nil, params) 83 | } 84 | }() 85 | 86 | switch op { 87 | case keyless.OpPing: 88 | b := keyless.PackRequest(p.ID, keyless.OpPong, params) 89 | lwriter.Write(b) 90 | 91 | case keyless.OpRSADecrypt: 92 | 93 | key := getPrivateKey(keys, params.Digest) 94 | if key == nil { 95 | writeErrorResponse(lwriter, p, op, keyless.ErrKeyNotFound, nil, params) 96 | break 97 | } 98 | 99 | out, err := rsa.DecryptPKCS1v15(rand.Reader, key, params.Payload) 100 | if err != nil { 101 | writeErrorResponse(lwriter, p, op, keyless.ErrCryptoFailed, err, params) 102 | break 103 | } 104 | 105 | b := keyless.PackRequest(p.ID, keyless.OpResponse, &keyless.Params{Payload: out}) 106 | lwriter.Write(b) 107 | 108 | case keyless.OpRSASignMD5SHA1, 109 | keyless.OpRSASignSHA1, 110 | keyless.OpRSASignSHA224, 111 | keyless.OpRSASignSHA256, 112 | keyless.OpRSASignSHA384, 113 | keyless.OpRSASignSHA512: 114 | 115 | key := getPrivateKey(keys, params.Digest) 116 | if key == nil { 117 | writeErrorResponse(lwriter, p, op, keyless.ErrKeyNotFound, err, params) 118 | break 119 | } 120 | 121 | h := keyless.OpToHash(op) 122 | 123 | out, err := rsa.SignPKCS1v15(rand.Reader, key, h, params.Payload) 124 | if err != nil { 125 | writeErrorResponse(lwriter, p, op, keyless.ErrCryptoFailed, err, params) 126 | break 127 | } 128 | 129 | b := keyless.PackRequest(p.ID, keyless.OpResponse, &keyless.Params{Payload: out}) 130 | lwriter.Write(b) 131 | default: 132 | writeErrorResponse(lwriter, p, op, keyless.ErrBadOpcode, nil, params) 133 | } 134 | }() 135 | } 136 | } 137 | 138 | func writeErrorResponse(conn io.Writer, p *keyless.Packet, op keyless.OpCode, errcode keyless.ErrCode, err error, params *keyless.Params) (int, error) { 139 | log.Printf("%s: %08x: %s: %x: %s: %v", params.ClientIP, p.ID, op, params.Digest, errcode, err) 140 | b := keyless.PackRequest(p.ID, keyless.OpError, &keyless.Params{Payload: []byte{byte(errcode)}}) 141 | return conn.Write(b) 142 | } 143 | 144 | func getPrivateKey(keys map[[32]byte]*rsa.PrivateKey, paramDigest []byte) *rsa.PrivateKey { 145 | var digest [32]byte 146 | if len(paramDigest) != 32 { 147 | return nil 148 | } 149 | copy(digest[:], paramDigest) 150 | return keys[digest] 151 | } 152 | 153 | func main() { 154 | 155 | port := flag.Int("port", 2048, "listen port") 156 | keydir := flag.String("private-key-directory", "", "directory storing private keys") 157 | /* 158 | serverCert := flag.String("server-cert", "", "server certificate") 159 | serverKey := flag.String("server-key", "", "server key") 160 | caCert := flag.String("ca-cert", "", "ca certificate") 161 | */ 162 | 163 | flag.Parse() 164 | 165 | keys := make(map[[32]byte]*rsa.PrivateKey) 166 | 167 | // load all private keys 168 | filepath.Walk(*keydir, func(path string, info os.FileInfo, err error) error { 169 | 170 | if err != nil { 171 | return err 172 | } 173 | 174 | if info.IsDir() { 175 | return nil 176 | } 177 | 178 | pkeyData, err := ioutil.ReadFile(path) 179 | if err != nil { 180 | log.Println("error reading key", path, ", skipping") 181 | return nil 182 | } 183 | block, _ := pem.Decode(pkeyData) 184 | 185 | pkey, err := x509.ParsePKCS1PrivateKey(block.Bytes) 186 | if err != nil { 187 | log.Println("error parsing key", path, ", skipping") 188 | return nil 189 | } 190 | 191 | digest := keyless.DigestPublicModulus(&pkey.PublicKey) 192 | keys[digest] = pkey 193 | 194 | log.Printf("loaded %s: %x", path, digest) 195 | 196 | return nil 197 | }) 198 | 199 | if len(keys) == 0 { 200 | log.Fatal("no private keys loaded") 201 | } 202 | 203 | ln, e := net.Listen("tcp", ":"+strconv.Itoa(*port)) 204 | if e != nil { 205 | log.Fatal("listen error:", e) 206 | } 207 | 208 | log.Println("tcp server starting") 209 | 210 | for { 211 | lconn, err := ln.Accept() 212 | if err != nil { 213 | log.Println(err) 214 | continue 215 | } 216 | 217 | go handleRequests(lconn, keys) 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /cmd/testclient/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/rand" 7 | "crypto/rsa" 8 | "crypto/sha256" 9 | "crypto/tls" 10 | "crypto/x509" 11 | "encoding/pem" 12 | "flag" 13 | "fmt" 14 | "github.com/dgryski/go-keyless" 15 | "io/ioutil" 16 | "log" 17 | "net" 18 | "strconv" 19 | ) 20 | 21 | func main() { 22 | 23 | port := flag.Int("port", 0, "port to connect to") 24 | clientCert := flag.String("client-cert", "", "client certificate file") 25 | clientKey := flag.String("client-key", "", "client key") 26 | caFile := flag.String("ca-file", "", "ca file") 27 | server := flag.String("server", "", "server") 28 | privateKey := flag.String("private-key", "", "server's private key") 29 | useTLS := flag.Bool("tls", true, "use TLS") 30 | 31 | flag.Parse() 32 | 33 | var conn *keyless.Conn 34 | 35 | remote := *server + ":" + strconv.Itoa(*port) 36 | log.Printf("remote %+v\n", remote) 37 | 38 | if *useTLS { 39 | cert, err := tls.LoadX509KeyPair(*clientCert, *clientKey) 40 | if err != nil { 41 | log.Fatalln("unable to load private key:", err) 42 | } 43 | 44 | caCert, err := ioutil.ReadFile(*caFile) 45 | if err != nil { 46 | log.Fatalln("unable to load CA:", err) 47 | } 48 | 49 | roots := x509.NewCertPool() 50 | ok := roots.AppendCertsFromPEM(caCert) 51 | if !ok { 52 | log.Fatalln("failed to load CAs") 53 | } 54 | 55 | config := tls.Config{Certificates: []tls.Certificate{cert}, RootCAs: roots, InsecureSkipVerify: true} 56 | conn, err = keyless.DialTLS(remote, &config) 57 | if err != nil { 58 | log.Fatalf("dial error: %s", err) 59 | } 60 | } else { 61 | var err error 62 | conn, err = keyless.Dial(remote) 63 | if err != nil { 64 | log.Fatalf("dial error: %s", err) 65 | } 66 | } 67 | 68 | for i := 0; i < 10; i++ { 69 | r, err := conn.Ping(&keyless.Params{Payload: []byte("hello, world")}) 70 | fmt.Println(r, err) 71 | } 72 | 73 | pkeyData, err := ioutil.ReadFile(*privateKey) 74 | if err != nil { 75 | log.Fatal("unable to load private key:", err) 76 | } 77 | block, _ := pem.Decode(pkeyData) 78 | 79 | pkey, err := x509.ParsePKCS1PrivateKey(block.Bytes) 80 | if err != nil { 81 | log.Fatalln("unable to parse private key", err) 82 | } 83 | 84 | out, err := rsa.EncryptPKCS1v15(rand.Reader, &pkey.PublicKey, []byte("hello, world")) 85 | if err != nil { 86 | log.Fatalln("unable to encrypt:", err) 87 | } 88 | 89 | digest := keyless.DigestPublicModulus(&pkey.PublicKey) 90 | 91 | netIP := net.ParseIP("8.8.8.8") 92 | 93 | plain, err := conn.Decrypt(&keyless.Params{Digest: digest[:], Payload: out, ClientIP: netIP}) 94 | 95 | fmt.Printf("string(plain) %+v, err=%v\n", string(plain), err) 96 | 97 | hashed := sha256.Sum256([]byte("hello, world")) 98 | 99 | sig, err := rsa.SignPKCS1v15(rand.Reader, pkey, crypto.SHA256, hashed[:]) 100 | if err != nil { 101 | log.Fatalln("unable to encrypt:", err) 102 | } 103 | 104 | remotesig, err := conn.Sign(keyless.OpRSASignSHA256, &keyless.Params{Digest: digest[:], Payload: hashed[:]}) 105 | fmt.Println("signature match", bytes.Equal(sig, remotesig), "err=", err) 106 | 107 | digest[0]++ 108 | 109 | remotesig, err = conn.Sign(keyless.OpRSASignSHA256, &keyless.Params{Digest: digest[:], Payload: hashed[:], ClientIP: netIP}) 110 | fmt.Println("expect failure: notfound err=", err) 111 | 112 | // test pipelining 113 | start := make(chan bool) 114 | done := make(chan bool, 10) 115 | for i := 0; i < 10; i++ { 116 | go func() { 117 | <-start 118 | conn.Ping(nil) 119 | done <- true 120 | }() 121 | } 122 | close(start) 123 | 124 | for i := 0; i < 10; i++ { 125 | <-done 126 | } 127 | 128 | conn.Close() 129 | } 130 | -------------------------------------------------------------------------------- /keyless.go: -------------------------------------------------------------------------------- 1 | package keyless 2 | 3 | import ( 4 | "crypto" 5 | "crypto/rsa" 6 | "crypto/sha256" 7 | "crypto/tls" 8 | "encoding/binary" 9 | "encoding/hex" 10 | "errors" 11 | "fmt" 12 | "io" 13 | "net" 14 | "sync" 15 | "sync/atomic" 16 | ) 17 | 18 | // From kssl.h 19 | 20 | type Packet struct { 21 | VersionMaj byte 22 | VersionMin byte 23 | ID uint32 24 | Items []Item 25 | } 26 | 27 | const headerSize = 8 28 | 29 | // The current KSSL protocol version 30 | const ( 31 | VersionMaj byte = 0x01 32 | VersionMin = 0x00 33 | ) 34 | 35 | type Tag byte 36 | 37 | // Possible item tags 38 | const ( 39 | TagDigest Tag = 0x01 // An RSA key digest (see digest_public_modulus) 40 | TagSNI = 0x02 // Server name (optional) 41 | TagClientIP = 0x03 // Client IP (4 bytes for IPv4, 16 for IPv6) 42 | TagOPCODE = 0x11 // Requested operation (one of KSSL_OP_*) 43 | TagPayload = 0x12 // Payload 44 | TagPadding = 0x20 // Padding 45 | ) 46 | 47 | // This structure stores the value of a given tag 48 | type Item struct { 49 | Tag Tag // Tag to identify contents of item 50 | Data []byte // The block of data to decrypt or sign 51 | } 52 | 53 | type Params struct { 54 | Digest []byte 55 | SNI string 56 | ClientIP net.IP 57 | Payload []byte 58 | } 59 | 60 | // Number of bytes to pad responses to 61 | const padTo = 1024 62 | 63 | // A test message which will be echoed with its payload with the 64 | // operation changed to OP_PONG 65 | 66 | type OpCode byte 67 | 68 | // Possible values for KSSL_TAG_OPCODE 69 | const ( 70 | OpPing OpCode = 0xF1 71 | OpPong = 0xF2 72 | 73 | // Decrypt data encrypted using RSA with RSA_PKCS1_PADDING 74 | OpRSADecrypt OpCode = 0x01 75 | 76 | // Sign data using RSA 77 | OpRSASignMD5SHA1 OpCode = 0x02 78 | OpRSASignSHA1 = 0x03 79 | OpRSASignSHA224 = 0x04 80 | OpRSASignSHA256 = 0x05 81 | OpRSASignSHA384 = 0x06 82 | OpRSASignSHA512 = 0x07 83 | 84 | // Used to send a block of data back to the client (in response, for 85 | // example, to a KSSL_OP_RSA_DECRYPT) 86 | OpResponse OpCode = 0xF0 87 | 88 | // Some error occurred, explanation is single byte in payload 89 | OpError OpCode = 0xFF 90 | ) 91 | 92 | func OpToHash(op OpCode) crypto.Hash { 93 | 94 | switch op { 95 | case OpRSASignMD5SHA1: 96 | return crypto.MD5SHA1 97 | case OpRSASignSHA1: 98 | return crypto.SHA1 99 | case OpRSASignSHA224: 100 | return crypto.SHA224 101 | case OpRSASignSHA256: 102 | return crypto.SHA256 103 | case OpRSASignSHA384: 104 | return crypto.SHA384 105 | case OpRSASignSHA512: 106 | return crypto.SHA512 107 | } 108 | 109 | return 0 110 | } 111 | 112 | func (op OpCode) String() string { 113 | 114 | switch op { 115 | 116 | case OpPing: 117 | return "Ping" 118 | case OpPong: 119 | return "Pong" 120 | case OpRSADecrypt: 121 | return "RSA decrypt payload" 122 | case OpRSASignMD5SHA1: 123 | return "RSA sign MD5SHA1" 124 | case OpRSASignSHA1: 125 | return "RSA sign SHA1" 126 | case OpRSASignSHA224: 127 | return "RSA sign SHA224" 128 | case OpRSASignSHA256: 129 | return "RSA sign SHA256" 130 | case OpRSASignSHA384: 131 | return "RSA sign SHA384" 132 | case OpRSASignSHA512: 133 | return "RSA sign SHA512" 134 | case OpResponse: 135 | return "response" 136 | case OpError: 137 | return "error" 138 | } 139 | 140 | return fmt.Sprintf("bad opcode %x", byte(op)) 141 | } 142 | 143 | type ErrCode byte 144 | 145 | // Different error codes for OpError payload 146 | const ( 147 | ErrNone ErrCode = 0x00 148 | ErrCryptoFailed = 0x01 149 | ErrKeyNotFound = 0x02 150 | ErrRead = 0x03 151 | ErrVersionMismatch = 0x04 152 | ErrBadOpcode = 0x05 153 | ErrUnexpectedOpcode = 0x06 154 | ErrFormat = 0x07 155 | ErrInternal = 0x08 156 | ) 157 | 158 | func (e ErrCode) Error() string { 159 | 160 | switch e { 161 | case 0x00: 162 | return "success" 163 | case 0x01: 164 | return "cryptography failure" 165 | case 0x02: 166 | return "key not found" 167 | case 0x03: 168 | return "read error" 169 | case 0x04: 170 | return "version mismatch" 171 | case 0x05: 172 | return "bad opcode" 173 | case 0x06: 174 | return "unexpected opcode" 175 | case 0x07: 176 | return "format error" 177 | case 0x08: 178 | return "internal error" 179 | } 180 | 181 | return "unknown" 182 | } 183 | 184 | type Conn struct { 185 | conn net.Conn 186 | 187 | mu sync.Mutex 188 | pending map[uint32]chan []byte 189 | 190 | // data to be written to the socket 191 | write chan []byte 192 | 193 | // these are signals *from* the reader and writer routines that they're not going to process anymore 194 | doneRead chan bool 195 | doneWrite chan bool 196 | 197 | // we have been asked to close 198 | done chan bool 199 | 200 | id uint32 201 | } 202 | 203 | func Dial(remote string) (*Conn, error) { 204 | 205 | c, err := net.Dial("tcp", remote) 206 | if err != nil { 207 | return nil, err 208 | } 209 | 210 | return NewWithConn(c), nil 211 | 212 | } 213 | 214 | func DialTLS(remote string, config *tls.Config) (*Conn, error) { 215 | 216 | c, err := tls.Dial("tcp", remote, config) 217 | if err != nil { 218 | return nil, err 219 | } 220 | 221 | return NewWithConn(c), nil 222 | 223 | } 224 | 225 | func NewWithConn(conn net.Conn) *Conn { 226 | 227 | c := Conn{ 228 | conn: conn, 229 | done: make(chan bool), 230 | doneRead: make(chan bool), 231 | doneWrite: make(chan bool), 232 | 233 | pending: make(map[uint32]chan []byte), 234 | write: make(chan []byte), 235 | } 236 | 237 | go c.reader() 238 | go c.writer() 239 | 240 | return &c 241 | } 242 | 243 | func (c *Conn) Close() { 244 | select { 245 | case <-c.done: 246 | // done channel already closed? Nothing to do 247 | return 248 | default: 249 | // signal cleanup 250 | close(c.done) 251 | c.conn.Close() 252 | } 253 | } 254 | 255 | func (c *Conn) writer() { 256 | 257 | FOR: 258 | for { 259 | select { 260 | case <-c.done: 261 | break FOR 262 | case b := <-c.write: 263 | _, err := c.conn.Write(b) 264 | if err != nil { 265 | break FOR 266 | } 267 | } 268 | } 269 | 270 | close(c.doneWrite) 271 | } 272 | 273 | func (c *Conn) reader() { 274 | 275 | FOR: 276 | for { 277 | select { 278 | case <-c.done: 279 | break FOR 280 | default: 281 | } 282 | 283 | var header [8]byte 284 | 285 | // FIXME(dgryski): need another timeout on these reads 286 | _, err := io.ReadFull(c.conn, header[:]) 287 | if err != nil { 288 | // partial read -- unknown connection state 289 | break 290 | } 291 | 292 | rlen := binary.BigEndian.Uint16(header[2:]) 293 | id := binary.BigEndian.Uint32(header[4:]) 294 | 295 | response := make([]byte, rlen+8) 296 | copy(response, header[:]) 297 | 298 | _, err = io.ReadFull(c.conn, response[8:]) 299 | if err != nil { 300 | // partial read -- unknown connection state 301 | break 302 | } 303 | 304 | c.mu.Lock() 305 | ch := c.pending[id] 306 | if ch != nil { 307 | delete(c.pending, id) 308 | } 309 | c.mu.Unlock() 310 | 311 | if ch != nil { 312 | ch <- response 313 | } else { 314 | // message for unknown id 315 | } 316 | } 317 | 318 | // tell everybody waiting for a request that the reader isn't processing any more 319 | close(c.doneRead) 320 | } 321 | 322 | var ErrBadResponse = errors.New("bad response packet") 323 | 324 | func (c *Conn) Ping(params *Params) ([]byte, error) { 325 | 326 | response, err := c.doRequest(OpPing, params) 327 | 328 | if err != nil { 329 | return nil, err 330 | } 331 | 332 | // probably shouldn't depend on the order of response packets.. 333 | if len(response) != 2 || 334 | response[0].Tag != TagOPCODE || 335 | len(response[0].Data) != 1 || 336 | response[0].Data[0] != OpPong || 337 | response[1].Tag != TagPayload { 338 | return nil, ErrBadResponse 339 | } 340 | 341 | return response[1].Data, nil 342 | } 343 | 344 | func (c *Conn) Decrypt(params *Params) ([]byte, error) { 345 | 346 | response, err := c.doRequest(OpRSADecrypt, params) 347 | if err != nil { 348 | return nil, err 349 | } 350 | 351 | // probably shouldn't depend on the order of response packets.. 352 | if len(response) != 2 || 353 | response[0].Tag != TagOPCODE || 354 | len(response[0].Data) != 1 || 355 | response[1].Tag != TagPayload { 356 | return nil, ErrBadResponse 357 | } 358 | 359 | if OpCode(response[0].Data[0]) == OpError { 360 | return nil, ErrCode(response[1].Data[0]) 361 | } 362 | 363 | return response[1].Data, nil 364 | } 365 | 366 | func (c *Conn) Sign(op OpCode, params *Params) ([]byte, error) { 367 | 368 | // FIXME(dgryski): make sure opcode is a valid signature request type 369 | 370 | response, err := c.doRequest(op, params) 371 | if err != nil { 372 | return nil, err 373 | } 374 | 375 | // probably shouldn't depend on the order of response packets.. 376 | if len(response) != 2 || 377 | response[0].Tag != TagOPCODE || 378 | len(response[0].Data) != 1 || 379 | response[1].Tag != TagPayload { 380 | return nil, ErrBadResponse 381 | } 382 | 383 | if OpCode(response[0].Data[0]) == OpError { 384 | return nil, ErrCode(response[1].Data[0]) 385 | } 386 | 387 | return response[1].Data, nil 388 | } 389 | 390 | func (c *Conn) doRequest(op OpCode, params *Params) ([]Item, error) { 391 | 392 | select { 393 | case <-c.done: 394 | return nil, io.EOF 395 | default: 396 | 397 | } 398 | 399 | id := atomic.AddUint32(&c.id, 1) 400 | 401 | b := PackRequest(id, op, params) 402 | 403 | ch := make(chan []byte, 1) 404 | c.mu.Lock() 405 | c.pending[id] = ch 406 | c.mu.Unlock() 407 | 408 | select { 409 | case <-c.doneWrite: 410 | return nil, io.EOF 411 | case c.write <- b: 412 | } 413 | 414 | select { 415 | case <-c.doneRead: 416 | return nil, io.EOF 417 | case b = <-ch: 418 | } 419 | 420 | var r Packet 421 | Unmarshal(b, &r) 422 | 423 | // docs say response is two items, plus padding. 424 | // strip out the padding, complain if it's not there. 425 | if len(r.Items) != 3 || r.Items[len(r.Items)-1].Tag != TagPadding { 426 | return nil, ErrBadResponse 427 | } 428 | 429 | return r.Items[:len(r.Items)-1], nil 430 | } 431 | 432 | func DigestPublicModulus(pub *rsa.PublicKey) [32]byte { 433 | dst := make([]byte, hex.EncodedLen(len(pub.N.Bytes()))) 434 | hex.Encode(dst, pub.N.Bytes()) 435 | // need the digest in uppercase 436 | for i, c := range dst { 437 | if c >= 'a' /* && c <= 'f' */ { 438 | dst[i] = c - 'a' + 'A' 439 | } 440 | } 441 | sum := sha256.Sum256(dst) 442 | return sum 443 | } 444 | 445 | func Marshal(p Packet) ([]byte, error) { 446 | 447 | var b []byte 448 | 449 | b = append(b, p.VersionMaj, p.VersionMin) 450 | b = append16(b, 0) 451 | b = append32(b, p.ID) 452 | 453 | for _, item := range p.Items { 454 | b = appendItem(b, item) 455 | } 456 | 457 | // pad response to at least padTo length 458 | var padding []byte 459 | if len(b) < padTo { 460 | padding = make([]byte, padTo-len(b)) 461 | } 462 | b = appendItem(b, Item{Tag: TagPadding, Data: padding}) 463 | 464 | binary.BigEndian.PutUint16(b[2:], uint16(len(b)-headerSize)) 465 | 466 | return b, nil 467 | } 468 | 469 | func Unmarshal(b []byte, p *Packet) error { 470 | 471 | blen := len(b) 472 | 473 | if blen < headerSize { 474 | return ErrCode(ErrFormat) 475 | } 476 | 477 | p.VersionMaj, p.VersionMin = b[0], b[1] 478 | b = b[2:] 479 | 480 | length := binary.BigEndian.Uint16(b[:]) 481 | b = b[2:] 482 | 483 | if int(length)+headerSize != blen { 484 | return ErrCode(ErrFormat) 485 | } 486 | 487 | p.ID = binary.BigEndian.Uint32(b[:]) 488 | b = b[4:] 489 | 490 | for len(b) > 0 { 491 | var item Item 492 | var err error 493 | b, err = readItem(b, &item) 494 | if err != nil { 495 | return err 496 | } 497 | p.Items = append(p.Items, item) 498 | } 499 | 500 | return nil 501 | } 502 | 503 | func PackRequest(id uint32, op OpCode, params *Params) []byte { 504 | 505 | items := []Item{ 506 | {Tag: TagOPCODE, Data: []byte{byte(op)}}, 507 | } 508 | 509 | if params != nil { 510 | if params.Digest != nil { 511 | items = append(items, Item{Tag: TagDigest, Data: params.Digest}) 512 | } 513 | if params.Payload != nil { 514 | items = append(items, Item{Tag: TagPayload, Data: params.Payload}) 515 | } 516 | if params.SNI != "" { 517 | items = append(items, Item{Tag: TagSNI, Data: []byte(params.SNI)}) 518 | } 519 | if params.ClientIP != nil { 520 | var ip []byte 521 | ip = params.ClientIP.To4() 522 | if ip == nil { 523 | ip = params.ClientIP.To16() 524 | } 525 | items = append(items, Item{Tag: TagClientIP, Data: ip}) 526 | } 527 | } 528 | 529 | p := Packet{ 530 | VersionMaj: VersionMaj, 531 | ID: id, 532 | Items: items, 533 | } 534 | 535 | b, _ := Marshal(p) 536 | 537 | return b 538 | } 539 | 540 | func UnpackRequest(b []byte) (*Packet, OpCode, *Params, error) { 541 | 542 | var p Packet 543 | 544 | err := Unmarshal(b, &p) 545 | 546 | if err != nil { 547 | return &p, 0, nil, err 548 | } 549 | 550 | var params Params 551 | var op OpCode 552 | 553 | for _, item := range p.Items { 554 | switch item.Tag { 555 | case TagOPCODE: 556 | if len(item.Data) != 1 { 557 | return nil, 0, nil, ErrCode(ErrBadOpcode) 558 | } 559 | op = OpCode(item.Data[0]) 560 | case TagDigest: 561 | params.Digest = item.Data 562 | case TagPayload: 563 | params.Payload = item.Data 564 | case TagSNI: 565 | params.SNI = string(item.Data) 566 | case TagClientIP: 567 | params.ClientIP = item.Data 568 | 569 | } 570 | } 571 | 572 | return &p, op, ¶ms, nil 573 | } 574 | 575 | func append16(b []byte, v uint16) []byte { 576 | return append(b, byte(v>>8), byte(v)) 577 | } 578 | 579 | func append32(b []byte, v uint32) []byte { 580 | return append(b, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) 581 | } 582 | 583 | func appendItem(b []byte, item Item) []byte { 584 | 585 | b = append(b, byte(item.Tag)) 586 | b = append16(b, uint16(len(item.Data))) 587 | b = append(b, item.Data...) 588 | 589 | return b 590 | } 591 | 592 | func readItem(b []byte, item *Item) ([]byte, error) { 593 | 594 | if len(b) < 3 { 595 | return nil, ErrCode(ErrFormat) 596 | } 597 | 598 | item.Tag = Tag(b[0]) 599 | b = b[1:] 600 | 601 | l := binary.BigEndian.Uint16(b[:]) 602 | b = b[2:] 603 | 604 | if int(l) > len(b) { 605 | return nil, ErrCode(ErrFormat) 606 | } 607 | 608 | item.Data, b = b[:l], b[l:] 609 | 610 | return b, nil 611 | } 612 | --------------------------------------------------------------------------------