getsops / getsops/sops

Encrypted data reveal the length of the clear text data

Open
#815 5 comments 7 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
23.1k
Forks
1.1k
Avg merge
1d 11h
Merged PRs (30d)
9

Description

Hello, everybody,

I post this issue after having contacted Julien Vehent. He suggested to open an issue here so it can be discussed. So I post it here in the hope to contribute to the usefulness and security of this great software.

Problem description

The length of the encrypted form of the data is always the same length as the clear text data. So it is possible to guess the data value from it’s length.

Let us take this yaml file as an example:

strings:
- a
- aB
- aBc
- aBcD
- aBcDe
numbers:
- 1
- 12
- 123
- 1.2
- 1.2300
booleans:
- true
- false
- y
- n
- on
- off

This is converted to something like this by sops:

field:
- ENC[AES256_GCM,data:Qw==,iv:3Db00Y2V3vELD+cxyeAzaI0WdZOOqw7P3SrIphYlkM8=,tag:Rm1hVD4J7Wcyteh2pUXqQw==,type:str]
- ENC[AES256_GCM,data:hlU=,iv:RStn69CRX7uddn7rKHu0YYYNvsSaxPp0bqgsuamdAIM=,tag:BjVgphl+yqd49lkGFvVtAw==,type:str]
- ENC[AES256_GCM,data:UXxu,iv:a4ufQdVF1dwH1m+wZRdd2tYIZaXKRj/QhzyJ1HDu7dg=,tag:xbUz2UxdVOV/GuDKkjTXdA==,type:str]
- ENC[AES256_GCM,data:HA1E1Q==,iv:6Y7pxNWY4jZn5OLfa4Sd/DQzo9ye2ubM1u2e5IQN9I0=,tag:a1WgmqUhJKvqo/qKbWvTiQ==,type:str]
- ENC[AES256_GCM,data:PYfdMqo=,iv:X+Z1DdUV7A+4p8Yj8zFYBhJtNJFLm1mqOmHevMcDxn0=,tag:l97d0/y8b/uBM50DLZ/TzA==,type:str]
numbers:
- ENC[AES256_GCM,data:IQ==,iv:AFshGBvOry/iUaPNm9ZgFrkb03AevzERJhEH8AiCfcQ=,tag:sILDbauS1RqIBCQM6XWArQ==,type:int]
- ENC[AES256_GCM,data:tSI=,iv:vvnAkSw69eQL64bmsZpjrCIh16qI13esc3tLsLGPlj0=,tag:Pf3Y/m3xP81fSVrcMez0eg==,type:int]
- ENC[AES256_GCM,data:rBeB,iv:xgzYmW9i6y61Udt226ooTx/vzcOQFtgIAVyomUNGix0=,tag:h8yHZasQGHa3AY4VG1J0ZA==,type:int]
- ENC[AES256_GCM,data:rNTf,iv:EzN8AQ4T63J0YjV+y2y6ViOe9M2KrERICpbEsTU/1io=,tag:Thftkoq//92XyzS3ZmKBgg==,type:float]
- ENC[AES256_GCM,data:Gvzw2A==,iv:OrdWXplruWs4BG2DduSL4Ld72Teao62WivMWISkG9+E=,tag:6XF2e3HNwyV3YB7AyPw2Rg==,type:float]
booleans:
- ENC[AES256_GCM,data:kU1bxA==,iv:h+B5QbhktBIP2gSoyPlYtFq+/LDlvRP5ndXuL8GN6xY=,tag:PIV5v63hDg4wPnEsFikeAA==,type:bool]
- ENC[AES256_GCM,data:yRRf3Kc=,iv:+kJKCNe2BHFU0zEK4IJoDUk+/VWXgiddyYxhFlqNnZA=,tag:dXw+8mSWKtTJd4F5VMHp/Q==,type:bool]
- ENC[AES256_GCM,data:kU1bxA==,iv:h+B5QbhktBIP2gSoyPlYtFq+/LDlvRP5ndXuL8GN6xY=,tag:PIV5v63hDg4wPnEsFikeAA==,type:bool]
- ENC[AES256_GCM,data:yRRf3Kc=,iv:+kJKCNe2BHFU0zEK4IJoDUk+/VWXgiddyYxhFlqNnZA=,tag:dXw+8mSWKtTJd4F5VMHp/Q==,type:bool]
- ENC[AES256_GCM,data:kU1bxA==,iv:h+B5QbhktBIP2gSoyPlYtFq+/LDlvRP5ndXuL8GN6xY=,tag:PIV5v63hDg4wPnEsFikeAA==,type:bool]
- ENC[AES256_GCM,data:yRRf3Kc=,iv:+kJKCNe2BHFU0zEK4IJoDUk+/VWXgiddyYxhFlqNnZA=,tag:dXw+8mSWKtTJd4F5VMHp/Q==,type:bool]

When we look at the encrypted data (the values after the data: tag) we see the following:

Type str:

Clear text Clear text length Encrypted bytes Encrypted length
a 1 0x43 1
aB 2 0x8655 2
aBc 3 0x517c6e 3
aBcD 4 0x1c0d44d5 4
aBcDe 5 0x3d87dd32aa 5

Type int:

Clear text Clear text length Encrypted bytes Encrypted length
1 1 0x21 1
12 2 0xb522 2
123 3 0xac1781 3

Type float:

Clear text Clear text length Encrypted bytes Encrypted length
1.2 3 0xacd4df 3
1.2300 6 (true length is 4) 0x1afcf0d8 4

Type boolean:

Clear text Clear text length Encrypted bytes Encrypted length
true 4 0x914d5bc4 4
false 5 0xc9145fdca7 5

There are two observations:

  1. The data is "canonicalized" before being encrypted
    1. This is most easily seen for the boolean values where all variants are mapped to true and false
    2. All the other types are processed, as well, as trailing zeroes (for int and float) or blanks (for all types) are removed before the data is encrypted
  2. The length of the encrypted bytes is always the same as the length of the "canonicalized" clear text

What is the implication?

An adversary does not know the content of a str, int or float but in the current implementation of sops he knows the clear text length from the encrypted lengths and this is a valuable information. If the clear text is short, like e.g. up to 5 characters, he can simply guess or brute force the clear text value. Having the length of the clear text limits the number of tries he has perform. If the length of the clear text data is long enough (whatever "long enough" means) this does not reduce the security as there are just to many possibilities to try.

Numbers tend to be short and so can easily be guessed by just knowing their length. So this is a problem for numbers and short strings.

A special case are boolean values. They can only have 2 different values, so it is trivial to "guess" the value. The adversary will have the correct value after at most two tries. All he needs to know is the type.

What to do about it?

I propose to conceal the true length of the data by prepending and appending random data to them before they are encrypted. There should be 0-m data prepended and 0-n data appended. The lengths of these "blindings" should be stored with the data.

Here is an example of how that could be done:

  1. Clear text: data
  2. Clear text as bytes: 0x64617461
  3. Prepend e.g. 3 random bytes and append e.g. 4 random bytes: 0x168ffa64617461a29fbc5d
  4. Append the lengths of the prepended and appended bytes: 0x168ffa64617461a29fbc5d0304

So the data that is finally encrypted is 0x168ffa64617461a29fbc5d0304, instead of 0x64617461.

On decryption the blinding lengths are read from the end of the decrypted bytes. With this information the blinding can be removed and the data restored.

This is just an example. There may be better ways to do this.

There should just be a method to conceal the length of the clear text.

Of course one needs some means to differentiate between values that use blinding and others that do not.

Regarding the boolean values I think it does not make sense to "blind" them like in the example above as there are only two possible clear text values. The only means to protect them would be to conceal the type and treat them in a special way.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The issue names no implementation files, entry points, or tests. Start by locating the current encryption and decryption format, then evaluate the proposed length-blinding approach and its compatibility implications; done requires an agreed design and implementation plan rather than a single defined edit.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
cryptography, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.