// Encrypt plaintext with AES in CBC mode.
func encryptAESCBC(plaintext []byte) ([]byte, []byte, []byte, error) {
	// CBC mode works on blocks so plaintexts need to be padded to the
	// next whole block (https://tools.ietf.org/html/rfc5246#section-6.2.3.2).
	plaintext = AddPKCS7Padding(plaintext, aes.BlockSize)

	ciphertext := make([]byte, len(plaintext))
	iv, err := common.MakeSecureRandomBytes(aes.BlockSize)
	if err != nil {
		return nil, nil, nil, err
	}

	key, err := common.MakeSecureRandomBytes(aes.BlockSize)
	if err != nil {
		return nil, nil, nil, common.ContextError(err)
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, nil, nil, common.ContextError(err)
	}

	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(ciphertext, plaintext)

	return iv, key, ciphertext, nil
}
Example #2
0
// makeOSLFileSpec creates a random OSL file key, splits it according
// the the scheme's key splits, and sets the OSL ID as its first SLOK
// ID. The returned key is used to encrypt the OSL payload and then
// discarded; the key may be reassembled using the data in the KeyShares
// tree, given sufficient SLOKs.
func makeOSLFileSpec(
	scheme *Scheme,
	propagationChannelID string,
	firstSLOKTime time.Time) ([]byte, *OSLFileSpec, error) {

	ref := &slokReference{
		PropagationChannelID: propagationChannelID,
		SeedSpecID:           string(scheme.SeedSpecs[0].ID),
		Time:                 firstSLOKTime,
	}
	firstSLOK := deriveSLOK(scheme, ref)
	oslID := firstSLOK.ID

	fileKey, err := common.MakeSecureRandomBytes(KEY_LENGTH_BYTES)
	if err != nil {
		return nil, nil, common.ContextError(err)
	}

	keyShares, err := divideKey(
		scheme,
		fileKey,
		scheme.SeedPeriodKeySplits,
		propagationChannelID,
		&firstSLOKTime)
	if err != nil {
		return nil, nil, common.ContextError(err)
	}

	fileSpec := &OSLFileSpec{
		ID:        oslID,
		KeyShares: keyShares,
	}

	return fileKey, fileSpec, nil
}
// MakeSessionId creates a new session ID. The same session ID is used across
// multi-tunnel controller runs, where each tunnel has its own ServerContext
// instance.
// In server-side stats, we now consider a "session" to be the lifetime of the
// Controller (e.g., the user's commanded start and stop) and we measure this
// duration as well as the duration of each tunnel within the session.
func MakeSessionId() (sessionId string, err error) {
	randomId, err := common.MakeSecureRandomBytes(common.PSIPHON_API_CLIENT_SESSION_ID_LENGTH)
	if err != nil {
		return "", common.ContextError(err)
	}
	return hex.EncodeToString(randomId), nil
}
// NewClientObfuscator creates a new Obfuscator, staging a seed message to be
// sent to the server (by the caller) and initializing stream ciphers to
// obfuscate data.
func NewClientObfuscator(
	config *ObfuscatorConfig) (obfuscator *Obfuscator, err error) {

	seed, err := common.MakeSecureRandomBytes(OBFUSCATE_SEED_LENGTH)
	if err != nil {
		return nil, common.ContextError(err)
	}

	clientToServerCipher, serverToClientCipher, err := initObfuscatorCiphers(seed, config)
	if err != nil {
		return nil, common.ContextError(err)
	}

	maxPadding := OBFUSCATE_MAX_PADDING
	if config.MaxPadding > 0 {
		maxPadding = config.MaxPadding
	}

	seedMessage, err := makeSeedMessage(maxPadding, seed, clientToServerCipher)
	if err != nil {
		return nil, common.ContextError(err)
	}

	return &Obfuscator{
		seedMessage:          seedMessage,
		clientToServerCipher: clientToServerCipher,
		serverToClientCipher: serverToClientCipher}, nil
}
func makeSeedMessage(maxPadding int, seed []byte, clientToServerCipher *rc4.Cipher) ([]byte, error) {
	// paddingLength is integer in range [0, maxPadding]
	paddingLength, err := common.MakeSecureRandomInt(maxPadding + 1)
	if err != nil {
		return nil, common.ContextError(err)
	}
	padding, err := common.MakeSecureRandomBytes(paddingLength)
	if err != nil {
		return nil, common.ContextError(err)
	}
	buffer := new(bytes.Buffer)
	err = binary.Write(buffer, binary.BigEndian, seed)
	if err != nil {
		return nil, common.ContextError(err)
	}
	err = binary.Write(buffer, binary.BigEndian, uint32(OBFUSCATE_MAGIC_VALUE))
	if err != nil {
		return nil, common.ContextError(err)
	}
	err = binary.Write(buffer, binary.BigEndian, uint32(paddingLength))
	if err != nil {
		return nil, common.ContextError(err)
	}
	err = binary.Write(buffer, binary.BigEndian, padding)
	if err != nil {
		return nil, common.ContextError(err)
	}
	seedMessage := buffer.Bytes()
	clientToServerCipher.XORKeyStream(seedMessage[len(seed):], seedMessage[len(seed):])
	return seedMessage, nil
}
// Generate HMAC for Encrypt-then-MAC paradigm.
func generateHMAC(iv, plaintext []byte) ([]byte, []byte, error) {
	key, err := common.MakeSecureRandomBytes(16)
	if err != nil {
		return nil, nil, err
	}

	mac := hmac.New(sha256.New, key)

	mac.Write(iv)
	mac.Write(plaintext)

	digest := mac.Sum(nil)

	return digest, key, nil
}
// Encrypt feedback and upload to server. If upload fails
// the feedback thread will sleep and retry multiple times.
func SendFeedback(configJson, diagnosticsJson, b64EncodedPublicKey, uploadServer, uploadPath, uploadServerHeaders string) error {

	config, err := LoadConfig([]byte(configJson))
	if err != nil {
		return common.ContextError(err)
	}

	untunneledDialConfig := &DialConfig{
		UpstreamProxyUrl:              config.UpstreamProxyUrl,
		UpstreamProxyCustomHeaders:    config.UpstreamProxyCustomHeaders,
		PendingConns:                  nil,
		DeviceBinder:                  nil,
		DnsServerGetter:               nil,
		UseIndistinguishableTLS:       config.UseIndistinguishableTLS,
		TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,
		DeviceRegion:                  config.DeviceRegion,
	}

	secureFeedback, err := encryptFeedback(diagnosticsJson, b64EncodedPublicKey)
	if err != nil {
		return err
	}

	randBytes, err := common.MakeSecureRandomBytes(8)
	if err != nil {
		return err
	}
	uploadId := hex.EncodeToString(randBytes)

	url := "https://" + uploadServer + uploadPath + uploadId
	headerPieces := strings.Split(uploadServerHeaders, ": ")
	// Only a single header is expected.
	if len(headerPieces) != 2 {
		return common.ContextError(errors.New("expected 2 header pieces, got: " + strconv.Itoa(len(headerPieces))))
	}

	for i := 0; i < FEEDBACK_UPLOAD_MAX_RETRIES; i++ {
		err := uploadFeedback(untunneledDialConfig, secureFeedback, url, headerPieces)
		if err != nil {
			NoticeAlert("failed to upload feedback: %s", err)
			time.Sleep(FEEDBACK_UPLOAD_RETRY_DELAY_SECONDS * time.Second)
		} else {
			break
		}
	}
	return nil
}
func extractSshPackets(writeBuffer []byte) ([]byte, []byte, bool, error) {
	var packetBuffer, packetsBuffer []byte
	hasMsgNewKeys := false
	for len(writeBuffer) >= SSH_PACKET_PREFIX_LENGTH {
		packetLength, paddingLength, payloadLength, messageLength := getSshPacketPrefix(writeBuffer)
		if len(writeBuffer) < messageLength {
			// We don't have the complete packet yet
			break
		}
		packetBuffer = append([]byte(nil), writeBuffer[:messageLength]...)
		writeBuffer = writeBuffer[messageLength:]
		if payloadLength > 0 {
			packetType := int(packetBuffer[SSH_PACKET_PREFIX_LENGTH])
			if packetType == SSH_MSG_NEWKEYS {
				hasMsgNewKeys = true
			}
		}
		// Padding transformation
		// See RFC 4253 sec. 6 for constraints
		possiblePaddings := (SSH_MAX_PADDING_LENGTH - paddingLength) / SSH_PADDING_MULTIPLE
		if possiblePaddings > 0 {
			// selectedPadding is integer in range [0, possiblePaddings)
			selectedPadding, err := common.MakeSecureRandomInt(possiblePaddings)
			if err != nil {
				return nil, nil, false, common.ContextError(err)
			}
			extraPaddingLength := selectedPadding * SSH_PADDING_MULTIPLE
			extraPadding, err := common.MakeSecureRandomBytes(extraPaddingLength)
			if err != nil {
				return nil, nil, false, common.ContextError(err)
			}
			setSshPacketPrefix(
				packetBuffer, packetLength+extraPaddingLength, paddingLength+extraPaddingLength)
			packetBuffer = append(packetBuffer, extraPadding...)
		}
		packetsBuffer = append(packetsBuffer, packetBuffer...)
	}
	return writeBuffer, packetsBuffer, hasMsgNewKeys, nil
}
Example #9
0
// divideKey recursively constructs a KeyShares tree.
func divideKey(
	scheme *Scheme,
	key []byte,
	keySplits []KeySplit,
	propagationChannelID string,
	nextSLOKTime *time.Time) (*KeyShares, error) {

	keySplitIndex := len(keySplits) - 1
	keySplit := keySplits[keySplitIndex]

	shares, err := shamirSplit(key, keySplit.Total, keySplit.Threshold)
	if err != nil {
		return nil, common.ContextError(err)
	}

	var boxedShares [][]byte
	var keyShares []*KeyShares

	for _, share := range shares {
		shareKey, err := common.MakeSecureRandomBytes(KEY_LENGTH_BYTES)
		if err != nil {
			return nil, common.ContextError(err)
		}
		if keySplitIndex > 0 {
			keyShare, err := divideKey(
				scheme,
				shareKey,
				keySplits[0:keySplitIndex],
				propagationChannelID,
				nextSLOKTime)
			if err != nil {
				return nil, common.ContextError(err)
			}
			keyShares = append(keyShares, keyShare)
		} else {
			keyShare, err := divideKeyWithSeedSpecSLOKs(
				scheme,
				shareKey,
				propagationChannelID,
				nextSLOKTime)
			if err != nil {
				return nil, common.ContextError(err)
			}
			keyShares = append(keyShares, keyShare)

			*nextSLOKTime = nextSLOKTime.Add(time.Duration(scheme.SeedPeriodNanoseconds))
		}
		boxedShare, err := box(shareKey, share)
		if err != nil {
			return nil, common.ContextError(err)
		}
		boxedShares = append(boxedShares, boxedShare)
	}

	return &KeyShares{
		Threshold:   keySplit.Threshold,
		BoxedShares: boxedShares,
		SLOKIDs:     nil,
		KeyShares:   keyShares,
	}, nil
}