-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
x/crypto/openpgp/armor: armored output differs from gpg or sks armored output #7241
Comments
Greetings! Please Checkout the output of hockeypuck(Developed in golang) and SKS for same key. e.g. output of http://hockeypuck.gazzang.net/pks/lookup?op=get&search=0x4f283d2f192189397419c3a61c2402a6529472b4 and http://ha.pool.sks-keyservers.net/pks/lookup?op=get&search=0x1C2402A6529472B4 differs though they are the same key. I want to sign a public key from ascii armor with a private key in go language using following code.The armor produced by following code gets parsed by gpg but the signature,when checked is reported as bad signature.Is it also a bug or I am doing something wrong,please tell me.Thank you. import ( "bytes" "code.google.com/p/go.crypto/openpgp" "code.google.com/p/go.crypto/openpgp/armor" "code.google.com/p/go.crypto/openpgp/packet" ) // This function takes asciiarmored private key which will sign the public key //Public key is also ascii armored,pripwd is password of private key in string //This function will return ascii armored signed public key i.e. (pubkey+sign by prikey) func SignPubKeyPKS(asciiPub string, asciiPri string, pripwd string) (asciiSignedKey string) { //get Private key from armor _, priEnt := getPri(asciiPri, pripwd)//pripwd is the password todecrypt the private key _, pubEnt := getPub(asciiPub) usrIdstring := "" for _, uIds := range pubEnt.Identities { usrIdstring = uIds.Name } pubEnt.SignIdentity(usrIdstring, &priEnt, nil) //This will generate signature and add it to pubEnt asciiSignedKey = PubEntToAsciiArmor(pubEnt) return } //get packet.PublicKey and openpgp.Entity of Public Key from ascii armor func getPub(asciiPub string) (pubKey packet.PublicKey, retEntity openpgp.Entity) { read1 := bytes.NewReader([]byte(asciiPub)) entityList, _ := openpgp.ReadArmoredKeyRing(read1) for _, pubKeyEntity := range entityList { if pubKeyEntity.PrimaryKey != nil { pubKey = *pubKeyEntity.PrimaryKey retEntity = *pubKeyEntity } } return } //get packet.PrivateKEy and openpgp.Entity of Private Key from ascii armor func getPri(asciiPri string, pripwd string) (priKey packet.PrivateKey, priEnt openpgp.Entity) { read1 := bytes.NewReader([]byte(asciiPri)) entityList, _ := openpgp.ReadArmoredKeyRing(read1) for _, can_pri := range entityList { smPr := can_pri.PrivateKey retEntity := can_pri if smPr == nil { return } priKey = *smPr priKey.Decrypt([]byte(pripwd)) retEntity.PrivateKey.Decrypt([]byte(pripwd)) retEntity.PrivateKey = &priKey priEnt = *retEntity } return } //Create ASscii Armor from openpgp.Entity func PubEntToAsciiArmor(pubEnt openpgp.Entity) (asciiEntity string) { gotWriter := bytes.NewBuffer(nil) wr, _ := armor.Encode(gotWriter, openpgp.PublicKeyType, nil) pubEnt.Serialize(wr) wr.Close() asciiEntity = gotWriter.String() return } |
Here you go sir.Following code has all the errors checked.The code still doesn't give any errors but every time it is giving different output in armor and hash of the armor changes i.e. Each time new 4 character at the end of the armor.e.g. in one run its ===7lfn and in next its ===OFlB. Thanks a lot for such quick replies , Please forgive if I didn't report bug in well formatted manner as this is my first bug report (I am a grad student).Also did you check the outputs of the hockeypuck and sks? It is showing different output for same key as well. // signer package main import ( "bytes" "code.google.com/p/go.crypto/openpgp" "code.google.com/p/go.crypto/openpgp/armor" "code.google.com/p/go.crypto/openpgp/packet" "fmt" ) // This function takes asciiarmored private key which will sign the public key //Public key is also ascii armored,pripwd is password of private key in string //This function will return ascii armored signed public key i.e. (pubkey+sign by prikey) func SignPubKeyPKS(asciiPub string, asciiPri string, pripwd string) (asciiSignedKey string) { //get Private key from armor _, priEnt := getPri(asciiPri, pripwd) //pripwd is the password todecrypt the private key _, pubEnt := getPub(asciiPub) //This will generate signature and add it to pubEnt usrIdstring := "" for _, uIds := range pubEnt.Identities { usrIdstring = uIds.Name } fmt.Println(usrIdstring) errSign := pubEnt.SignIdentity(usrIdstring, &priEnt, nil) if errSign != nil { fmt.Println("Signing Key ", errSign.Error()) return } asciiSignedKey = PubEntToAsciiArmor(pubEnt) return } //get packet.PublicKey and openpgp.Entity of Public Key from ascii armor func getPub(asciiPub string) (pubKey packet.PublicKey, retEntity openpgp.Entity) { read1 := bytes.NewReader([]byte(asciiPub)) entityList, errReadArm := openpgp.ReadArmoredKeyRing(read1) if errReadArm != nil { fmt.Println("Reading Pubkey ", errReadArm.Error()) return } for _, pubKeyEntity := range entityList { if pubKeyEntity.PrimaryKey != nil { pubKey = *pubKeyEntity.PrimaryKey retEntity = *pubKeyEntity } } return } //get packet.PrivateKEy and openpgp.Entity of Private Key from ascii armor func getPri(asciiPri string, pripwd string) (priKey packet.PrivateKey, priEnt openpgp.Entity) { read1 := bytes.NewReader([]byte(asciiPri)) entityList, errReadArm := openpgp.ReadArmoredKeyRing(read1) if errReadArm != nil { fmt.Println("Reading PriKey ", errReadArm.Error()) return } for _, can_pri := range entityList { smPr := can_pri.PrivateKey retEntity := can_pri if smPr == nil { fmt.Println("No Private Key") return } priKey = *smPr errDecr := priKey.Decrypt([]byte(pripwd)) if errDecr != nil { fmt.Println("Decrypting ", errDecr.Error()) return } retEntity.PrivateKey = &priKey priEnt = *retEntity } return } //Create ASscii Armor from openpgp.Entity func PubEntToAsciiArmor(pubEnt openpgp.Entity) (asciiEntity string) { gotWriter := bytes.NewBuffer(nil) wr, errEncode := armor.Encode(gotWriter, openpgp.PublicKeyType, nil) if errEncode != nil { fmt.Println("Encoding Armor ", errEncode.Error()) return } errSerial := pubEnt.Serialize(wr) if errSerial != nil { fmt.Println("Serializing PubKey ", errSerial.Error()) } errClosing := wr.Close() if errClosing != nil { fmt.Println("Closing writer ", errClosing.Error()) } asciiEntity = gotWriter.String() return } |
Following are Signatures objects. one generated by code and another is by gpg --sign-keys command. Code Signed PubEnt Signature &{16 1 5 [4 16 1 8 0 16 5 2 82 241 227 77 9 16 70 75 47 172 246 211 247 238 4 255 0 0 0 22] [234 4] 2014-02-05 12:37:57.352008112 +0530 IST {[159 214 69 22 59 50 52 28 123 52 201 139 207 208 187 92 21 199 239 160 2 229 1 252 14 159 212 43 109 99 12 226 240 178 115 226 121 26 214 43 16 234 76 156 230 125 151 14 56 228 194 28 68 109 224 11 60 201 14 104 203 38 155 227 177 236 124 211 206 7 167 103 47 233 234 177 9 80 191 215 250 83 170 20 100 190 154 159 112 178 166 240 70 171 101 96 57 34 62 109 143 245 88 99 116 156 107 87 134 127 126 24 225 3 230 130 212 218 97 26 120 174 193 23 149 170 12 213 51 106 201 226 57 103 1 82 70 180 114 125 15 120 37 23 238 4 30 189 93 13 72 207 243 61 226 114 77 226 211 174 42 42 128 209 73 105 111 227 193 160 189 13 206 90 225 62 147 189 132 181 137 53 184 126 204 81 105 156 155 176 19 155 179 122 129 189 42 174 43 123 116 150 64 4 75 149 92 243 135 211 176 23 121 215 117 215 111 214 110 253 136 130 131 36 153 159 79 233 40 181 2 73 254 136 171 200 3 23 75 144 210 73 56 40 109 209 74 63 46 58 229 76 217 27 51 135] 2048} {[] 0} {[] 0} {[] 0} {[] 0} [] <nil> <nil> [] [] [] 0xc200070748 <nil> false false false false false [{true 2 false [82 241 227 77]} {true 16 false [70 75 47 172 246 211 247 238]}]} GPG Signed PubEnt Signature &{16 1 3 [4 16 1 2 0 6 5 2 82 241 226 146 4 255 0 0 0 12] [252 35] 2014-02-05 12:34:50 +0530 IST {[159 21 206 239 129 165 253 32 213 116 68 133 109 248 118 127 105 147 112 54 118 178 237 13 0 111 254 97 174 248 98 90 193 207 185 78 69 150 167 71 251 124 84 138 171 147 134 153 91 118 213 101 139 144 24 118 126 185 202 144 110 252 94 58 242 13 135 216 114 239 86 29 73 50 225 60 156 86 225 69 115 179 7 127 113 235 223 205 196 224 136 201 190 119 245 8 112 83 80 127 106 56 228 164 108 1 175 232 108 33 88 159 122 138 61 244 100 177 172 211 46 227 191 131 214 220 244 253 119 157 134 42 67 14 247 115 223 147 128 200 96 4 160 19 134 4 47 208 201 217 149 162 196 138 166 186 80 74 28 223 19 190 182 103 111 230 230 253 241 154 254 244 250 249 220 183 209 55 6 250 36 225 230 152 100 2 248 91 83 164 147 200 205 111 235 77 70 4 255 45 184 108 89 112 55 15 144 179 76 250 229 42 62 70 204 109 229 81 227 46 110 153 95 45 60 79 43 193 76 245 148 62 84 113 50 30 51 44 33 119 34 108 19 224 179 238 23 2 80 58 113 150 209 117 60 180] 2048} {[] 0} {[] 0} {[] 0} {[] 0} [{true 2 false [82 241 226 146]} {false 16 false [70 75 47 172 246 211 247 238]}] <nil> <nil> [] [] [] 0xc200000150 <nil> false false false false false []} I observed following differences. 1. rawSubpackets and outSubPackets are output differently in gpg and go.Content seems to be same but go outputs it in outSubpackets and gpg in rawSubPackets. 2. If we assume that contents are same then we can see that In go there is {true 16 false [70 75 47 172 246 211 247 238]} while in gpg it is {false 16 false [70 75 47 172 246 211 247 238]} That means go includes some type 16 signature while calculating hash while gpg doesnt. I may be wrong in above analysis.What do you say? |
I've also wondered about the difference in the armored representation of a public key between go.crypto and GnuPG. I am interested in getting to the bottom of this one, if only to satisfy my own curiosity. I would expect most key material to parse correctly on both GnuPG and with go.crypto, except for edge cases like unsupported algorithms, invalid content, etc (there are a few really weird ones floating around in the SKS global pool, let me tell you...). I would expect reasonably valid key material to "round-trip" parsing and serializing with identical output. If you find something otherwise, please create and share a test case. > 1. rawSubpackets and outSubPackets are output differently in gpg and go.Content seems to be same but go outputs it in outSubpackets and gpg in rawSubPackets. Signature.outSubpackets is a stateful field used in serializing the packet and calculating the hash. You would expect to see differences in outSubpackets, between a signature that had just been parsed, vs. a signature that had been just been serialized or created by packet.Sign. > 2. If we assume that contents are same then we can see that In go there is > {true 16 false [70 75 47 172 246 211 247 238]} > while in gpg it is > {false 16 false [70 75 47 172 246 211 247 238]} For new content (generating keys, adding signatures, etc.) go.crypto may differ from GnuPG in its interpretation of RFC 4880, in ways that are perfectly valid. Signature v4 subpacket type 16 is "Issuer" (RFC 4880, 5.2.3.1, 5.2.3.5). While it's probably ok in most cases not to include in the hash, I can't see any harm in doing so. It's certainly possible that some APT has developed the capability to generate keys with colliding 8-byte key IDs on demand. |
Thanks a lot and I am extremely sorry for not replying as I was extremely busy with my Exams.The keys are parsed correctly with NO problems. We know that the key is not modified its just armor but the owner doesn't know that. The owner may get alarmed seeing his key modified. I am again back on the project and will also try to get bottom of this when I get time.Thanks again. Checkout the following links for other problems that I faced with openpgp. https://golang.org/issue/7371 http://stackoverflow.com/questions/21494035/cannot-sign-a-valid-gpg-key-using-golangs-openpgp-packet |
Per the accepted #44226 proposal and due to lack of maintenance, the golang.org/x/crypto/openpgp package is now frozen and deprecated. No new changes will be accepted except for security fixes. The package will not be removed. If this is a security issue, please email security@golang.org and we will assess it and provide a fix. If you're looking for alternatives, consider the crypto/ed25519 package for simple signatures, golang.org/x/mod/sumdb/note for inline signatures, or filippo.io/age for encryption. You can read a summary of OpenPGP issues and alternatives here. If you are required to interoperate with OpenPGP systems and need a maintained package, we suggest considering one of multiple community forks of golang.org/x/crypto/openpgp. We don't endorse any specific one. |
by pruthvirajsinh7:
The text was updated successfully, but these errors were encountered: