| 1 | // vtest build: present_openssl? && !(openbsd && gcc) && !(sanitize-memory-clang || docker-ubuntu-musl) |
| 2 | module ecdsa |
| 3 | |
| 4 | import encoding.hex |
| 5 | import crypto.pem |
| 6 | import crypto.sha1 |
| 7 | import crypto.sha512 |
| 8 | |
| 9 | // This material wss generated with https://emn178.github.io/online-tools/ecdsa/key-generator |
| 10 | // with curve SECG secp384r1 aka NIST P-384 |
| 11 | const privatekey_sample = '-----BEGIN PRIVATE KEY----- |
| 12 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAwzj2iiJZaxgk/C6mp |
| 13 | oVskdr6j7akl4bPB8JRnT1J5XNbLPK/iNd/BW+xUJEj/pxWhZANiAAT4/euEWRPV |
| 14 | 9cdhtjcKlwF2HrFMLvgxAXFx+01UPfMQ9XOj/85qUhVq1jXraSyDy5FYF28UW4dn |
| 15 | 04xVeRuPBbCFxc/uqYj2s5ItHcAZSV3L5sGlXadPfTqoIjCBQAx44k8= |
| 16 | -----END PRIVATE KEY-----' |
| 17 | |
| 18 | const public_key_sample = '-----BEGIN PUBLIC KEY----- |
| 19 | MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+P3rhFkT1fXHYbY3CpcBdh6xTC74MQFx |
| 20 | cftNVD3zEPVzo//OalIVatY162ksg8uRWBdvFFuHZ9OMVXkbjwWwhcXP7qmI9rOS |
| 21 | LR3AGUldy+bBpV2nT306qCIwgUAMeOJP |
| 22 | -----END PUBLIC KEY-----' |
| 23 | |
| 24 | // Message tobe signed and verified |
| 25 | const message_tobe_signed = 'Example of ECDSA with P-384'.bytes() |
| 26 | // Message signature created with SHA384 digest with associated above key |
| 27 | const expected_signature = hex.decode('3066023100b08f6ec77bb319fdb7bce55a2714d7e79cc645d834ee539d8903cfcc88c6fa90df1558856cb840b2dd82e82cd89d7046023100d9d482ca8a6545a3b081fbdd4bb9643a2b4eda4e21fd624833216596032471faae646891f8d2f0bbb86b796c36d3c390')! |
| 28 | |
| 29 | fn test_load_pubkey_from_der_serialized_bytes() ! { |
| 30 | block, _ := pem.decode(public_key_sample) or { panic(err) } |
| 31 | pbkey := pubkey_from_bytes(block.data)! |
| 32 | |
| 33 | // .with_no_hash currently changed to have same behaviour with .with_recommended_hash |
| 34 | status_without_hashed := pbkey.verify(message_tobe_signed, expected_signature, |
| 35 | hash_config: .with_no_hash |
| 36 | )! |
| 37 | assert status_without_hashed == true |
| 38 | |
| 39 | // expected signature was comes from hashed message with sha384 |
| 40 | status_with_hashed := pbkey.verify(message_tobe_signed, expected_signature)! |
| 41 | assert status_with_hashed == true |
| 42 | pbkey.free() |
| 43 | } |
| 44 | |
| 45 | fn test_for_pubkey_bytes() ! { |
| 46 | // material generated with online ecdsa generator https://emn178.github.io/online-tools/ecdsa/key-generator/ |
| 47 | pv := '62e998bea8a15f52ff0b76cf3fe281cfcd8042ce4479b6e652ca7b5a36f6fb40' |
| 48 | pb := '0421af184ac64c8a13e66c65d4f1ad31677edeaa97af791aef73b66ea26d1623a411f67b6c4d842ba22fa39d1216bd64acef00a1b924ac11a10af679ac3a7eb2fd' |
| 49 | pvkey := new_key_from_seed(hex.decode(pv)!)! |
| 50 | |
| 51 | assert pvkey.bytes()!.hex() == pv |
| 52 | pbkey := pvkey.public_key()! |
| 53 | assert pbkey.bytes()!.hex() == pb |
| 54 | pbkey.free() |
| 55 | pvkey.free() |
| 56 | } |
| 57 | |
| 58 | // above pem-formatted private key read with |
| 59 | // `$openssl ec -in vlib/crypto/ecdsa/example.pem -text -param_out -check` |
| 60 | // produces following result: |
| 61 | // ```codeblock |
| 62 | // read EC key |
| 63 | // Private-Key: (384 bit) |
| 64 | // priv: |
| 65 | // 30:ce:3d:a2:88:96:5a:c6:09:3f:0b:a9:a9:a1:5b: |
| 66 | // 24:76:be:a3:ed:a9:25:e1:b3:c1:f0:94:67:4f:52: |
| 67 | // 79:5c:d6:cb:3c:af:e2:35:df:c1:5b:ec:54:24:48: |
| 68 | // ff:a7:15 |
| 69 | // pub: |
| 70 | // 04:f8:fd:eb:84:59:13:d5:f5:c7:61:b6:37:0a:97: |
| 71 | // 01:76:1e:b1:4c:2e:f8:31:01:71:71:fb:4d:54:3d: |
| 72 | // f3:10:f5:73:a3:ff:ce:6a:52:15:6a:d6:35:eb:69: |
| 73 | // 2c:83:cb:91:58:17:6f:14:5b:87:67:d3:8c:55:79: |
| 74 | // 1b:8f:05:b0:85:c5:cf:ee:a9:88:f6:b3:92:2d:1d: |
| 75 | // c0:19:49:5d:cb:e6:c1:a5:5d:a7:4f:7d:3a:a8:22: |
| 76 | // 30:81:40:0c:78:e2:4f |
| 77 | // ASN1 OID: secp384r1 |
| 78 | // NIST CURVE: P-384 |
| 79 | // EC Key valid. |
| 80 | // writing EC key |
| 81 | // -----BEGIN EC PRIVATE KEY----- |
| 82 | // MIGkAgEBBDAwzj2iiJZaxgk/C6mpoVskdr6j7akl4bPB8JRnT1J5XNbLPK/iNd/B |
| 83 | // W+xUJEj/pxWgBwYFK4EEACKhZANiAAT4/euEWRPV9cdhtjcKlwF2HrFMLvgxAXFx |
| 84 | // +01UPfMQ9XOj/85qUhVq1jXraSyDy5FYF28UW4dn04xVeRuPBbCFxc/uqYj2s5It |
| 85 | // HcAZSV3L5sGlXadPfTqoIjCBQAx44k8= |
| 86 | // -----END EC PRIVATE KEY----- |
| 87 | // ``` |
| 88 | fn test_load_privkey_from_string_sign_and_verify() ! { |
| 89 | pvkey := privkey_from_string(privatekey_sample)! |
| 90 | expected_pvkey_bytes := '30ce3da288965ac6093f0ba9a9a15b2476bea3eda925e1b3c1f094674f52795cd6cb3cafe235dfc15bec542448ffa715' |
| 91 | assert pvkey.bytes()!.hex() == expected_pvkey_bytes |
| 92 | |
| 93 | // public key part |
| 94 | pbkey := pvkey.public_key()! |
| 95 | pbkey_bytes := pbkey.bytes()! |
| 96 | expected_pubkey_bytes := '04f8fdeb845913d5f5c761b6370a9701761eb14c2ef831017171fb4d543df310f573a3ffce6a52156ad635eb692c83cb9158176f145b8767d38c55791b8f05b085c5cfeea988f6b3922d1dc019495dcbe6c1a55da74f7d3aa8223081400c78e24f' |
| 97 | assert pbkey_bytes.hex() == expected_pubkey_bytes |
| 98 | |
| 99 | // lets sign the message with default hash, ie, sha384 |
| 100 | signature := pvkey.sign(message_tobe_signed)! |
| 101 | |
| 102 | verified := pbkey.verify(message_tobe_signed, signature)! |
| 103 | assert verified == true |
| 104 | pvkey.free() |
| 105 | pbkey.free() |
| 106 | } |
| 107 | |
| 108 | fn test_load_pubkey_from_string_and_used_for_verifying() ! { |
| 109 | pbkey := pubkey_from_string(public_key_sample)! |
| 110 | pbkey_bytes := pbkey.bytes()! |
| 111 | expected_pubkey_bytes := '04f8fdeb845913d5f5c761b6370a9701761eb14c2ef831017171fb4d543df310f573a3ffce6a52156ad635eb692c83cb9158176f145b8767d38c55791b8f05b085c5cfeea988f6b3922d1dc019495dcbe6c1a55da74f7d3aa8223081400c78e24f' |
| 112 | assert pbkey_bytes.hex() == expected_pubkey_bytes |
| 113 | |
| 114 | // expected signature was comes from hashed message with sha384 |
| 115 | status_with_hashed := pbkey.verify(message_tobe_signed, expected_signature)! |
| 116 | assert status_with_hashed == true |
| 117 | pbkey.free() |
| 118 | } |
| 119 | |
| 120 | // test for loading privat key from unsupported curve should fail. |
| 121 | fn test_load_privkey_from_string_with_unsupported_curve() ! { |
| 122 | // generated with openssl ecparam -name secp192k1 -genkey -noout -out key.pem |
| 123 | key := '-----BEGIN EC PRIVATE KEY----- |
| 124 | MFwCAQEEGDHV+WhJL2UjUhgMLh52k0RJjRebtu4HvqAHBgUrgQQAH6E0AzIABFyF |
| 125 | UHhnmmVRraSwrVkPdYIeXhH/Ob4+8OLcwrQBMv4RXsD1GVFsgkvEYDTEb/vnMA== |
| 126 | -----END EC PRIVATE KEY-----' |
| 127 | _ := privkey_from_string(key) or { |
| 128 | assert err == error('Unsupported group') |
| 129 | return |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | fn test_key_signing_verifying_with_custom_hash() ! { |
| 134 | // privatekey_sample was P-384 key |
| 135 | pvkey := privkey_from_string(privatekey_sample)! |
| 136 | // public key part |
| 137 | pbkey := pvkey.public_key()! |
| 138 | pbk := pubkey_from_string(public_key_sample)! |
| 139 | |
| 140 | // lets sign the message with default hash, ie, sha384 |
| 141 | signature := pvkey.sign(message_tobe_signed)! |
| 142 | verified := pbkey.verify(message_tobe_signed, signature)! |
| 143 | assert verified == true |
| 144 | |
| 145 | // Use the bigger custom hash |
| 146 | opt0 := SignerOpts{ |
| 147 | hash_config: .with_custom_hash |
| 148 | allow_custom_hash: true |
| 149 | custom_hash: sha512.new() |
| 150 | } |
| 151 | // online-generated signature with sha512 digest with the same params from https://emn178.github.io/online-tools/ecdsa/sign/ |
| 152 | online_sign0 := |
| 153 | hex.decode('3066023100b54b479b64961481074c4200a9dec83fb8a42bb7db53cf97f1da131504a058ead85d0a9e4e32be14098bc9b4d1a5a8dd023100f9c7de178a286329103f684d1eab1ccfe359c65a41a1459d7f535b703c57048f25931b1670ab4ec7a812d94c69063522')! |
| 154 | // library generated signature |
| 155 | sign0 := pvkey.sign(message_tobe_signed, opt0)! |
| 156 | v00 := pbkey.verify(message_tobe_signed, sign0, opt0)! |
| 157 | // this own signature should assert into true |
| 158 | assert v00 == true |
| 159 | |
| 160 | // verify online-generated signature |
| 161 | v01 := pbkey.verify(message_tobe_signed, online_sign0, opt0)! |
| 162 | assert v01 == true |
| 163 | |
| 164 | // with public_key_sample key |
| 165 | v02 := pbk.verify(message_tobe_signed, sign0, opt0)! |
| 166 | assert v02 == true |
| 167 | v03 := pbk.verify(message_tobe_signed, online_sign0, opt0)! |
| 168 | assert v03 == true |
| 169 | |
| 170 | // Use smaller custom hash |
| 171 | opt1 := SignerOpts{ |
| 172 | hash_config: .with_custom_hash |
| 173 | allow_custom_hash: true |
| 174 | allow_smaller_size: true |
| 175 | custom_hash: sha1.new() |
| 176 | } |
| 177 | // online-generated signature with SHA1 digest |
| 178 | online_sign1 := |
| 179 | hex.decode('306602310084299d8a70bf512c25cd2b79ae36509572f2bd6f198baeee074683578a70b4af8008e1cf451a2df1a887cf43daff4eea023100dceb267fe5037025c2af9f37911e05a36cbe666dd90fd6904020b5db056e86f25f9439a0ccb443d113b174cab6e2ad61')! |
| 180 | // library generated signature |
| 181 | sign1 := pvkey.sign(message_tobe_signed, opt1)! |
| 182 | verified1 := pbkey.verify(message_tobe_signed, sign1, opt1)! |
| 183 | // this own signature should assert into true |
| 184 | assert verified1 == true |
| 185 | // verify online-generated signature |
| 186 | verified11 := pbkey.verify(message_tobe_signed, online_sign1, opt1)! |
| 187 | assert verified11 == true |
| 188 | |
| 189 | // verify with public_key_sample key |
| 190 | v11 := pbk.verify(message_tobe_signed, sign1, opt1)! |
| 191 | assert v11 == true |
| 192 | v12 := pbk.verify(message_tobe_signed, online_sign1, opt1)! |
| 193 | assert v12 == true |
| 194 | |
| 195 | pvkey.free() |
| 196 | pbkey.free() |
| 197 | pbk.free() |
| 198 | } |
| 199 | |