// 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 }
// 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 }
// 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 }