github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/lndc/noise.go (about) 1 package lndc 2 3 import ( 4 "crypto/cipher" 5 "crypto/sha256" 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "io" 10 "math" 11 "time" 12 13 "golang.org/x/crypto/chacha20poly1305" 14 "golang.org/x/crypto/hkdf" 15 16 "github.com/mit-dci/lit/crypto/koblitz" 17 ) 18 19 const ( 20 // protocolName is the precise instantiation of the Noise protocol 21 // This value will be used as part of the prologue. If the initiator 22 // and responder aren't using the exact same string for this value, 23 // along with prologue of the Bitcoin network, then the initial 24 // handshake will fail. 25 protocolName = "Noise_XX_secp256k1_ChaChaPoly_SHA256" 26 27 // macSize is the length in bytes of the tags generated by poly1305. 28 macSize = 16 29 30 // lengthHeaderSize is the number of bytes used to prefix encode the 31 // length of a message payload. 32 lengthHeaderSize = 2 33 34 // keyRotationInterval is the number of messages sent on a single 35 // cipher stream before the keys are rotated forwards. 36 keyRotationInterval = 1000 37 38 // handshakeReadTimeout is a read timeout that will be enforced when 39 // waiting for data payloads during the various acts of lndc. If 40 // the remote party fails to deliver the proper payload within this 41 // time frame, then we'll fail the connection. 42 handshakeReadTimeout = time.Second * 5 // not 10 because of litrpc 43 ) 44 45 // ecdh performs an ECDH operation between pub and priv. The returned value is 46 // the sha256 of the compressed shared point. 47 func ecdh(pub *koblitz.PublicKey, priv *koblitz.PrivateKey) []byte { 48 s := &koblitz.PublicKey{} 49 x, y := koblitz.S256().ScalarMult(pub.X, pub.Y, priv.D.Bytes()) 50 s.X = x 51 s.Y = y 52 53 h := sha256.Sum256(s.SerializeCompressed()) 54 return h[:] 55 } 56 57 // cipherState encapsulates the state for the AEAD which will be used to 58 // encrypt+authenticate any payloads sent during the handshake, and messages 59 // sent once the handshake has completed. 60 type cipherState struct { 61 // nonce is the nonce passed into the chacha20-poly1305 instance for 62 // encryption+decryption. The nonce is incremented after each successful 63 // encryption/decryption. 64 nonce uint64 65 66 // secretKey is the shared symmetric key which will be used to 67 // instantiate the cipher. 68 secretKey [32]byte 69 70 // salt is an additional secret which is used during key rotation to 71 // generate new keys. 72 salt [32]byte 73 74 // cipher is an instance of the ChaCha20-Poly1305 AEAD construction 75 // created using the secretKey above. 76 cipher cipher.AEAD 77 } 78 79 // Encrypt returns a ciphertext which is the encryption of the plainText 80 // observing the passed associatedData within the AEAD construction. 81 func (c *cipherState) Encrypt(associatedData, cipherText, plainText []byte) []byte { 82 defer func() { 83 c.nonce++ 84 85 if c.nonce == keyRotationInterval { 86 c.rotateKey() 87 } 88 }() 89 90 var nonce [12]byte 91 binary.LittleEndian.PutUint64(nonce[4:], c.nonce) 92 93 return c.cipher.Seal(cipherText, nonce[:], plainText, associatedData) 94 } 95 96 // Decrypt attempts to decrypt the passed ciphertext observing the specified 97 // associatedData within the AEAD construction. In the case that the final MAC 98 // check fails, then a non-nil error will be returned. 99 func (c *cipherState) Decrypt(associatedData, plainText, cipherText []byte) ([]byte, error) { 100 defer func() { 101 c.nonce++ 102 103 if c.nonce == keyRotationInterval { 104 c.rotateKey() 105 } 106 }() 107 108 var nonce [12]byte 109 binary.LittleEndian.PutUint64(nonce[4:], c.nonce) 110 111 return c.cipher.Open(plainText, nonce[:], cipherText, associatedData) 112 } 113 114 // InitializeKey initializes the secret key and AEAD cipher scheme based off of 115 // the passed key. 116 func (c *cipherState) InitializeKey(key [32]byte) { 117 c.secretKey = key 118 c.nonce = 0 119 120 // Safe to ignore the error here as our key is properly sized 121 // (32-bytes). 122 c.cipher, _ = chacha20poly1305.New(c.secretKey[:]) 123 } 124 125 // InitializeKeyWithSalt is identical to InitializeKey however it also sets the 126 // cipherState's salt field which is used for key rotation. 127 func (c *cipherState) InitializeKeyWithSalt(salt, key [32]byte) { 128 c.salt = salt 129 c.InitializeKey(key) 130 } 131 132 // rotateKey rotates the current encryption/decryption key for this cipherState 133 // instance. Key rotation is performed by ratcheting the current key forward 134 // using an HKDF invocation with the cipherState's salt as the salt, and the 135 // current key as the input. 136 func (c *cipherState) rotateKey() { 137 var ( 138 info []byte 139 nextKey [32]byte 140 ) 141 142 oldKey := c.secretKey 143 h := hkdf.New(sha256.New, oldKey[:], c.salt[:], info) 144 145 // hkdf(ck, k, zero) 146 // | 147 // | \ 148 // | \ 149 // ck k' 150 h.Read(c.salt[:]) 151 h.Read(nextKey[:]) 152 153 c.InitializeKey(nextKey) 154 } 155 156 // symmetricState encapsulates a cipherState object and houses the ephemeral 157 // handshake digest state. This struct is used during the handshake to derive 158 // new shared secrets based off of the result of ECDH operations. Ultimately, 159 // the final key yielded by this struct is the result of an incremental 160 // Triple-DH operation. 161 type symmetricState struct { 162 cipherState 163 164 // chainingKey is used as the salt to the HKDF function to derive a new 165 // chaining key as well as a new tempKey which is used for 166 // encryption/decryption. 167 chainingKey [32]byte 168 169 // tempKey is the latter 32 bytes resulted from the latest HKDF 170 // iteration. This key is used to encrypt/decrypt any handshake 171 // messages or payloads sent until the next DH operation is executed. 172 tempKey [32]byte 173 174 // handshakeDigest is the cumulative hash digest of all handshake 175 // messages sent from start to finish. This value is never transmitted 176 // to the other side, but will be used as the AD when 177 // encrypting/decrypting messages using our AEAD construction. 178 handshakeDigest [32]byte 179 } 180 181 // mixKey is implements a basic HKDF-based key ratchet. This method is called 182 // with the result of each DH output generated during the handshake process. 183 // The first 32 bytes extract from the HKDF reader is the next chaining key, 184 // then latter 32 bytes become the temp secret key using within any future AEAD 185 // operations until another DH operation is performed. 186 func (s *symmetricState) mixKey(input []byte) { 187 var info []byte 188 189 secret := input 190 salt := s.chainingKey 191 h := hkdf.New(sha256.New, secret, salt[:], info) 192 193 // hkdf(ck, input, zero) 194 // | 195 // | \ 196 // | \ 197 // ck k 198 h.Read(s.chainingKey[:]) 199 h.Read(s.tempKey[:]) 200 201 // cipher.k = temp_key 202 s.InitializeKey(s.tempKey) 203 } 204 205 // mixHash hashes the passed input data into the cumulative handshake digest. 206 // The running result of this value (h) is used as the associated data in all 207 // decryption/encryption operations. 208 func (s *symmetricState) mixHash(data []byte) { 209 h := sha256.New() 210 h.Write(s.handshakeDigest[:]) 211 h.Write(data) 212 213 copy(s.handshakeDigest[:], h.Sum(nil)) 214 } 215 216 // EncryptAndHash returns the authenticated encryption of the passed plaintext. 217 // When encrypting the handshake digest (h) is used as the associated data to 218 // the AEAD cipher. 219 func (s *symmetricState) EncryptAndHash(plaintext []byte) []byte { 220 ciphertext := s.Encrypt(s.handshakeDigest[:], nil, plaintext) 221 s.mixHash(ciphertext) 222 223 return ciphertext 224 } 225 226 // DecryptAndHash returns the authenticated decryption of the passed 227 // ciphertext. When encrypting the handshake digest (h) is used as the 228 // associated data to the AEAD cipher. 229 func (s *symmetricState) DecryptAndHash(ciphertext []byte) ([]byte, error) { 230 plaintext, err := s.Decrypt(s.handshakeDigest[:], nil, ciphertext) 231 if err != nil { 232 return nil, err 233 } 234 s.mixHash(ciphertext) 235 236 return plaintext, nil 237 } 238 239 // InitializeSymmetric initializes the symmetric state by setting the handshake 240 // digest (h) and the chaining key (ck) to protocol name. 241 func (s *symmetricState) InitializeSymmetric(protocolName []byte) { 242 var empty [32]byte 243 s.handshakeDigest = sha256.Sum256(protocolName) 244 s.chainingKey = s.handshakeDigest 245 s.InitializeKey(empty) // init with empty key 246 } 247 248 // handshakeState encapsulates the symmetricState and keeps track of all the 249 // public keys (static and ephemeral) for both sides during the handshake 250 // transcript. If the handshake completes successfully, then two instances of a 251 // cipherState are emitted: one to encrypt messages from initiator to 252 // responder, and the other for the opposite direction. 253 type handshakeState struct { 254 symmetricState 255 256 initiator bool 257 258 localStatic *koblitz.PrivateKey 259 localEphemeral *koblitz.PrivateKey 260 261 remoteStatic *koblitz.PublicKey 262 remoteEphemeral *koblitz.PublicKey 263 } 264 265 // newHandshakeState returns a new instance of the handshake state initialized 266 // with the prologue and protocol name. If this is the responder's handshake 267 // state, then the remotePub can be nil. 268 func newHandshakeState(initiator bool, prologue []byte, 269 localStatic *koblitz.PrivateKey) handshakeState { 270 271 h := handshakeState{ 272 initiator: initiator, 273 localStatic: localStatic, 274 } 275 276 // Set the current chaining key and handshake digest to the hash of the 277 // protocol name, and additionally mix in the prologue. If either sides 278 // disagree about the prologue or protocol name, then the handshake 279 // will fail. 280 h.InitializeSymmetric([]byte(protocolName)) 281 h.mixHash(prologue) 282 return h 283 } 284 285 // EphemeralGenerator is a functional option that allows callers to substitute 286 // a custom function for use when generating ephemeral keys for ActOne or 287 // ActTwo. The function closure return by this function can be passed into 288 // NewNoiseMachine as a function option parameter. 289 func EphemeralGenerator(gen func() (*koblitz.PrivateKey, error)) func(*Machine) { 290 return func(m *Machine) { 291 m.ephemeralGen = gen 292 } 293 } 294 295 // Machine is a state-machine which implements lndc: an 296 // Authenticated-key Exchange in Three Acts. lndc is derived from the Noise 297 // framework, specifically implementing the Noise_XX handshake. Once the 298 // initial 3-act handshake has completed all messages are encrypted with a 299 // chacha20 AEAD cipher. On the wire, all messages are prefixed with an 300 // authenticated+encrypted length field. Additionally, the encrypted+auth'd 301 // length prefix is used as the AD when encrypting+decryption messages. This 302 // construction provides confidentiality of packet length, avoids introducing 303 // a padding-oracle, and binds the encrypted packet length to the packet 304 // itself. Noise protocol reference: http://noiseprotocol.org/noise.html 305 // 306 // The acts proceeds the following order (initiator on the left): 307 // GenActOne() -> 308 // RecvActOne() 309 // <- GenActTwo() 310 // RecvActTwo() 311 // GenActThree() -> 312 // RecvActThree() 313 // 314 // The protocol has the following steps involved: 315 // XX(s, rs): 316 // INITIATOR -> e RESPONDER 317 // INITIATOR <- e, ee, s, es RESPONDER 318 // INITIATOR -> s, se RESPONDER 319 // s refers to the static key (or public key) belonging to an entity 320 // e refers to the ephemeral key 321 // e, ee, es refer to a DH exchange between the initiator's key pair and the 322 // responder's key pair. The letters e and s hold the same meaning as before. 323 324 type Machine struct { 325 sendCipher cipherState 326 recvCipher cipherState 327 328 ephemeralGen func() (*koblitz.PrivateKey, error) 329 330 handshakeState 331 332 // nextCipherHeader is a static buffer that we'll use to read in the 333 // next ciphertext header from the wire. The header is a 2 byte length 334 // (of the next ciphertext), followed by a 16 byte MAC. 335 nextCipherHeader [lengthHeaderSize + macSize]byte 336 337 // nextCipherText is a static buffer that we'll use to read in the 338 // bytes of the next cipher text message. As all messages in the 339 // protocol MUST be below 65KB plus our macSize, this will be 340 // sufficient to buffer all messages from the socket when we need to 341 // read the next one. Having a fixed buffer that's re-used also means 342 // that we save on allocations as we don't need to create a new one 343 // each time. 344 nextCipherText [math.MaxUint16 + macSize]byte 345 } 346 347 // NewNoiseMachine creates a new instance of the lndc state-machine. If 348 // the responder (listener) is creating the object, then the remotePub should 349 // be nil. The handshake state within lndc is initialized using the ascii 350 // string "lightning" as the prologue. The last parameter is a set of variadic 351 // arguments for adding additional options to the lndc Machine 352 // initialization. 353 func NewNoiseMachine(initiator bool, localStatic *koblitz.PrivateKey, 354 options ...func(*Machine)) *Machine { 355 356 handshake := newHandshakeState(initiator, []byte("lit"), localStatic) 357 // TODO: if we're sending messages of type XK, set it back to 358 // "lightning" which is what BOLT uses 359 360 m := &Machine{handshakeState: handshake} 361 362 // With the initial base machine created, we'll assign our default 363 // version of the ephemeral key generator. 364 m.ephemeralGen = func() (*koblitz.PrivateKey, error) { 365 return koblitz.NewPrivateKey(koblitz.S256()) 366 } 367 // With the default options established, we'll now process all the 368 // options passed in as parameters. 369 for _, option := range options { 370 option(m) 371 } 372 373 return m 374 } 375 376 const ( 377 // HandshakeVersion is the expected version of the lndc handshake. 378 // Any messages that carry a different version will cause the handshake 379 // to abort immediately. 380 HandshakeVersion = byte(1) // TODO: add support for noise_XK (brontide) as well 381 382 // ActOneSize is the size of the packet sent from initiator to 383 // responder in ActOne. The packet consists of a handshake version, an 384 // ephemeral key in compressed format, and a 16-byte poly1305 tag. 385 // -> e 386 // 1 + 33 + 16 387 ActOneSize = 50 388 389 // ActTwoSize is the size the packet sent from responder to initiator 390 // in ActTwo. The packet consists of a handshake version, an ephemeral 391 // key in compressed format, a public key in compressed format 392 // and a 16-byte poly1305 tag. 393 // <- e, ee, s, es 394 // 1 + 33 + 33 + 16 395 ActTwoSize = 83 396 397 // ActThreeSize is the size of the packet sent from initiator to 398 // responder in ActThree. The packet consists of a handshake version, 399 // the initiators static key encrypted with strong forward secrecy and 400 // a 16-byte poly1035 tag. 401 // -> s, se 402 // 1 + 33 + 16 + 16 403 ActThreeSize = 66 404 ) 405 406 // GenActOne generates the initial packet (act one) to be sent from initiator 407 // to responder. During act one the initiator generates an ephemeral key and 408 // hashes it into the handshake digest. Future payloads are encrypted with a key 409 // derived from this result. 410 // -> e 411 412 func (b *Machine) GenActOne() ([ActOneSize]byte, error) { 413 var ( 414 err error 415 actOne [ActOneSize]byte 416 ) 417 418 // Generate e 419 b.localEphemeral, err = b.ephemeralGen() 420 if err != nil { 421 return actOne, err 422 } 423 424 // Compress e 425 e := b.localEphemeral.PubKey().SerializeCompressed() 426 // Hash it into the handshake digest 427 b.mixHash(e) 428 429 authPayload := b.EncryptAndHash([]byte{}) 430 actOne[0] = HandshakeVersion 431 copy(actOne[1:34], e) 432 copy(actOne[34:], authPayload) 433 return actOne, nil 434 } 435 436 // RecvActOne processes the act one packet sent by the initiator. The responder 437 // executes the mirrored actions to that of the initiator extending the 438 // handshake digest and deriving a new shared secret based on an ECDH with the 439 // initiator's ephemeral key and responder's static key. 440 func (b *Machine) RecvActOne(actOne [ActOneSize]byte) error { 441 var ( 442 err error 443 e [33]byte 444 p [16]byte 445 ) 446 447 // If the handshake version is unknown, then the handshake fails 448 // immediately. 449 if actOne[0] != HandshakeVersion { 450 return fmt.Errorf("Act One: invalid handshake version: %v, "+ 451 "only %v is valid, msg=%x", actOne[0], HandshakeVersion, 452 actOne[:]) 453 } 454 455 copy(e[:], actOne[1:34]) 456 copy(p[:], actOne[34:]) 457 458 // e 459 b.remoteEphemeral, err = koblitz.ParsePubKey(e[:], koblitz.S256()) 460 if err != nil { 461 return err 462 } 463 b.mixHash(b.remoteEphemeral.SerializeCompressed()) 464 465 _, err = b.DecryptAndHash(p[:]) 466 return err // nil means Act one completed successfully 467 } 468 469 // GenActTwo generates the second packet (act two) to be sent from the 470 // responder to the initiator 471 // <- e, ee, s, es 472 func (b *Machine) GenActTwo() ([ActTwoSize]byte, error) { 473 var ( 474 err error 475 actTwo [ActTwoSize]byte 476 ) 477 478 // e 479 b.localEphemeral, err = b.ephemeralGen() 480 if err != nil { 481 return actTwo, err 482 } 483 484 e := b.localEphemeral.PubKey().SerializeCompressed() 485 b.mixHash(b.localEphemeral.PubKey().SerializeCompressed()) 486 487 // ee 488 ee := ecdh(b.remoteEphemeral, b.localEphemeral) 489 b.mixKey(ee) 490 491 // s 492 s := b.localStatic.PubKey().SerializeCompressed() 493 b.mixHash(s) 494 495 // es 496 es := ecdh(b.remoteEphemeral, b.localStatic) 497 b.mixKey(es) 498 499 authPayload := b.EncryptAndHash([]byte{}) 500 actTwo[0] = HandshakeVersion 501 copy(actTwo[1:34], e) 502 copy(actTwo[34:67], s) 503 copy(actTwo[67:], authPayload) 504 // add additional stuff based on what we need 505 return actTwo, nil 506 } 507 508 // RecvActTwo processes the second packet (act two) sent from the responder to 509 // the initiator. A successful processing of this packet authenticates the 510 // initiator to the responder. 511 func (b *Machine) RecvActTwo(actTwo [ActTwoSize]byte) ([33]byte, error) { 512 var ( 513 err error 514 e [33]byte 515 s [33]byte 516 p [16]byte 517 ) 518 var empty [33]byte 519 // If the handshake version is unknown, then the handshake fails 520 // immediately. 521 if actTwo[0] != HandshakeVersion { 522 return empty, fmt.Errorf("Act Two: invalid handshake version: %v, "+ 523 "only %v is valid, msg=%x", actTwo[0], HandshakeVersion, 524 actTwo[:]) 525 } 526 527 copy(e[:], actTwo[1:34]) 528 copy(s[:], actTwo[34:67]) 529 copy(p[:], actTwo[67:]) 530 531 // e 532 b.remoteEphemeral, err = koblitz.ParsePubKey(e[:], koblitz.S256()) 533 if err != nil { 534 return empty, err 535 } 536 b.mixHash(b.remoteEphemeral.SerializeCompressed()) 537 538 // ee 539 ee := ecdh(b.remoteEphemeral, b.localEphemeral) 540 b.mixKey(ee) 541 542 // s 543 b.remoteStatic, err = koblitz.ParsePubKey(s[:], koblitz.S256()) 544 if err != nil { 545 return empty, err 546 } 547 b.mixHash(b.remoteStatic.SerializeCompressed()) 548 549 // es 550 es := ecdh(b.remoteStatic, b.localEphemeral) 551 b.mixKey(es) 552 553 _, err = b.DecryptAndHash(p[:]) 554 return s, err 555 } 556 557 // GenActThree creates the final (act three) packet of the handshake. Act three 558 // is to be sent from the initiator to the responder. The purpose of act three 559 // is to transmit the initiator's public key under strong forward secrecy to 560 // the responder. This act also includes the final ECDH operation which yields 561 // the final session. 562 // -> s, se 563 func (b *Machine) GenActThree() ([ActThreeSize]byte, error) { 564 var actThree [ActThreeSize]byte 565 566 // s 567 s := b.localStatic.PubKey().SerializeCompressed() 568 encryptedS := b.EncryptAndHash(s) 569 570 //se 571 se := ecdh(b.remoteEphemeral, b.localStatic) 572 b.mixKey(se) 573 574 authPayload := b.EncryptAndHash([]byte{}) 575 576 actThree[0] = HandshakeVersion 577 copy(actThree[1:50], encryptedS) 578 copy(actThree[50:], authPayload) 579 580 // With the final ECDH operation complete, derive the session sending 581 // and receiving keys. 582 b.split() 583 return actThree, nil 584 } 585 586 // RecvActThree processes the final act (act three) sent from the initiator to 587 // the responder. After processing this act, the responder learns of the 588 // initiator's static public key. Decryption of the static key serves to 589 // authenticate the initiator to the responder. 590 func (b *Machine) RecvActThree(actThree [ActThreeSize]byte) error { 591 var ( 592 err error 593 s [49]byte 594 p [16]byte 595 ) 596 597 // If the handshake version is unknown, then the handshake fails 598 // immediately. 599 if actThree[0] != HandshakeVersion { 600 return fmt.Errorf("Act Three: invalid handshake version: %v, "+ 601 "only %v is valid, msg=%x", actThree[0], HandshakeVersion, 602 actThree[:]) 603 } 604 605 copy(s[:], actThree[1:50]) 606 copy(p[:], actThree[50:]) 607 608 // s 609 remotePub, err := b.DecryptAndHash(s[:]) 610 if err != nil { 611 return err 612 } 613 614 b.remoteStatic, err = koblitz.ParsePubKey(remotePub, koblitz.S256()) 615 if err != nil { 616 return err 617 } 618 619 // se 620 se := ecdh(b.remoteStatic, b.localEphemeral) 621 b.mixKey(se) 622 623 if _, err := b.DecryptAndHash(p[:]); err != nil { 624 return err 625 } 626 627 // With the final ECDH operation complete, derive the session sending 628 // and receiving keys. 629 b.split() 630 return nil 631 } 632 633 // split is the final wrap-up act to be executed at the end of a successful 634 // three act handshake. This function creates two internal cipherState 635 // instances: one which is used to encrypt messages from the initiator to the 636 // responder, and another which is used to encrypt message for the opposite 637 // direction. 638 func (b *Machine) split() { 639 var ( 640 empty []byte 641 sendKey [32]byte 642 recvKey [32]byte 643 ) 644 645 h := hkdf.New(sha256.New, empty, b.chainingKey[:], empty) 646 647 // If we're the initiator the first 32 bytes are used to encrypt our 648 // messages and the second 32-bytes to decrypt their messages. For the 649 // responder the opposite is true. 650 if b.initiator { 651 h.Read(sendKey[:]) 652 b.sendCipher = cipherState{} 653 b.sendCipher.InitializeKeyWithSalt(b.chainingKey, sendKey) 654 655 h.Read(recvKey[:]) 656 b.recvCipher = cipherState{} 657 b.recvCipher.InitializeKeyWithSalt(b.chainingKey, recvKey) 658 } else { 659 h.Read(recvKey[:]) 660 b.recvCipher = cipherState{} 661 b.recvCipher.InitializeKeyWithSalt(b.chainingKey, recvKey) 662 663 h.Read(sendKey[:]) 664 b.sendCipher = cipherState{} 665 b.sendCipher.InitializeKeyWithSalt(b.chainingKey, sendKey) 666 } 667 } 668 669 // WriteMessage writes the next message p to the passed io.Writer. The 670 // ciphertext of the message is prepended with an encrypt+auth'd length which 671 // must be used as the AD to the AEAD construction when being decrypted by the 672 // other side. 673 func (b *Machine) WriteMessage(w io.Writer, p []byte) error { 674 // The total length of each message payload including the MAC size 675 // payload exceed the largest number encodable within a 16-bit unsigned 676 // integer. 677 if len(p) > math.MaxUint16 { 678 return errors.New("the generated payload exceeds " + 679 "the max allowed message length of (2^16)-1") 680 } 681 682 // The full length of the packet is only the packet length, and does 683 // NOT include the MAC. 684 fullLength := uint16(len(p)) 685 686 var pktLen [2]byte 687 binary.BigEndian.PutUint16(pktLen[:], fullLength) 688 689 // First, write out the encrypted+MAC'd length prefix for the packet. 690 cipherLen := b.sendCipher.Encrypt(nil, nil, pktLen[:]) 691 if _, err := w.Write(cipherLen); err != nil { 692 return err 693 } 694 695 // Finally, write out the encrypted packet itself. We only write out a 696 // single packet, as any fragmentation should have taken place at a 697 // higher level. 698 cipherText := b.sendCipher.Encrypt(nil, nil, p) 699 _, err := w.Write(cipherText) 700 return err 701 } 702 703 // ReadMessage attempts to read the next message from the passed io.Reader. In 704 // the case of an authentication error, a non-nil error is returned. 705 func (b *Machine) ReadMessage(r io.Reader) ([]byte, error) { 706 if _, err := io.ReadFull(r, b.nextCipherHeader[:]); err != nil { 707 return nil, err 708 } 709 710 // Attempt to decrypt+auth the packet length present in the stream. 711 pktLenBytes, err := b.recvCipher.Decrypt( 712 nil, nil, b.nextCipherHeader[:], 713 ) 714 if err != nil { 715 return nil, err 716 } 717 718 // Next, using the length read from the packet header, read the 719 // encrypted packet itself. 720 pktLen := uint32(binary.BigEndian.Uint16(pktLenBytes)) + macSize 721 if _, err := io.ReadFull(r, b.nextCipherText[:pktLen]); err != nil { 722 return nil, err 723 } 724 725 return b.recvCipher.Decrypt(nil, nil, b.nextCipherText[:pktLen]) 726 }