1// Copyright 2012 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package otr implements the Off The Record protocol as specified in 6// http://www.cypherpunks.ca/otr/Protocol-v2-3.1.0.html 7// 8// The version of OTR implemented by this package has been deprecated 9// (https://bugs.otr.im/lib/libotr/issues/140). An implementation of OTRv3 is 10// available at https://github.com/coyim/otr3. 11package otr // import "golang.org/x/crypto/otr" 12 13import ( 14 "bytes" 15 "crypto/aes" 16 "crypto/cipher" 17 "crypto/dsa" 18 "crypto/hmac" 19 "crypto/rand" 20 "crypto/sha1" 21 "crypto/sha256" 22 "crypto/subtle" 23 "encoding/base64" 24 "encoding/hex" 25 "errors" 26 "hash" 27 "io" 28 "math/big" 29 "strconv" 30) 31 32// SecurityChange describes a change in the security state of a Conversation. 33type SecurityChange int 34 35const ( 36 NoChange SecurityChange = iota 37 // NewKeys indicates that a key exchange has completed. This occurs 38 // when a conversation first becomes encrypted, and when the keys are 39 // renegotiated within an encrypted conversation. 40 NewKeys 41 // SMPSecretNeeded indicates that the peer has started an 42 // authentication and that we need to supply a secret. Call SMPQuestion 43 // to get the optional, human readable challenge and then Authenticate 44 // to supply the matching secret. 45 SMPSecretNeeded 46 // SMPComplete indicates that an authentication completed. The identity 47 // of the peer has now been confirmed. 48 SMPComplete 49 // SMPFailed indicates that an authentication failed. 50 SMPFailed 51 // ConversationEnded indicates that the peer ended the secure 52 // conversation. 53 ConversationEnded 54) 55 56// QueryMessage can be sent to a peer to start an OTR conversation. 57var QueryMessage = "?OTRv2?" 58 59// ErrorPrefix can be used to make an OTR error by appending an error message 60// to it. 61var ErrorPrefix = "?OTR Error:" 62 63var ( 64 fragmentPartSeparator = []byte(",") 65 fragmentPrefix = []byte("?OTR,") 66 msgPrefix = []byte("?OTR:") 67 queryMarker = []byte("?OTR") 68) 69 70// isQuery attempts to parse an OTR query from msg and returns the greatest 71// common version, or 0 if msg is not an OTR query. 72func isQuery(msg []byte) (greatestCommonVersion int) { 73 pos := bytes.Index(msg, queryMarker) 74 if pos == -1 { 75 return 0 76 } 77 for i, c := range msg[pos+len(queryMarker):] { 78 if i == 0 { 79 if c == '?' { 80 // Indicates support for version 1, but we don't 81 // implement that. 82 continue 83 } 84 85 if c != 'v' { 86 // Invalid message 87 return 0 88 } 89 90 continue 91 } 92 93 if c == '?' { 94 // End of message 95 return 96 } 97 98 if c == ' ' || c == '\t' { 99 // Probably an invalid message 100 return 0 101 } 102 103 if c == '2' { 104 greatestCommonVersion = 2 105 } 106 } 107 108 return 0 109} 110 111const ( 112 statePlaintext = iota 113 stateEncrypted 114 stateFinished 115) 116 117const ( 118 authStateNone = iota 119 authStateAwaitingDHKey 120 authStateAwaitingRevealSig 121 authStateAwaitingSig 122) 123 124const ( 125 msgTypeDHCommit = 2 126 msgTypeData = 3 127 msgTypeDHKey = 10 128 msgTypeRevealSig = 17 129 msgTypeSig = 18 130) 131 132const ( 133 // If the requested fragment size is less than this, it will be ignored. 134 minFragmentSize = 18 135 // Messages are padded to a multiple of this number of bytes. 136 paddingGranularity = 256 137 // The number of bytes in a Diffie-Hellman private value (320-bits). 138 dhPrivateBytes = 40 139 // The number of bytes needed to represent an element of the DSA 140 // subgroup (160-bits). 141 dsaSubgroupBytes = 20 142 // The number of bytes of the MAC that are sent on the wire (160-bits). 143 macPrefixBytes = 20 144) 145 146// These are the global, common group parameters for OTR. 147var ( 148 p *big.Int // group prime 149 g *big.Int // group generator 150 q *big.Int // group order 151 pMinus2 *big.Int 152) 153 154func init() { 155 p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 16) 156 q, _ = new(big.Int).SetString("7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68948127044533E63A0105DF531D89CD9128A5043CC71A026EF7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9EE1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AFC1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36B3861AA7255E4C0278BA36046511B993FFFFFFFFFFFFFFFF", 16) 157 g = new(big.Int).SetInt64(2) 158 pMinus2 = new(big.Int).Sub(p, g) 159} 160 161// Conversation represents a relation with a peer. The zero value is a valid 162// Conversation, although PrivateKey must be set. 163// 164// When communicating with a peer, all inbound messages should be passed to 165// Conversation.Receive and all outbound messages to Conversation.Send. The 166// Conversation will take care of maintaining the encryption state and 167// negotiating encryption as needed. 168type Conversation struct { 169 // PrivateKey contains the private key to use to sign key exchanges. 170 PrivateKey *PrivateKey 171 172 // Rand can be set to override the entropy source. Otherwise, 173 // crypto/rand will be used. 174 Rand io.Reader 175 // If FragmentSize is set, all messages produced by Receive and Send 176 // will be fragmented into messages of, at most, this number of bytes. 177 FragmentSize int 178 179 // Once Receive has returned NewKeys once, the following fields are 180 // valid. 181 SSID [8]byte 182 TheirPublicKey PublicKey 183 184 state, authState int 185 186 r [16]byte 187 x, y *big.Int 188 gx, gy *big.Int 189 gxBytes []byte 190 digest [sha256.Size]byte 191 192 revealKeys, sigKeys akeKeys 193 194 myKeyId uint32 195 myCurrentDHPub *big.Int 196 myCurrentDHPriv *big.Int 197 myLastDHPub *big.Int 198 myLastDHPriv *big.Int 199 200 theirKeyId uint32 201 theirCurrentDHPub *big.Int 202 theirLastDHPub *big.Int 203 204 keySlots [4]keySlot 205 206 myCounter [8]byte 207 theirLastCtr [8]byte 208 oldMACs []byte 209 210 k, n int // fragment state 211 frag []byte 212 213 smp smpState 214} 215 216// A keySlot contains key material for a specific (their keyid, my keyid) pair. 217type keySlot struct { 218 // used is true if this slot is valid. If false, it's free for reuse. 219 used bool 220 theirKeyId uint32 221 myKeyId uint32 222 sendAESKey, recvAESKey []byte 223 sendMACKey, recvMACKey []byte 224 theirLastCtr [8]byte 225} 226 227// akeKeys are generated during key exchange. There's one set for the reveal 228// signature message and another for the signature message. In the protocol 229// spec the latter are indicated with a prime mark. 230type akeKeys struct { 231 c [16]byte 232 m1, m2 [32]byte 233} 234 235func (c *Conversation) rand() io.Reader { 236 if c.Rand != nil { 237 return c.Rand 238 } 239 return rand.Reader 240} 241 242func (c *Conversation) randMPI(buf []byte) *big.Int { 243 _, err := io.ReadFull(c.rand(), buf) 244 if err != nil { 245 panic("otr: short read from random source") 246 } 247 248 return new(big.Int).SetBytes(buf) 249} 250 251// tlv represents the type-length value from the protocol. 252type tlv struct { 253 typ, length uint16 254 data []byte 255} 256 257const ( 258 tlvTypePadding = 0 259 tlvTypeDisconnected = 1 260 tlvTypeSMP1 = 2 261 tlvTypeSMP2 = 3 262 tlvTypeSMP3 = 4 263 tlvTypeSMP4 = 5 264 tlvTypeSMPAbort = 6 265 tlvTypeSMP1WithQuestion = 7 266) 267 268// Receive handles a message from a peer. It returns a human readable message, 269// an indicator of whether that message was encrypted, a hint about the 270// encryption state and zero or more messages to send back to the peer. 271// These messages do not need to be passed to Send before transmission. 272func (c *Conversation) Receive(in []byte) (out []byte, encrypted bool, change SecurityChange, toSend [][]byte, err error) { 273 if bytes.HasPrefix(in, fragmentPrefix) { 274 in, err = c.processFragment(in) 275 if in == nil || err != nil { 276 return 277 } 278 } 279 280 if bytes.HasPrefix(in, msgPrefix) && in[len(in)-1] == '.' { 281 in = in[len(msgPrefix) : len(in)-1] 282 } else if version := isQuery(in); version > 0 { 283 c.authState = authStateAwaitingDHKey 284 c.reset() 285 toSend = c.encode(c.generateDHCommit()) 286 return 287 } else { 288 // plaintext message 289 out = in 290 return 291 } 292 293 msg := make([]byte, base64.StdEncoding.DecodedLen(len(in))) 294 msgLen, err := base64.StdEncoding.Decode(msg, in) 295 if err != nil { 296 err = errors.New("otr: invalid base64 encoding in message") 297 return 298 } 299 msg = msg[:msgLen] 300 301 // The first two bytes are the protocol version (2) 302 if len(msg) < 3 || msg[0] != 0 || msg[1] != 2 { 303 err = errors.New("otr: invalid OTR message") 304 return 305 } 306 307 msgType := int(msg[2]) 308 msg = msg[3:] 309 310 switch msgType { 311 case msgTypeDHCommit: 312 switch c.authState { 313 case authStateNone: 314 c.authState = authStateAwaitingRevealSig 315 if err = c.processDHCommit(msg); err != nil { 316 return 317 } 318 c.reset() 319 toSend = c.encode(c.generateDHKey()) 320 return 321 case authStateAwaitingDHKey: 322 // This is a 'SYN-crossing'. The greater digest wins. 323 var cmp int 324 if cmp, err = c.compareToDHCommit(msg); err != nil { 325 return 326 } 327 if cmp > 0 { 328 // We win. Retransmit DH commit. 329 toSend = c.encode(c.serializeDHCommit()) 330 return 331 } else { 332 // They win. We forget about our DH commit. 333 c.authState = authStateAwaitingRevealSig 334 if err = c.processDHCommit(msg); err != nil { 335 return 336 } 337 c.reset() 338 toSend = c.encode(c.generateDHKey()) 339 return 340 } 341 case authStateAwaitingRevealSig: 342 if err = c.processDHCommit(msg); err != nil { 343 return 344 } 345 toSend = c.encode(c.serializeDHKey()) 346 case authStateAwaitingSig: 347 if err = c.processDHCommit(msg); err != nil { 348 return 349 } 350 c.reset() 351 toSend = c.encode(c.generateDHKey()) 352 c.authState = authStateAwaitingRevealSig 353 default: 354 panic("bad state") 355 } 356 case msgTypeDHKey: 357 switch c.authState { 358 case authStateAwaitingDHKey: 359 var isSame bool 360 if isSame, err = c.processDHKey(msg); err != nil { 361 return 362 } 363 if isSame { 364 err = errors.New("otr: unexpected duplicate DH key") 365 return 366 } 367 toSend = c.encode(c.generateRevealSig()) 368 c.authState = authStateAwaitingSig 369 case authStateAwaitingSig: 370 var isSame bool 371 if isSame, err = c.processDHKey(msg); err != nil { 372 return 373 } 374 if isSame { 375 toSend = c.encode(c.serializeDHKey()) 376 } 377 } 378 case msgTypeRevealSig: 379 if c.authState != authStateAwaitingRevealSig { 380 return 381 } 382 if err = c.processRevealSig(msg); err != nil { 383 return 384 } 385 toSend = c.encode(c.generateSig()) 386 c.authState = authStateNone 387 c.state = stateEncrypted 388 change = NewKeys 389 case msgTypeSig: 390 if c.authState != authStateAwaitingSig { 391 return 392 } 393 if err = c.processSig(msg); err != nil { 394 return 395 } 396 c.authState = authStateNone 397 c.state = stateEncrypted 398 change = NewKeys 399 case msgTypeData: 400 if c.state != stateEncrypted { 401 err = errors.New("otr: encrypted message received without encrypted session established") 402 return 403 } 404 var tlvs []tlv 405 out, tlvs, err = c.processData(msg) 406 encrypted = true 407 408 EachTLV: 409 for _, inTLV := range tlvs { 410 switch inTLV.typ { 411 case tlvTypeDisconnected: 412 change = ConversationEnded 413 c.state = stateFinished 414 break EachTLV 415 case tlvTypeSMP1, tlvTypeSMP2, tlvTypeSMP3, tlvTypeSMP4, tlvTypeSMPAbort, tlvTypeSMP1WithQuestion: 416 var reply tlv 417 var complete bool 418 reply, complete, err = c.processSMP(inTLV) 419 if err == smpSecretMissingError { 420 err = nil 421 change = SMPSecretNeeded 422 c.smp.saved = &inTLV 423 return 424 } 425 if err == smpFailureError { 426 err = nil 427 change = SMPFailed 428 } else if complete { 429 change = SMPComplete 430 } 431 if reply.typ != 0 { 432 toSend = c.encode(c.generateData(nil, &reply)) 433 } 434 break EachTLV 435 default: 436 // skip unknown TLVs 437 } 438 } 439 default: 440 err = errors.New("otr: unknown message type " + strconv.Itoa(msgType)) 441 } 442 443 return 444} 445 446// Send takes a human readable message from the local user, possibly encrypts 447// it and returns zero one or more messages to send to the peer. 448func (c *Conversation) Send(msg []byte) ([][]byte, error) { 449 switch c.state { 450 case statePlaintext: 451 return [][]byte{msg}, nil 452 case stateEncrypted: 453 return c.encode(c.generateData(msg, nil)), nil 454 case stateFinished: 455 return nil, errors.New("otr: cannot send message because secure conversation has finished") 456 } 457 458 return nil, errors.New("otr: cannot send message in current state") 459} 460 461// SMPQuestion returns the human readable challenge question from the peer. 462// It's only valid after Receive has returned SMPSecretNeeded. 463func (c *Conversation) SMPQuestion() string { 464 return c.smp.question 465} 466 467// Authenticate begins an authentication with the peer. Authentication involves 468// an optional challenge message and a shared secret. The authentication 469// proceeds until either Receive returns SMPComplete, SMPSecretNeeded (which 470// indicates that a new authentication is happening and thus this one was 471// aborted) or SMPFailed. 472func (c *Conversation) Authenticate(question string, mutualSecret []byte) (toSend [][]byte, err error) { 473 if c.state != stateEncrypted { 474 err = errors.New("otr: can't authenticate a peer without a secure conversation established") 475 return 476 } 477 478 if c.smp.saved != nil { 479 c.calcSMPSecret(mutualSecret, false /* they started it */) 480 481 var out tlv 482 var complete bool 483 out, complete, err = c.processSMP(*c.smp.saved) 484 if complete { 485 panic("SMP completed on the first message") 486 } 487 c.smp.saved = nil 488 if out.typ != 0 { 489 toSend = c.encode(c.generateData(nil, &out)) 490 } 491 return 492 } 493 494 c.calcSMPSecret(mutualSecret, true /* we started it */) 495 outs := c.startSMP(question) 496 for _, out := range outs { 497 toSend = append(toSend, c.encode(c.generateData(nil, &out))...) 498 } 499 return 500} 501 502// End ends a secure conversation by generating a termination message for 503// the peer and switches to unencrypted communication. 504func (c *Conversation) End() (toSend [][]byte) { 505 switch c.state { 506 case statePlaintext: 507 return nil 508 case stateEncrypted: 509 c.state = statePlaintext 510 return c.encode(c.generateData(nil, &tlv{typ: tlvTypeDisconnected})) 511 case stateFinished: 512 c.state = statePlaintext 513 return nil 514 } 515 panic("unreachable") 516} 517 518// IsEncrypted returns true if a message passed to Send would be encrypted 519// before transmission. This result remains valid until the next call to 520// Receive or End, which may change the state of the Conversation. 521func (c *Conversation) IsEncrypted() bool { 522 return c.state == stateEncrypted 523} 524 525var fragmentError = errors.New("otr: invalid OTR fragment") 526 527// processFragment processes a fragmented OTR message and possibly returns a 528// complete message. Fragmented messages look like "?OTR,k,n,msg," where k is 529// the fragment number (starting from 1), n is the number of fragments in this 530// message and msg is a substring of the base64 encoded message. 531func (c *Conversation) processFragment(in []byte) (out []byte, err error) { 532 in = in[len(fragmentPrefix):] // remove "?OTR," 533 parts := bytes.Split(in, fragmentPartSeparator) 534 if len(parts) != 4 || len(parts[3]) != 0 { 535 return nil, fragmentError 536 } 537 538 k, err := strconv.Atoi(string(parts[0])) 539 if err != nil { 540 return nil, fragmentError 541 } 542 543 n, err := strconv.Atoi(string(parts[1])) 544 if err != nil { 545 return nil, fragmentError 546 } 547 548 if k < 1 || n < 1 || k > n { 549 return nil, fragmentError 550 } 551 552 if k == 1 { 553 c.frag = append(c.frag[:0], parts[2]...) 554 c.k, c.n = k, n 555 } else if n == c.n && k == c.k+1 { 556 c.frag = append(c.frag, parts[2]...) 557 c.k++ 558 } else { 559 c.frag = c.frag[:0] 560 c.n, c.k = 0, 0 561 } 562 563 if c.n > 0 && c.k == c.n { 564 c.n, c.k = 0, 0 565 return c.frag, nil 566 } 567 568 return nil, nil 569} 570 571func (c *Conversation) generateDHCommit() []byte { 572 _, err := io.ReadFull(c.rand(), c.r[:]) 573 if err != nil { 574 panic("otr: short read from random source") 575 } 576 577 var xBytes [dhPrivateBytes]byte 578 c.x = c.randMPI(xBytes[:]) 579 c.gx = new(big.Int).Exp(g, c.x, p) 580 c.gy = nil 581 c.gxBytes = appendMPI(nil, c.gx) 582 583 h := sha256.New() 584 h.Write(c.gxBytes) 585 h.Sum(c.digest[:0]) 586 587 aesCipher, err := aes.NewCipher(c.r[:]) 588 if err != nil { 589 panic(err.Error()) 590 } 591 592 var iv [aes.BlockSize]byte 593 ctr := cipher.NewCTR(aesCipher, iv[:]) 594 ctr.XORKeyStream(c.gxBytes, c.gxBytes) 595 596 return c.serializeDHCommit() 597} 598 599func (c *Conversation) serializeDHCommit() []byte { 600 var ret []byte 601 ret = appendU16(ret, 2) // protocol version 602 ret = append(ret, msgTypeDHCommit) 603 ret = appendData(ret, c.gxBytes) 604 ret = appendData(ret, c.digest[:]) 605 return ret 606} 607 608func (c *Conversation) processDHCommit(in []byte) error { 609 var ok1, ok2 bool 610 c.gxBytes, in, ok1 = getData(in) 611 digest, in, ok2 := getData(in) 612 if !ok1 || !ok2 || len(in) > 0 { 613 return errors.New("otr: corrupt DH commit message") 614 } 615 copy(c.digest[:], digest) 616 return nil 617} 618 619func (c *Conversation) compareToDHCommit(in []byte) (int, error) { 620 _, in, ok1 := getData(in) 621 digest, in, ok2 := getData(in) 622 if !ok1 || !ok2 || len(in) > 0 { 623 return 0, errors.New("otr: corrupt DH commit message") 624 } 625 return bytes.Compare(c.digest[:], digest), nil 626} 627 628func (c *Conversation) generateDHKey() []byte { 629 var yBytes [dhPrivateBytes]byte 630 c.y = c.randMPI(yBytes[:]) 631 c.gy = new(big.Int).Exp(g, c.y, p) 632 return c.serializeDHKey() 633} 634 635func (c *Conversation) serializeDHKey() []byte { 636 var ret []byte 637 ret = appendU16(ret, 2) // protocol version 638 ret = append(ret, msgTypeDHKey) 639 ret = appendMPI(ret, c.gy) 640 return ret 641} 642 643func (c *Conversation) processDHKey(in []byte) (isSame bool, err error) { 644 gy, _, ok := getMPI(in) 645 if !ok { 646 err = errors.New("otr: corrupt DH key message") 647 return 648 } 649 if gy.Cmp(g) < 0 || gy.Cmp(pMinus2) > 0 { 650 err = errors.New("otr: DH value out of range") 651 return 652 } 653 if c.gy != nil { 654 isSame = c.gy.Cmp(gy) == 0 655 return 656 } 657 c.gy = gy 658 return 659} 660 661func (c *Conversation) generateEncryptedSignature(keys *akeKeys, xFirst bool) ([]byte, []byte) { 662 var xb []byte 663 xb = c.PrivateKey.PublicKey.Serialize(xb) 664 665 var verifyData []byte 666 if xFirst { 667 verifyData = appendMPI(verifyData, c.gx) 668 verifyData = appendMPI(verifyData, c.gy) 669 } else { 670 verifyData = appendMPI(verifyData, c.gy) 671 verifyData = appendMPI(verifyData, c.gx) 672 } 673 verifyData = append(verifyData, xb...) 674 verifyData = appendU32(verifyData, c.myKeyId) 675 676 mac := hmac.New(sha256.New, keys.m1[:]) 677 mac.Write(verifyData) 678 mb := mac.Sum(nil) 679 680 xb = appendU32(xb, c.myKeyId) 681 xb = append(xb, c.PrivateKey.Sign(c.rand(), mb)...) 682 683 aesCipher, err := aes.NewCipher(keys.c[:]) 684 if err != nil { 685 panic(err.Error()) 686 } 687 var iv [aes.BlockSize]byte 688 ctr := cipher.NewCTR(aesCipher, iv[:]) 689 ctr.XORKeyStream(xb, xb) 690 691 mac = hmac.New(sha256.New, keys.m2[:]) 692 encryptedSig := appendData(nil, xb) 693 mac.Write(encryptedSig) 694 695 return encryptedSig, mac.Sum(nil) 696} 697 698func (c *Conversation) generateRevealSig() []byte { 699 s := new(big.Int).Exp(c.gy, c.x, p) 700 c.calcAKEKeys(s) 701 c.myKeyId++ 702 703 encryptedSig, mac := c.generateEncryptedSignature(&c.revealKeys, true /* gx comes first */) 704 705 c.myCurrentDHPub = c.gx 706 c.myCurrentDHPriv = c.x 707 c.rotateDHKeys() 708 incCounter(&c.myCounter) 709 710 var ret []byte 711 ret = appendU16(ret, 2) 712 ret = append(ret, msgTypeRevealSig) 713 ret = appendData(ret, c.r[:]) 714 ret = append(ret, encryptedSig...) 715 ret = append(ret, mac[:20]...) 716 return ret 717} 718 719func (c *Conversation) processEncryptedSig(encryptedSig, theirMAC []byte, keys *akeKeys, xFirst bool) error { 720 mac := hmac.New(sha256.New, keys.m2[:]) 721 mac.Write(appendData(nil, encryptedSig)) 722 myMAC := mac.Sum(nil)[:20] 723 724 if len(myMAC) != len(theirMAC) || subtle.ConstantTimeCompare(myMAC, theirMAC) == 0 { 725 return errors.New("bad signature MAC in encrypted signature") 726 } 727 728 aesCipher, err := aes.NewCipher(keys.c[:]) 729 if err != nil { 730 panic(err.Error()) 731 } 732 var iv [aes.BlockSize]byte 733 ctr := cipher.NewCTR(aesCipher, iv[:]) 734 ctr.XORKeyStream(encryptedSig, encryptedSig) 735 736 sig := encryptedSig 737 sig, ok1 := c.TheirPublicKey.Parse(sig) 738 keyId, sig, ok2 := getU32(sig) 739 if !ok1 || !ok2 { 740 return errors.New("otr: corrupt encrypted signature") 741 } 742 743 var verifyData []byte 744 if xFirst { 745 verifyData = appendMPI(verifyData, c.gx) 746 verifyData = appendMPI(verifyData, c.gy) 747 } else { 748 verifyData = appendMPI(verifyData, c.gy) 749 verifyData = appendMPI(verifyData, c.gx) 750 } 751 verifyData = c.TheirPublicKey.Serialize(verifyData) 752 verifyData = appendU32(verifyData, keyId) 753 754 mac = hmac.New(sha256.New, keys.m1[:]) 755 mac.Write(verifyData) 756 mb := mac.Sum(nil) 757 758 sig, ok1 = c.TheirPublicKey.Verify(mb, sig) 759 if !ok1 { 760 return errors.New("bad signature in encrypted signature") 761 } 762 if len(sig) > 0 { 763 return errors.New("corrupt encrypted signature") 764 } 765 766 c.theirKeyId = keyId 767 zero(c.theirLastCtr[:]) 768 return nil 769} 770 771func (c *Conversation) processRevealSig(in []byte) error { 772 r, in, ok1 := getData(in) 773 encryptedSig, in, ok2 := getData(in) 774 theirMAC := in 775 if !ok1 || !ok2 || len(theirMAC) != 20 { 776 return errors.New("otr: corrupt reveal signature message") 777 } 778 779 aesCipher, err := aes.NewCipher(r) 780 if err != nil { 781 return errors.New("otr: cannot create AES cipher from reveal signature message: " + err.Error()) 782 } 783 var iv [aes.BlockSize]byte 784 ctr := cipher.NewCTR(aesCipher, iv[:]) 785 ctr.XORKeyStream(c.gxBytes, c.gxBytes) 786 h := sha256.New() 787 h.Write(c.gxBytes) 788 digest := h.Sum(nil) 789 if len(digest) != len(c.digest) || subtle.ConstantTimeCompare(digest, c.digest[:]) == 0 { 790 return errors.New("otr: bad commit MAC in reveal signature message") 791 } 792 var rest []byte 793 c.gx, rest, ok1 = getMPI(c.gxBytes) 794 if !ok1 || len(rest) > 0 { 795 return errors.New("otr: gx corrupt after decryption") 796 } 797 if c.gx.Cmp(g) < 0 || c.gx.Cmp(pMinus2) > 0 { 798 return errors.New("otr: DH value out of range") 799 } 800 s := new(big.Int).Exp(c.gx, c.y, p) 801 c.calcAKEKeys(s) 802 803 if err := c.processEncryptedSig(encryptedSig, theirMAC, &c.revealKeys, true /* gx comes first */); err != nil { 804 return errors.New("otr: in reveal signature message: " + err.Error()) 805 } 806 807 c.theirCurrentDHPub = c.gx 808 c.theirLastDHPub = nil 809 810 return nil 811} 812 813func (c *Conversation) generateSig() []byte { 814 c.myKeyId++ 815 816 encryptedSig, mac := c.generateEncryptedSignature(&c.sigKeys, false /* gy comes first */) 817 818 c.myCurrentDHPub = c.gy 819 c.myCurrentDHPriv = c.y 820 c.rotateDHKeys() 821 incCounter(&c.myCounter) 822 823 var ret []byte 824 ret = appendU16(ret, 2) 825 ret = append(ret, msgTypeSig) 826 ret = append(ret, encryptedSig...) 827 ret = append(ret, mac[:macPrefixBytes]...) 828 return ret 829} 830 831func (c *Conversation) processSig(in []byte) error { 832 encryptedSig, in, ok1 := getData(in) 833 theirMAC := in 834 if !ok1 || len(theirMAC) != macPrefixBytes { 835 return errors.New("otr: corrupt signature message") 836 } 837 838 if err := c.processEncryptedSig(encryptedSig, theirMAC, &c.sigKeys, false /* gy comes first */); err != nil { 839 return errors.New("otr: in signature message: " + err.Error()) 840 } 841 842 c.theirCurrentDHPub = c.gy 843 c.theirLastDHPub = nil 844 845 return nil 846} 847 848func (c *Conversation) rotateDHKeys() { 849 // evict slots using our retired key id 850 for i := range c.keySlots { 851 slot := &c.keySlots[i] 852 if slot.used && slot.myKeyId == c.myKeyId-1 { 853 slot.used = false 854 c.oldMACs = append(c.oldMACs, slot.recvMACKey...) 855 } 856 } 857 858 c.myLastDHPriv = c.myCurrentDHPriv 859 c.myLastDHPub = c.myCurrentDHPub 860 861 var xBytes [dhPrivateBytes]byte 862 c.myCurrentDHPriv = c.randMPI(xBytes[:]) 863 c.myCurrentDHPub = new(big.Int).Exp(g, c.myCurrentDHPriv, p) 864 c.myKeyId++ 865} 866 867func (c *Conversation) processData(in []byte) (out []byte, tlvs []tlv, err error) { 868 origIn := in 869 flags, in, ok1 := getU8(in) 870 theirKeyId, in, ok2 := getU32(in) 871 myKeyId, in, ok3 := getU32(in) 872 y, in, ok4 := getMPI(in) 873 counter, in, ok5 := getNBytes(in, 8) 874 encrypted, in, ok6 := getData(in) 875 macedData := origIn[:len(origIn)-len(in)] 876 theirMAC, in, ok7 := getNBytes(in, macPrefixBytes) 877 _, in, ok8 := getData(in) 878 if !ok1 || !ok2 || !ok3 || !ok4 || !ok5 || !ok6 || !ok7 || !ok8 || len(in) > 0 { 879 err = errors.New("otr: corrupt data message") 880 return 881 } 882 883 ignoreErrors := flags&1 != 0 884 885 slot, err := c.calcDataKeys(myKeyId, theirKeyId) 886 if err != nil { 887 if ignoreErrors { 888 err = nil 889 } 890 return 891 } 892 893 mac := hmac.New(sha1.New, slot.recvMACKey) 894 mac.Write([]byte{0, 2, 3}) 895 mac.Write(macedData) 896 myMAC := mac.Sum(nil) 897 if len(myMAC) != len(theirMAC) || subtle.ConstantTimeCompare(myMAC, theirMAC) == 0 { 898 if !ignoreErrors { 899 err = errors.New("otr: bad MAC on data message") 900 } 901 return 902 } 903 904 if bytes.Compare(counter, slot.theirLastCtr[:]) <= 0 { 905 err = errors.New("otr: counter regressed") 906 return 907 } 908 copy(slot.theirLastCtr[:], counter) 909 910 var iv [aes.BlockSize]byte 911 copy(iv[:], counter) 912 aesCipher, err := aes.NewCipher(slot.recvAESKey) 913 if err != nil { 914 panic(err.Error()) 915 } 916 ctr := cipher.NewCTR(aesCipher, iv[:]) 917 ctr.XORKeyStream(encrypted, encrypted) 918 decrypted := encrypted 919 920 if myKeyId == c.myKeyId { 921 c.rotateDHKeys() 922 } 923 if theirKeyId == c.theirKeyId { 924 // evict slots using their retired key id 925 for i := range c.keySlots { 926 slot := &c.keySlots[i] 927 if slot.used && slot.theirKeyId == theirKeyId-1 { 928 slot.used = false 929 c.oldMACs = append(c.oldMACs, slot.recvMACKey...) 930 } 931 } 932 933 c.theirLastDHPub = c.theirCurrentDHPub 934 c.theirKeyId++ 935 c.theirCurrentDHPub = y 936 } 937 938 if nulPos := bytes.IndexByte(decrypted, 0); nulPos >= 0 { 939 out = decrypted[:nulPos] 940 tlvData := decrypted[nulPos+1:] 941 for len(tlvData) > 0 { 942 var t tlv 943 var ok1, ok2, ok3 bool 944 945 t.typ, tlvData, ok1 = getU16(tlvData) 946 t.length, tlvData, ok2 = getU16(tlvData) 947 t.data, tlvData, ok3 = getNBytes(tlvData, int(t.length)) 948 if !ok1 || !ok2 || !ok3 { 949 err = errors.New("otr: corrupt tlv data") 950 return 951 } 952 tlvs = append(tlvs, t) 953 } 954 } else { 955 out = decrypted 956 } 957 958 return 959} 960 961func (c *Conversation) generateData(msg []byte, extra *tlv) []byte { 962 slot, err := c.calcDataKeys(c.myKeyId-1, c.theirKeyId) 963 if err != nil { 964 panic("otr: failed to generate sending keys: " + err.Error()) 965 } 966 967 var plaintext []byte 968 plaintext = append(plaintext, msg...) 969 plaintext = append(plaintext, 0) 970 971 padding := paddingGranularity - ((len(plaintext) + 4) % paddingGranularity) 972 plaintext = appendU16(plaintext, tlvTypePadding) 973 plaintext = appendU16(plaintext, uint16(padding)) 974 for i := 0; i < padding; i++ { 975 plaintext = append(plaintext, 0) 976 } 977 978 if extra != nil { 979 plaintext = appendU16(plaintext, extra.typ) 980 plaintext = appendU16(plaintext, uint16(len(extra.data))) 981 plaintext = append(plaintext, extra.data...) 982 } 983 984 encrypted := make([]byte, len(plaintext)) 985 986 var iv [aes.BlockSize]byte 987 copy(iv[:], c.myCounter[:]) 988 aesCipher, err := aes.NewCipher(slot.sendAESKey) 989 if err != nil { 990 panic(err.Error()) 991 } 992 ctr := cipher.NewCTR(aesCipher, iv[:]) 993 ctr.XORKeyStream(encrypted, plaintext) 994 995 var ret []byte 996 ret = appendU16(ret, 2) 997 ret = append(ret, msgTypeData) 998 ret = append(ret, 0 /* flags */) 999 ret = appendU32(ret, c.myKeyId-1) 1000 ret = appendU32(ret, c.theirKeyId) 1001 ret = appendMPI(ret, c.myCurrentDHPub) 1002 ret = append(ret, c.myCounter[:]...) 1003 ret = appendData(ret, encrypted) 1004 1005 mac := hmac.New(sha1.New, slot.sendMACKey) 1006 mac.Write(ret) 1007 ret = append(ret, mac.Sum(nil)[:macPrefixBytes]...) 1008 ret = appendData(ret, c.oldMACs) 1009 c.oldMACs = nil 1010 incCounter(&c.myCounter) 1011 1012 return ret 1013} 1014 1015func incCounter(counter *[8]byte) { 1016 for i := 7; i >= 0; i-- { 1017 counter[i]++ 1018 if counter[i] > 0 { 1019 break 1020 } 1021 } 1022} 1023 1024// calcDataKeys computes the keys used to encrypt a data message given the key 1025// IDs. 1026func (c *Conversation) calcDataKeys(myKeyId, theirKeyId uint32) (slot *keySlot, err error) { 1027 // Check for a cache hit. 1028 for i := range c.keySlots { 1029 slot = &c.keySlots[i] 1030 if slot.used && slot.theirKeyId == theirKeyId && slot.myKeyId == myKeyId { 1031 return 1032 } 1033 } 1034 1035 // Find an empty slot to write into. 1036 slot = nil 1037 for i := range c.keySlots { 1038 if !c.keySlots[i].used { 1039 slot = &c.keySlots[i] 1040 break 1041 } 1042 } 1043 if slot == nil { 1044 return nil, errors.New("otr: internal error: no more key slots") 1045 } 1046 1047 var myPriv, myPub, theirPub *big.Int 1048 1049 if myKeyId == c.myKeyId { 1050 myPriv = c.myCurrentDHPriv 1051 myPub = c.myCurrentDHPub 1052 } else if myKeyId == c.myKeyId-1 { 1053 myPriv = c.myLastDHPriv 1054 myPub = c.myLastDHPub 1055 } else { 1056 err = errors.New("otr: peer requested keyid " + strconv.FormatUint(uint64(myKeyId), 10) + " when I'm on " + strconv.FormatUint(uint64(c.myKeyId), 10)) 1057 return 1058 } 1059 1060 if theirKeyId == c.theirKeyId { 1061 theirPub = c.theirCurrentDHPub 1062 } else if theirKeyId == c.theirKeyId-1 && c.theirLastDHPub != nil { 1063 theirPub = c.theirLastDHPub 1064 } else { 1065 err = errors.New("otr: peer requested keyid " + strconv.FormatUint(uint64(myKeyId), 10) + " when they're on " + strconv.FormatUint(uint64(c.myKeyId), 10)) 1066 return 1067 } 1068 1069 var sendPrefixByte, recvPrefixByte [1]byte 1070 1071 if myPub.Cmp(theirPub) > 0 { 1072 // we're the high end 1073 sendPrefixByte[0], recvPrefixByte[0] = 1, 2 1074 } else { 1075 // we're the low end 1076 sendPrefixByte[0], recvPrefixByte[0] = 2, 1 1077 } 1078 1079 s := new(big.Int).Exp(theirPub, myPriv, p) 1080 sBytes := appendMPI(nil, s) 1081 1082 h := sha1.New() 1083 h.Write(sendPrefixByte[:]) 1084 h.Write(sBytes) 1085 slot.sendAESKey = h.Sum(slot.sendAESKey[:0])[:16] 1086 1087 h.Reset() 1088 h.Write(slot.sendAESKey) 1089 slot.sendMACKey = h.Sum(slot.sendMACKey[:0]) 1090 1091 h.Reset() 1092 h.Write(recvPrefixByte[:]) 1093 h.Write(sBytes) 1094 slot.recvAESKey = h.Sum(slot.recvAESKey[:0])[:16] 1095 1096 h.Reset() 1097 h.Write(slot.recvAESKey) 1098 slot.recvMACKey = h.Sum(slot.recvMACKey[:0]) 1099 1100 slot.theirKeyId = theirKeyId 1101 slot.myKeyId = myKeyId 1102 slot.used = true 1103 1104 zero(slot.theirLastCtr[:]) 1105 return 1106} 1107 1108func (c *Conversation) calcAKEKeys(s *big.Int) { 1109 mpi := appendMPI(nil, s) 1110 h := sha256.New() 1111 1112 var cBytes [32]byte 1113 hashWithPrefix(c.SSID[:], 0, mpi, h) 1114 1115 hashWithPrefix(cBytes[:], 1, mpi, h) 1116 copy(c.revealKeys.c[:], cBytes[:16]) 1117 copy(c.sigKeys.c[:], cBytes[16:]) 1118 1119 hashWithPrefix(c.revealKeys.m1[:], 2, mpi, h) 1120 hashWithPrefix(c.revealKeys.m2[:], 3, mpi, h) 1121 hashWithPrefix(c.sigKeys.m1[:], 4, mpi, h) 1122 hashWithPrefix(c.sigKeys.m2[:], 5, mpi, h) 1123} 1124 1125func hashWithPrefix(out []byte, prefix byte, in []byte, h hash.Hash) { 1126 h.Reset() 1127 var p [1]byte 1128 p[0] = prefix 1129 h.Write(p[:]) 1130 h.Write(in) 1131 if len(out) == h.Size() { 1132 h.Sum(out[:0]) 1133 } else { 1134 digest := h.Sum(nil) 1135 copy(out, digest) 1136 } 1137} 1138 1139func (c *Conversation) encode(msg []byte) [][]byte { 1140 b64 := make([]byte, base64.StdEncoding.EncodedLen(len(msg))+len(msgPrefix)+1) 1141 base64.StdEncoding.Encode(b64[len(msgPrefix):], msg) 1142 copy(b64, msgPrefix) 1143 b64[len(b64)-1] = '.' 1144 1145 if c.FragmentSize < minFragmentSize || len(b64) <= c.FragmentSize { 1146 // We can encode this in a single fragment. 1147 return [][]byte{b64} 1148 } 1149 1150 // We have to fragment this message. 1151 var ret [][]byte 1152 bytesPerFragment := c.FragmentSize - minFragmentSize 1153 numFragments := (len(b64) + bytesPerFragment) / bytesPerFragment 1154 1155 for i := 0; i < numFragments; i++ { 1156 frag := []byte("?OTR," + strconv.Itoa(i+1) + "," + strconv.Itoa(numFragments) + ",") 1157 todo := bytesPerFragment 1158 if todo > len(b64) { 1159 todo = len(b64) 1160 } 1161 frag = append(frag, b64[:todo]...) 1162 b64 = b64[todo:] 1163 frag = append(frag, ',') 1164 ret = append(ret, frag) 1165 } 1166 1167 return ret 1168} 1169 1170func (c *Conversation) reset() { 1171 c.myKeyId = 0 1172 1173 for i := range c.keySlots { 1174 c.keySlots[i].used = false 1175 } 1176} 1177 1178type PublicKey struct { 1179 dsa.PublicKey 1180} 1181 1182func (pk *PublicKey) Parse(in []byte) ([]byte, bool) { 1183 var ok bool 1184 var pubKeyType uint16 1185 1186 if pubKeyType, in, ok = getU16(in); !ok || pubKeyType != 0 { 1187 return nil, false 1188 } 1189 if pk.P, in, ok = getMPI(in); !ok { 1190 return nil, false 1191 } 1192 if pk.Q, in, ok = getMPI(in); !ok { 1193 return nil, false 1194 } 1195 if pk.G, in, ok = getMPI(in); !ok { 1196 return nil, false 1197 } 1198 if pk.Y, in, ok = getMPI(in); !ok { 1199 return nil, false 1200 } 1201 1202 return in, true 1203} 1204 1205func (pk *PublicKey) Serialize(in []byte) []byte { 1206 in = appendU16(in, 0) 1207 in = appendMPI(in, pk.P) 1208 in = appendMPI(in, pk.Q) 1209 in = appendMPI(in, pk.G) 1210 in = appendMPI(in, pk.Y) 1211 return in 1212} 1213 1214// Fingerprint returns the 20-byte, binary fingerprint of the PublicKey. 1215func (pk *PublicKey) Fingerprint() []byte { 1216 b := pk.Serialize(nil) 1217 h := sha1.New() 1218 h.Write(b[2:]) 1219 return h.Sum(nil) 1220} 1221 1222func (pk *PublicKey) Verify(hashed, sig []byte) ([]byte, bool) { 1223 if len(sig) != 2*dsaSubgroupBytes { 1224 return nil, false 1225 } 1226 r := new(big.Int).SetBytes(sig[:dsaSubgroupBytes]) 1227 s := new(big.Int).SetBytes(sig[dsaSubgroupBytes:]) 1228 ok := dsa.Verify(&pk.PublicKey, hashed, r, s) 1229 return sig[dsaSubgroupBytes*2:], ok 1230} 1231 1232type PrivateKey struct { 1233 PublicKey 1234 dsa.PrivateKey 1235} 1236 1237func (priv *PrivateKey) Sign(rand io.Reader, hashed []byte) []byte { 1238 r, s, err := dsa.Sign(rand, &priv.PrivateKey, hashed) 1239 if err != nil { 1240 panic(err.Error()) 1241 } 1242 rBytes := r.Bytes() 1243 sBytes := s.Bytes() 1244 if len(rBytes) > dsaSubgroupBytes || len(sBytes) > dsaSubgroupBytes { 1245 panic("DSA signature too large") 1246 } 1247 1248 out := make([]byte, 2*dsaSubgroupBytes) 1249 copy(out[dsaSubgroupBytes-len(rBytes):], rBytes) 1250 copy(out[len(out)-len(sBytes):], sBytes) 1251 return out 1252} 1253 1254func (priv *PrivateKey) Serialize(in []byte) []byte { 1255 in = priv.PublicKey.Serialize(in) 1256 in = appendMPI(in, priv.PrivateKey.X) 1257 return in 1258} 1259 1260func (priv *PrivateKey) Parse(in []byte) ([]byte, bool) { 1261 in, ok := priv.PublicKey.Parse(in) 1262 if !ok { 1263 return in, ok 1264 } 1265 priv.PrivateKey.PublicKey = priv.PublicKey.PublicKey 1266 priv.PrivateKey.X, in, ok = getMPI(in) 1267 return in, ok 1268} 1269 1270func (priv *PrivateKey) Generate(rand io.Reader) { 1271 if err := dsa.GenerateParameters(&priv.PrivateKey.PublicKey.Parameters, rand, dsa.L1024N160); err != nil { 1272 panic(err.Error()) 1273 } 1274 if err := dsa.GenerateKey(&priv.PrivateKey, rand); err != nil { 1275 panic(err.Error()) 1276 } 1277 priv.PublicKey.PublicKey = priv.PrivateKey.PublicKey 1278} 1279 1280func notHex(r rune) bool { 1281 if r >= '0' && r <= '9' || 1282 r >= 'a' && r <= 'f' || 1283 r >= 'A' && r <= 'F' { 1284 return false 1285 } 1286 1287 return true 1288} 1289 1290// Import parses the contents of a libotr private key file. 1291func (priv *PrivateKey) Import(in []byte) bool { 1292 mpiStart := []byte(" #") 1293 1294 mpis := make([]*big.Int, 5) 1295 1296 for i := 0; i < len(mpis); i++ { 1297 start := bytes.Index(in, mpiStart) 1298 if start == -1 { 1299 return false 1300 } 1301 in = in[start+len(mpiStart):] 1302 end := bytes.IndexFunc(in, notHex) 1303 if end == -1 { 1304 return false 1305 } 1306 hexBytes := in[:end] 1307 in = in[end:] 1308 1309 if len(hexBytes)&1 != 0 { 1310 return false 1311 } 1312 1313 mpiBytes := make([]byte, len(hexBytes)/2) 1314 if _, err := hex.Decode(mpiBytes, hexBytes); err != nil { 1315 return false 1316 } 1317 1318 mpis[i] = new(big.Int).SetBytes(mpiBytes) 1319 } 1320 1321 for _, mpi := range mpis { 1322 if mpi.Sign() <= 0 { 1323 return false 1324 } 1325 } 1326 1327 priv.PrivateKey.P = mpis[0] 1328 priv.PrivateKey.Q = mpis[1] 1329 priv.PrivateKey.G = mpis[2] 1330 priv.PrivateKey.Y = mpis[3] 1331 priv.PrivateKey.X = mpis[4] 1332 priv.PublicKey.PublicKey = priv.PrivateKey.PublicKey 1333 1334 a := new(big.Int).Exp(priv.PrivateKey.G, priv.PrivateKey.X, priv.PrivateKey.P) 1335 return a.Cmp(priv.PrivateKey.Y) == 0 1336} 1337 1338func getU8(in []byte) (uint8, []byte, bool) { 1339 if len(in) < 1 { 1340 return 0, in, false 1341 } 1342 return in[0], in[1:], true 1343} 1344 1345func getU16(in []byte) (uint16, []byte, bool) { 1346 if len(in) < 2 { 1347 return 0, in, false 1348 } 1349 r := uint16(in[0])<<8 | uint16(in[1]) 1350 return r, in[2:], true 1351} 1352 1353func getU32(in []byte) (uint32, []byte, bool) { 1354 if len(in) < 4 { 1355 return 0, in, false 1356 } 1357 r := uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3]) 1358 return r, in[4:], true 1359} 1360 1361func getMPI(in []byte) (*big.Int, []byte, bool) { 1362 l, in, ok := getU32(in) 1363 if !ok || uint32(len(in)) < l { 1364 return nil, in, false 1365 } 1366 r := new(big.Int).SetBytes(in[:l]) 1367 return r, in[l:], true 1368} 1369 1370func getData(in []byte) ([]byte, []byte, bool) { 1371 l, in, ok := getU32(in) 1372 if !ok || uint32(len(in)) < l { 1373 return nil, in, false 1374 } 1375 return in[:l], in[l:], true 1376} 1377 1378func getNBytes(in []byte, n int) ([]byte, []byte, bool) { 1379 if len(in) < n { 1380 return nil, in, false 1381 } 1382 return in[:n], in[n:], true 1383} 1384 1385func appendU16(out []byte, v uint16) []byte { 1386 out = append(out, byte(v>>8), byte(v)) 1387 return out 1388} 1389 1390func appendU32(out []byte, v uint32) []byte { 1391 out = append(out, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) 1392 return out 1393} 1394 1395func appendData(out, v []byte) []byte { 1396 out = appendU32(out, uint32(len(v))) 1397 out = append(out, v...) 1398 return out 1399} 1400 1401func appendMPI(out []byte, v *big.Int) []byte { 1402 vBytes := v.Bytes() 1403 out = appendU32(out, uint32(len(vBytes))) 1404 out = append(out, vBytes...) 1405 return out 1406} 1407 1408func appendMPIs(out []byte, mpis ...*big.Int) []byte { 1409 for _, mpi := range mpis { 1410 out = appendMPI(out, mpi) 1411 } 1412 return out 1413} 1414 1415func zero(b []byte) { 1416 for i := range b { 1417 b[i] = 0 1418 } 1419} 1420