コード例 #1
0
ファイル: msg_test.go プロジェクト: JonathanLogan/mute
func TestMaxMessageLength(t *testing.T) {
	alice := "*****@*****.**"
	aliceUID, err := uid.Create(alice, false, "", "", uid.Strict,
		hashchain.TestEntry, cipher.RandReader)
	if err != nil {
		t.Fatal(err)
	}
	bob := "*****@*****.**"
	bobUID, err := uid.Create(bob, false, "", "", uid.Strict,
		hashchain.TestEntry, cipher.RandReader)
	if err != nil {
		t.Fatal(err)
	}
	now := uint64(times.Now())
	bobKI, _, privateKey, err := bobUID.KeyInit(1, now+times.Day, now-times.Day,
		false, "mute.berlin", "", "", cipher.RandReader)
	if err != nil {
		t.Fatal(err)
	}
	bobKE, err := bobKI.KeyEntryECDHE25519(bobUID.SigPubKey())
	if err != nil {
		t.Fatal(err)
	}
	// create large message
	message, err := padding.Generate(MaxContentLength, cipher.RandReader)
	if err != nil {
		t.Fatal(err)
	}
	// encrypt message from Alice to Bob
	var encMsg bytes.Buffer
	aliceKeyStore := memstore.New()
	aliceKeyStore.AddPublicKeyEntry(bob, bobKE)
	encryptArgs := &EncryptArgs{
		Writer: &encMsg,
		From:   aliceUID,
		To:     bobUID,
		SenderLastKeychainHash: hashchain.TestEntry,
		PrivateSigKey:          aliceUID.PrivateSigKey64(),
		Reader:                 bytes.NewBuffer(message),
		Rand:                   cipher.RandReader,
		KeyStore:               aliceKeyStore,
	}
	if _, err = Encrypt(encryptArgs); err != nil {
		t.Fatal(err)
	}
	// decrypt message from Alice to Bob
	var res bytes.Buffer
	bobIdentities := []*uid.Message{bobUID}
	input := base64.NewDecoder(&encMsg)
	version, preHeader, err := ReadFirstOuterHeader(input)
	if err != nil {
		t.Fatal(err)
	}
	if version != Version {
		t.Fatal("wrong version")
	}
	bobKeyStore := memstore.New()
	if err := bobKE.SetPrivateKey(privateKey); err != nil {
		t.Fatal(err)
	}
	bobKeyStore.AddPrivateKeyEntry(bobKE)
	decryptArgs := &DecryptArgs{
		Writer:     &res,
		Identities: bobIdentities,
		PreHeader:  preHeader,
		Reader:     input,
		Rand:       cipher.RandReader,
		KeyStore:   bobKeyStore,
	}
	_, _, err = Decrypt(decryptArgs)
	if err != nil {
		t.Fatal(err)
	}
	if res.String() != string(message) {
		t.Fatal("messages differ")
	}
}
コード例 #2
0
ファイル: header.go プロジェクト: JonathanLogan/mute
func newHeader(
	sender, recipient *uid.Message,
	recipientTempHash string,
	senderSessionPub, nextSenderSessionPub,
	nextRecipientSessionPubSeen *uid.KeyEntry,
	nymAddress string,
	senderSessionCount, senderMessageCount uint64,
	senderLastKeychainHash string,
	rand io.Reader,
	statusCode StatusCode,
) (*header, error) {
	if len(senderLastKeychainHash) != hashchain.EntryBase64Len {
		return nil, log.Errorf("msg: last hashchain entry '%s' does not have base64 length %d (but %d)",
			senderLastKeychainHash, hashchain.EntryBase64Len, len(senderLastKeychainHash))
	}
	h := &header{
		Ciphersuite:                 uid.DefaultCiphersuite, // at the moment we only support one ciphersuite
		RecipientPubHash:            recipient.PubHash(),
		RecipientTempHash:           recipientTempHash,
		SenderIdentity:              sender.Identity(),
		SenderSessionPub:            *senderSessionPub,
		SenderIdentityPubHash:       sender.PubHash(),
		SenderIdentityPub:           *sender.PubKey(),
		NextSenderSessionPub:        nextSenderSessionPub,
		NextRecipientSessionPubSeen: nextRecipientSessionPubSeen,
		NymAddress:                  nymAddress,
		MaxDelay:                    0, // TODO
		SenderSessionCount:          senderSessionCount,
		SenderMessageCount:          senderMessageCount,
		SenderUID:                   string(sender.JSON()),
		SenderLastKeychainHash:      senderLastKeychainHash,
		Status:                      statusCode,
		Padding:                     "", // is set below
	}

	// calculate padding length
	padLen := wiggleRoom
	// pad sender identity
	if len(h.SenderIdentity) > identity.MaxLen {
		return nil, log.Error("msg: sender identity is too long")
	}
	padLen += identity.MaxLen - len(h.SenderIdentity)
	// pad nextSenderSessionPub
	if nextSenderSessionPub == nil {
		padLen += length.KeyEntryECDHE25519 - length.Nil
	}
	// pad nextRecipientSessionPubSeen
	if nextRecipientSessionPubSeen == nil {
		padLen += length.KeyEntryECDHE25519 - length.Nil
	}
	// pad nym address
	if len(h.NymAddress) > length.MaxNymAddress {
		return nil, log.Error("msg: nym address is too long")
	}
	padLen += length.MaxNymAddress - len(h.NymAddress)
	// pad integers
	padLen += 20 - digits.Count(h.MaxDelay)
	padLen += 20 - digits.Count(h.SenderSessionCount)
	padLen += 20 - digits.Count(h.SenderMessageCount)
	// pad sender UIDMessage
	if len(h.SenderUID) > length.MaxUIDMessage {
		return nil, log.Error("msg: sender UIDMesssage is too long")
	}
	padLen += length.MaxUIDMessage - len(h.SenderUID)
	// generate padding
	randLen := padLen/2 + padLen%2
	pad, err := padding.Generate(randLen, cipher.RandReader)
	if err != nil {
		return nil, err
	}
	// set padding
	p := hex.EncodeToString(pad)
	if padLen%2 == 1 {
		p = p[:len(p)-1]
	}
	h.Padding = p
	return h, nil
}
コード例 #3
0
ファイル: encrypt.go プロジェクト: JonathanLogan/mute
// Encrypt encrypts a message with the argument given in args and returns the
// nymAddress the message should be delivered to.
func Encrypt(args *EncryptArgs) (nymAddress string, err error) {
	log.Debugf("msg.Encrypt(): %s -> %s", args.From.Identity(), args.To.Identity())

	// set defaults
	if args.NumOfKeys == 0 {
		args.NumOfKeys = NumOfFutureKeys
	}
	if args.AvgSessionSize == 0 {
		args.AvgSessionSize = AverageSessionSize
	}

	// create sender key
	senderHeaderKey, err := cipher.Curve25519Generate(cipher.RandReader)
	if err != nil {
		return "", log.Error(err)
	}

	// create pre-header
	ph := newPreHeader(senderHeaderKey.PublicKey()[:])

	// create base64 encoder
	var out bytes.Buffer
	wc := base64.NewEncoder(&out)

	// write pre-header
	var buf bytes.Buffer
	var count uint32
	if err := ph.write(&buf); err != nil {
		return "", err
	}
	oh := newOuterHeader(preHeaderPacket, count, buf.Bytes())
	if err := oh.write(wc, true); err != nil {
		return "", err
	}
	count++

	// get session state
	sender := args.From.Identity()
	recipient := args.To.Identity()
	sessionStateKey := session.CalcStateKey(args.From.PubKey().PublicKey32(),
		args.To.PubKey().PublicKey32())
	var ss *session.State
	if args.StatusCode != StatusReset {
		ss, err = args.KeyStore.GetSessionState(sessionStateKey)
		if err != nil {
			return "", err
		}
	}
	if ss == nil {
		// no session found -> start first session
		log.Debug("no session found -> start first session")
		var recipientTemp *uid.KeyEntry
		recipientTemp, nymAddress, err = args.KeyStore.GetPublicKeyEntry(args.To)
		if err != nil {
			return "", err
		}
		// create session key
		var senderSession uid.KeyEntry
		if err := senderSession.InitDHKey(args.Rand); err != nil {
			return "", err
		}
		// store session key
		if err := addSessionKey(args.KeyStore, &senderSession); err != nil {
			return "", err
		}
		// root key agreement
		err = rootKeyAgreementSender(senderHeaderKey.PublicKey(),
			args.From.Identity(), args.To.Identity(), &senderSession,
			args.From.PubKey(), recipientTemp, args.To.PubKey(), nil,
			args.NumOfKeys, args.KeyStore)
		if err != nil {
			return "", err
		}
		// set session state
		ss = &session.State{
			SenderSessionCount:          0,
			SenderMessageCount:          0,
			MaxRecipientCount:           0,
			RecipientTemp:               *recipientTemp,
			SenderSessionPub:            senderSession,
			NextSenderSessionPub:        nil,
			NextRecipientSessionPubSeen: nil,
			NymAddress:                  nymAddress,
			KeyInitSession:              true,
		}
		log.Debugf("set session: %s", ss.SenderSessionPub.HASH)
		err = args.KeyStore.SetSessionState(sessionStateKey, ss)
		if err != nil {
			return "", err
		}
	} else if args.StatusCode != StatusError { // do not update sessions for StatusError messages
		log.Debug("session found")
		log.Debugf("got session: %s", ss.SenderSessionPub.HASH)
		nymAddress = ss.NymAddress
		if ss.NextSenderSessionPub == nil {
			// start new session in randomized fashion
			n, err := rand.Int(cipher.RandReader, big.NewInt(int64(args.AvgSessionSize)))
			if err != nil {
				return "", err
			}
			if n.Int64() == 0 {
				_, err := setNextSenderSessionPub(args.KeyStore, ss,
					sessionStateKey, args.Rand)
				if err != nil {
					return "", err
				}
			}
		}
	}

	// create header
	log.Debugf("senderID:    %s", args.From.UIDContent.PUBKEYS[0].HASH)
	log.Debugf("recipientID: %s", args.To.UIDContent.PUBKEYS[0].HASH)
	log.Debugf("ss.SenderSessionCount: %d", ss.SenderSessionCount)
	log.Debugf("ss.SenderMessageCount: %d", ss.SenderMessageCount)
	log.Debugf("ss.RecipientTempHash:  %s", ss.RecipientTemp.HASH)
	h, err := newHeader(args.From, args.To, ss.RecipientTemp.HASH,
		&ss.SenderSessionPub, ss.NextSenderSessionPub,
		ss.NextRecipientSessionPubSeen, args.NymAddress, ss.SenderSessionCount,
		ss.SenderMessageCount, args.SenderLastKeychainHash, args.Rand,
		args.StatusCode)
	if err != nil {
		return "", err
	}
	log.Debugf("h.SenderSessionPub:             %s", h.SenderSessionPub.HASH)
	if h.NextSenderSessionPub != nil {
		log.Debugf("h.NextSenderSessionPub:         %s", h.NextSenderSessionPub.HASH)
	}
	if h.NextRecipientSessionPubSeen != nil {
		log.Debugf("h.NextRecipientSessionPubSeen:  %s",
			h.NextRecipientSessionPubSeen.HASH)
	}

	// create (encrypted) header packet
	recipientIdentityPub, err := args.To.PublicKey()
	if err != nil {
		return "", err
	}

	hp, err := newHeaderPacket(h, recipientIdentityPub,
		senderHeaderKey.PrivateKey(), args.Rand)
	if err != nil {
		return "", err
	}

	// write (encrypted) header packet
	buf.Reset()
	if err := hp.write(&buf); err != nil {
		return "", err
	}
	oh = newOuterHeader(encryptedHeader, count, buf.Bytes())
	if err := oh.write(wc, true); err != nil {
		return "", err
	}
	count++

	sessionKey := session.CalcKey(args.From.PubKey().HASH,
		args.To.PubKey().HASH, ss.SenderSessionPub.HASH, ss.RecipientTemp.HASH)

	// make sure we got enough message keys
	n, err := args.KeyStore.NumMessageKeys(sessionKey)
	if err != nil {
		return "", err
	}
	if ss.SenderMessageCount >= n {
		// generate more message keys
		log.Debugf("generate more message keys (ss.SenderMessageCount=%d)",
			ss.SenderMessageCount)
		chainKey, err := args.KeyStore.GetChainKey(sessionKey)
		if err != nil {
			return "", err
		}

		err = generateMessageKeys(sender, recipient, args.From.PubKey().HASH,
			args.To.PubKey().HASH, chainKey, false,
			ss.SenderSessionPub.PublicKey32(), ss.RecipientTemp.PublicKey32(),
			args.NumOfKeys, args.KeyStore)
		if err != nil {
			return "", err
		}
	}

	// get message key
	messageKey, err := args.KeyStore.GetMessageKey(sessionKey, true,
		ss.SenderMessageCount)
	if err != nil {
		return "", err
	}

	// derive symmetric keys
	cryptoKey, hmacKey, err := deriveSymmetricKeys(messageKey)
	if err != nil {
		return "", err
	}

	// write crypto setup packet
	iv := make([]byte, aes.BlockSize)
	if _, err := io.ReadFull(args.Rand, iv); err != nil {
		return "", log.Error(err)
	}
	oh = newOuterHeader(cryptoSetup, count, iv)

	if err := oh.write(wc, true); err != nil {
		return "", err
	}
	count++

	// start HMAC calculation
	mac := hmac.New(sha512.New, hmacKey)
	if err := oh.write(mac, true); err != nil {
		return "", err
	}

	// actual encryption
	var content []byte
	if args.StatusCode == StatusOK { // StatusReset and StatusError messages are empty
		content, err = ioutil.ReadAll(args.Reader)
		if err != nil {
			return "", log.Error(err)
		}
	}
	// enforce maximum content length
	if len(content) > MaxContentLength {
		return "", log.Errorf("len(content) = %d > %d = MaxContentLength)",
			len(content), MaxContentLength)
	}

	// encrypted packet
	var contentHash []byte
	var innerType uint8
	if args.PrivateSigKey != nil {
		contentHash = cipher.SHA512(content)
		innerType = dataType | signType
	} else {
		innerType = dataType
	}
	ih := newInnerHeader(innerType, false, content)
	buf.Reset()
	if err := ih.write(&buf); err != nil {
		return "", err
	}
	stream := cipher.AES256CTRStream(cryptoKey, iv)
	stream.XORKeyStream(buf.Bytes(), buf.Bytes())
	oh = newOuterHeader(encryptedPacket, count, buf.Bytes())
	if err := oh.write(wc, true); err != nil {
		return "", err
	}
	count++

	// continue HMAC calculation
	if err := oh.write(mac, true); err != nil {
		return "", err
	}

	// signature header & padding
	buf.Reset()
	if args.PrivateSigKey != nil {
		sig := ed25519.Sign(args.PrivateSigKey, contentHash)
		// signature
		ih = newInnerHeader(signatureType, true, sig[:])
		if err := ih.write(&buf); err != nil {
			return "", err
		}
		// padding
		padLen := MaxContentLength - len(content)
		pad, err := padding.Generate(padLen, cipher.RandReader)
		if err != nil {
			return "", err
		}
		ih = newInnerHeader(paddingType, false, pad)
		if err := ih.write(&buf); err != nil {
			return "", err
		}
	} else {
		// just padding
		padLen := MaxContentLength + signatureSize - encryptedPacketSize +
			innerHeaderSize - len(content)
		pad, err := padding.Generate(padLen, cipher.RandReader)
		if err != nil {
			return "", err
		}
		ih = newInnerHeader(paddingType, false, pad)
		if err := ih.write(&buf); err != nil {
			return "", err
		}
	}
	// encrypt inner header
	stream.XORKeyStream(buf.Bytes(), buf.Bytes())
	oh = newOuterHeader(encryptedPacket, count, buf.Bytes())
	if err := oh.write(wc, true); err != nil {
		return "", err
	}
	count++

	// continue HMAC calculation
	if err := oh.write(mac, true); err != nil {
		return "", err
	}

	// create HMAC header
	oh = newOuterHeader(hmacPacket, count, nil)
	oh.PLen = sha512.Size
	if err := oh.write(mac, false); err != nil {
		return "", err
	}
	oh.inner = mac.Sum(oh.inner)
	log.Debugf("HMAC:       %s", base64.Encode(oh.inner))
	if err := oh.write(wc, true); err != nil {
		return "", err
	}
	count++

	// write output
	wc.Close()
	if out.Len() != EncodedMsgSize {
		return "", log.Errorf("out.Len() = %d != %d = EncodedMsgSize)",
			out.Len(), EncodedMsgSize)
	}
	if _, err := io.Copy(args.Writer, &out); err != nil {
		return "", log.Error(err)
	}

	// delete message key
	err = args.KeyStore.DelMessageKey(sessionKey, true, ss.SenderMessageCount)
	if err != nil {
		return "", err
	}
	// increase SenderMessageCount
	ss.SenderMessageCount++
	err = args.KeyStore.SetSessionState(sessionStateKey, ss)
	if err != nil {
		return "", err
	}

	return
}