Skip to content
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

SHA-1 checksum of the Secret-Key packet is not checked #46

Closed
link2xt opened this issue Sep 6, 2019 · 4 comments · Fixed by #260
Closed

SHA-1 checksum of the Secret-Key packet is not checked #46

link2xt opened this issue Sep 6, 2019 · 4 comments · Fixed by #260
Assignees
Labels

Comments

@link2xt
Copy link
Contributor

link2xt commented Sep 6, 2019

Now trying to open a key encrypted with a password fails with an error similar to Incomplete(Size(6017)). It is not clear from this error whether the key file is indeed truncated or if the password provided is wrong.

See #45

@phiros
Copy link

phiros commented Feb 20, 2020

I can only second that. Observed this error recently and was completly confused...

@link2xt
Copy link
Contributor Author

link2xt commented Dec 17, 2023

I have made a minimal test and added it to tests/key_test.rs:

#[test]
fn test_encrypted_key() {
    let p = Path::new("./tests/key-with-password-123.asc");
    let mut file = read_file(p.to_path_buf());

    let mut buf = vec![];
    file.read_to_end(&mut buf).unwrap();

    let input = ::std::str::from_utf8(buf.as_slice()).expect("failed to convert to string");
    let (key, _headers) = SignedSecretKey::from_armor_single(Cursor::new(input)).expect("failed to parse key");
    let (key, _headers) = SignedSecretKey::from_string(input).expect("failed to parse key");
    key.verify().expect("invalid key");
    let unsigned_pubkey = key.public_key();
    let _signed_pubkey = unsigned_pubkey.sign(&key, || "".into()).unwrap();
}

Key with password 123 generated with sq is here:

-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: B5A3 5856 0F60 BFC0 0885  4634 7C91 8F21 C12B 5F5B
Comment: bob@example.org

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
yvKV8lJR3Pdw6go=
=aYbQ
-----END PGP PRIVATE KEY BLOCK-----

Here is what sq packet dump says about the first packet:

Secret-Key Packet, new CTB, 134 bytes
    Version: 4
    Creation time: 2023-12-17 16:19:56 UTC
    Pk algo: EdDSA
    Pk size: 256 bits
    Fingerprint: B5A358560F60BFC0088546347C918F21C12B5F5B
    KeyID: 7C918F21C12B5F5B
  
    Secret Key:
  
        Encrypted
        S2K: Iterated
          Hash: SHA256
          Salt: A913EF7B409815EB
          Hash bytes: 65011712
        Sym. algo: AES-256

gpg --list-packets output:

# off=0 ctb=c5 tag=5 hlen=2 plen=134 new-ctb
:secret key packet:
	version 4, algo 22, created 1702829996, expires 0
	pkey[0]: [80 bits] ed25519 (1.3.6.1.4.1.11591.15.1)
	pkey[1]: [263 bits]
	iter+salt S2K, algo: 9, SHA1 protection, hash: 8, salt: A913EF7B409815EB
	protect count: 65011712 (255)
	protect IV:  11 40 77 5c 21 09 e7 c9 bd c4 97 2f ce 74 41 ed
	skey[2]: [v4 protected]
	keyid: 7C918F21C12B5F5B

It is an AES-256 encrypted packet and there is a SHA1 checksum.

The test fails here:

PlainSecretParams::from_slice(&plaintext, alg, params)

Apparently it tries to parse plaintext "decrypted" with empty passphrase right above without checking any checksums.
RFC 4880 says about the secret-key packet:

   The two-octet checksum that follows the algorithm-specific portion is
   the algebraic sum, mod 65536, of the plaintext of all the algorithm-
   specific octets (including MPI prefix and data).  With V3 keys, the
   checksum is stored in the clear.  With V4 keys, the checksum is
   encrypted like the algorithm-specific data.  This value is used to
   check that the passphrase was correct.  However, this checksum is
   deprecated; an implementation SHOULD NOT use it, but should rather
   use the SHA-1 hash denoted with a usage octet of 254.  The reason for
   this is that there are some attacks that involve undetectably
   modifying the secret key.

So rPGP must check SHA-1 hash before attempting to parse decrypted data.

@link2xt
Copy link
Contributor Author

link2xt commented Dec 17, 2023

This is where SHA-1 checksum is written in case of id 254:

data.extend_from_slice(&self.checksum_sha1()[..]);

But when parsing it is not checked.

@link2xt link2xt changed the title Better error message for keys encrypted with a password SHA-1 checksum of the Secret-Key packet is not checked Dec 17, 2023
@link2xt link2xt added bug and removed enhancement labels Dec 17, 2023
@link2xt link2xt self-assigned this Dec 17, 2023
@link2xt
Copy link
Contributor Author

link2xt commented Dec 17, 2023

Made a fix: #260

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants