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