xref: /aosp_15_r20/external/open-dice/third_party/cose-c/cose_deps.cc (revision 60b67249c2e226f42f35cc6cfe66c6048e0bae6b)
1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include <stdint.h>
16 #include <string.h>
17 
18 #include <optional>
19 
20 #include "cose/cose.h"
21 #include "cose/cose_configure.h"
22 #include "cose_int.h"
23 #include "openssl/bn.h"
24 #include "openssl/curve25519.h"
25 #include "openssl/ec.h"
26 #include "openssl/ec_key.h"
27 #include "openssl/ecdsa.h"
28 #include "openssl/evp.h"
29 #include "openssl/is_boringssl.h"
30 #include "openssl/sha.h"
31 
32 namespace {
33 
34 // Checks the type and ops have the expected values.
CheckCoseKeyTypeAndOps(const cn_cbor * key,uint64_t expected_type)35 bool CheckCoseKeyTypeAndOps(const cn_cbor *key, uint64_t expected_type) {
36   const int64_t kCoseKeyOpsLabel = 4;
37   const uint64_t kCoseKeyOpsVerify = 2;
38 
39   cn_cbor *type = cn_cbor_mapget_int(key, COSE_Key_Type);
40   if (!type) {
41     return false;
42   }
43   if (type->type != CN_CBOR_UINT || type->v.uint != expected_type) {
44     return false;
45   }
46 
47   cn_cbor *ops = cn_cbor_mapget_int(key, kCoseKeyOpsLabel);
48   if (ops) {
49     if (ops->type != CN_CBOR_ARRAY || ops->length == 0) {
50       return false;
51     }
52     bool found_verify = false;
53     for (size_t i = 0; i < ops->length; ++i) {
54       cn_cbor *item = cn_cbor_index(ops, i);
55       if (!item || item->type != CN_CBOR_UINT) {
56         return false;
57       }
58       if (item->v.uint == kCoseKeyOpsVerify) {
59         found_verify = true;
60       }
61     }
62     if (!found_verify) {
63       return false;
64     }
65   }
66   return true;
67 }
68 
69 // Checks that the optional algorithm field is the expected value.
CheckCoseKeyAlg(const cn_cbor * key,int64_t expected_alg)70 bool CheckCoseKeyAlg(const cn_cbor *key, int64_t expected_alg) {
71   const int64_t kCoseKeyAlgLabel = 3;
72 
73   cn_cbor *alg = cn_cbor_mapget_int(key, kCoseKeyAlgLabel);
74   if (alg) {
75     if (alg->type != CN_CBOR_INT || alg->v.sint != expected_alg) {
76       return false;
77     }
78   }
79   return true;
80 }
81 
82 // Gets the public key from a well-formed EC2 COSE_Key.
GetEcKey(cn_cbor * key,int nid,size_t coord_size)83 std::optional<bssl::UniquePtr<EC_KEY>> GetEcKey(cn_cbor *key, int nid,
84                                                 size_t coord_size) {
85   cn_cbor *raw_x = cn_cbor_mapget_int(key, COSE_Key_EC2_X);
86   if (!raw_x || raw_x->type != CN_CBOR_BYTES || raw_x->length != coord_size) {
87     return std::nullopt;
88   }
89 
90   cn_cbor *raw_y = cn_cbor_mapget_int(key, COSE_Key_EC2_Y);
91   if (!raw_y || raw_y->type != CN_CBOR_BYTES || raw_y->length != coord_size) {
92     return std::nullopt;
93   }
94 
95   bssl::UniquePtr<BIGNUM> x(BN_new());
96   bssl::UniquePtr<BIGNUM> y(BN_new());
97   bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new_by_curve_name(nid));
98   if (!x || !y || !eckey) {
99     return std::nullopt;
100   }
101 
102   BN_bin2bn(raw_x->v.bytes, coord_size, x.get());
103   BN_bin2bn(raw_y->v.bytes, coord_size, y.get());
104   if (0 ==
105       EC_KEY_set_public_key_affine_coordinates(eckey.get(), x.get(), y.get())) {
106     return std::nullopt;
107   }
108 
109   return eckey;
110 }
111 
112 }  // namespace
113 
114 // A simple implementation of 'EdDSA_Verify' using boringssl. This function is
115 // required by 'COSE_Sign1_validate'.
EdDSA_Verify(COSE * cose_signer,int signature_index,COSE_KEY * cose_key,const byte * message,size_t message_size,cose_errback *)116 bool EdDSA_Verify(COSE *cose_signer, int signature_index, COSE_KEY *cose_key,
117                   const byte *message, size_t message_size, cose_errback *) {
118   const int64_t kCoseAlgEdDSA = -8;
119 
120   cn_cbor *signature = _COSE_arrayget_int(cose_signer, signature_index);
121   cn_cbor *key = cose_key->m_cborKey;
122   if (!signature || !key) {
123     return false;
124   }
125   if (signature->type != CN_CBOR_BYTES || signature->length != 64) {
126     return false;
127   }
128   if (!CheckCoseKeyTypeAndOps(key, COSE_Key_Type_OKP)) {
129     return false;
130   }
131   cn_cbor *curve = cn_cbor_mapget_int(key, COSE_Key_OPK_Curve);
132   cn_cbor *x = cn_cbor_mapget_int(key, COSE_Key_OPK_X);
133   if (!curve || !x) {
134     return false;
135   }
136   if (curve->type != CN_CBOR_UINT || curve->v.uint != COSE_Curve_Ed25519) {
137     return false;
138   }
139   if (x->type != CN_CBOR_BYTES || x->length != 32) {
140     return false;
141   }
142   if (!CheckCoseKeyAlg(key, kCoseAlgEdDSA)) {
143     return false;
144   }
145   if (1 !=
146       ED25519_verify(message, message_size, signature->v.bytes, x->v.bytes)) {
147     return false;
148   }
149   return true;
150 }
151 
152 // A stub for 'EdDSA_Sign'. This is unused, but helps make linkers happy.
EdDSA_Sign(COSE *,int,COSE_KEY *,const byte *,size_t,cose_errback *)153 bool EdDSA_Sign(COSE * /*cose_signer*/, int /*signature_index*/,
154                 COSE_KEY * /*cose_key*/, const byte * /*message*/,
155                 size_t /*message_size*/, cose_errback *) {
156   return false;
157 }
158 
159 // A simple implementation of 'ECDSA_Verify' using boringssl. This function is
160 // required by 'COSE_Sign1_validate'.
ECDSA_Verify(COSE * cose_signer,int signature_index,COSE_KEY * cose_key,int cbitsDigest,const byte * message,size_t message_size,cose_errback *)161 bool ECDSA_Verify(COSE *cose_signer, int signature_index, COSE_KEY *cose_key,
162                   int cbitsDigest, const byte *message, size_t message_size,
163                   cose_errback *) {
164   const int64_t kCoseAlgEs256 = -7;
165   const int64_t kCoseAlgEs384 = -35;
166 
167   (void)cbitsDigest;
168   cn_cbor *signature = _COSE_arrayget_int(cose_signer, signature_index);
169   cn_cbor *key = cose_key->m_cborKey;
170   if (!signature || !key) {
171     return false;
172   }
173 
174   if (!CheckCoseKeyTypeAndOps(key, COSE_Key_Type_EC2)) {
175     return false;
176   }
177 
178   cn_cbor *curve = cn_cbor_mapget_int(key, COSE_Key_OPK_Curve);
179   if (!curve || curve->type != CN_CBOR_UINT) {
180     return false;
181   }
182 
183   size_t coord_size;
184   int nid;
185   const EVP_MD *md_type;
186   if (curve->v.uint == COSE_Curve_P256) {
187     if (!CheckCoseKeyAlg(key, kCoseAlgEs256)) {
188       return false;
189     }
190     coord_size = 32;
191     nid = NID_X9_62_prime256v1;
192     md_type = EVP_sha256();
193   } else if (curve->v.uint == COSE_Curve_P384) {
194     if (!CheckCoseKeyAlg(key, kCoseAlgEs384)) {
195       return false;
196     }
197     coord_size = 48;
198     nid = NID_secp384r1;
199     md_type = EVP_sha384();
200   } else {
201     return false;
202   }
203 
204   uint8_t md[EVP_MAX_MD_SIZE];
205   unsigned int md_size;
206   if (1 != EVP_Digest(message, message_size, md, &md_size, md_type, nullptr)) {
207     return false;
208   }
209 
210   std::optional<bssl::UniquePtr<EC_KEY>> eckey = GetEcKey(key, nid, coord_size);
211   if (!eckey) {
212     return false;
213   }
214 
215   if (signature->type != CN_CBOR_BYTES ||
216       signature->length != (coord_size * 2)) {
217     return false;
218   }
219 
220   bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
221   BN_bin2bn(&signature->v.bytes[0], coord_size, sig->r);
222   BN_bin2bn(&signature->v.bytes[coord_size], coord_size, sig->s);
223   if (1 != ECDSA_do_verify(md, md_size, sig.get(), eckey->get())) {
224     return false;
225   }
226 
227   return true;
228 }
229 
230 // A stub for 'ECDSA_Sign'. This is unused, but helps make linkers happy.
ECDSA_Sign(COSE *,int,COSE_KEY *,const byte *,size_t,cose_errback *)231 bool ECDSA_Sign(COSE * /*cose_signer*/, int /*signature_index*/,
232                 COSE_KEY * /*cose_key*/, const byte * /*message*/,
233                 size_t /*message_size*/, cose_errback *) {
234   return false;
235 }
236