-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle wrong passwords #38
Comments
When reading another password protected 7z file, I get Test case to replicate it (added to the OP): {
name: "password protected, wrong password, another error",
archiveBase64: "N3q8ryccAATmiIjWEAAAAAAAAABqAAAAAAAAAPj97y5WCxqWKbLj7vDaXAe/cLh2AQQGAAEJEAAHCwEAAiQG8QcBElMPRIjTr6dLKHHK30sEtwLboyEhAQABAAwMCAAICgH8UFzbAAAFARkBABETAHQAZQB4AHQALgB0AHgAdAAAABkAFAoBADbNXd0pr9gBFQYBACCApIEAAA==",
password: "not 'password'",
} |
For context, in all cases, I create the 7z file with: $ echo testing > text.txt
$ 7z a -ppassword password.7z text.txt
$ cat password.7z | base64 | pbcopy |
So the way the encryption works on 7-zip archives is that it performs the usual compression (with LZMA, or any other algorithm for that matter) in one layer, producing a compressed stream of bytes, then it's wrapped by the layer that does the encryption, producing the encrypted (and compressed) stream of bytes. The encryption has no idea what the underlying compression format was. So conversely when it comes to the decryption/decompression, the decryption layer doesn't know what the correct password is as it still produces a stream of bytes even with the wrong password, it's only when the decompression layer tries to interpret it that it won't be a valid LZMA stream and produces the error but it can't easily distinguish between a corrupt unencrypted archive and a valid encrypted archive with the wrong password set. As you can see from your test cases, setting the password to a variety of different bad values results in different errors and that's purely down to the decrypted stream being "more or less wrong" when interpreted by the underlying LZMA library. Checking with the |
Good point. That also matches my experience. I have another extractor implementation using
I can do the same thing for this package, but there seems to be a lot of noise (different types of errors) that could be misinterpreted as password being wrong. If there were a way to know that an archive can't be decrypted, I would be happy with that too. |
Please try this branch and see if that helps: https://github.com/bodgit/sevenzip/tree/wrong-password It introduces a new You can assert that the returned error includes this type using Like with the |
Thanks! I couldn't get it to work. I'm still getting the // go.mod
github.com/bodgit/sevenzip v1.3.1-0.20220814220638-421188bed950 func TestSevenzipPasswordError(t *testing.T) {
passwordDot7z := `N3q8ryccAATLw8/pEAAAAAAAAABqAAAAAAAAAL5k3wUq4RlmRrx/qDeBDV2MzerKAQQGAAEJEAAHCwEAAiQG8QcBElMPbBOTnKWOQVr5GQ8zDSlIuSEhAQABAAwMCAAICgH8UFzbAAAFARkBABETAHQAZQB4AHQALgB0AHgAdAAAABkAFAoBAMcsnkBbsNgBFQYBACCApIEAAA==`
tests := []struct {
name string
archiveBase64 string
wantErr bool
password string
}{
{
name: "correct pass",
archiveBase64: passwordDot7z,
password: "password",
wantErr: false,
},
{
name: "wrong pass",
archiveBase64: passwordDot7z,
password: "notpassword",
wantErr: true, // fails with lzma: unexpected chunk type
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, size := readBase64(tt.archiveBase64)
ar, err := sevenzip.NewReaderWithPassword(f, size, tt.password)
assert.NoError(t, err) // should have failed here!!!!!
for _, it := range ar.File {
af, err := it.Open()
assert.NoError(t, err)
_, err = io.Copy(ioutil.Discard, af)
if tt.wantErr {
var pErr *sevenzip.PasswordError
assert.ErrorAs(t, err, &pErr)
} else {
assert.NoError(t, err)
}
}
})
}
}
func readBase64(encoded string) (io.ReaderAt, int64) {
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
panic(err)
}
return bytesAt{decoded}, int64(len(decoded))
}
type bytesAt struct {
b []byte
}
func (b bytesAt) ReadAt(p []byte, off int64) (n int, err error) {
if off >= int64(len(b.b)) {
return 0, io.EOF
}
n = copy(p, b.b[off:])
return
} this outputs:
|
I tried it again with fixtures Line 135 in 421188b
The files I'm using don't fail while opening the archive, but when reading a file off of them. For reference, version of
|
So I think you've managed to create a 7-zip archive that doesn't encrypt the headers, (i.e. what files are in the archive and how big they are, etc.), but the files themselves are encrypted. This means you'll never know if the password is wrong until you try and extract a file, there's no way around that unfortunately. If you create your archive with |
Yeah, it found it bizarre that I could read file names. I understand the difference now. I was only adding I guess I can chalk up any error starting with |
Not necessarily, it depends what compression algorithm is used. LZMA is the default algorithm so that's the most common but you can use other algorithms such as bzip2, deflate, etc. and then there are other ones that are really filters such as delta and BCJ2 which aim to rearrange the file contents in a way that makes them compress better, they can also be stacked/wrapped. In Golang terminology, think of it as a chain of one or more I've added support for most of the common algorithms where there's either support in the standard library or an external library and at the moment my code just bubbles up whatever error the underlying library emits in the case of an error. It might be an idea to wrap these in a generic |
Can you merge master into this branch? I'm interested in having better password-error identification. My app supports 'trying' multiple passwords, and right now I just try the next password on any error. If the archive is not encrypted, it'd be nice to just not try anymore passwords. |
Is there any possibility to merge the wrong-password branch to master? It seems that many people waiting for that feature, thanks a lot! |
I'm using
github.com/bodgit/sevenzip v1.3.0
.When I try to extract a password protected 7z archive with a wrong password, I get
newRangeDecoder: first byte not zero
error.Here's a test to replicate it (needs
stretchr/testify
):Here the second case
password protected, wrong password
fails with an error. It seems that the error comes deep withinio
package when I try to read the archived fileio.Copy(ioutil.Discard, f)
.It would be great if it detected wrong passwords when I call
NewReaderWithPassword
and returned anErrWrongPassword
or something. This would let me catch it witherrors.Is(err, ErrWrongPassword)
and try another password.I can submit a fix if you can give me some pointers on where to look.
The text was updated successfully, but these errors were encountered: