1*84e33947SAndroid Build Coastguard Worker /*
2*84e33947SAndroid Build Coastguard Worker * Copyright (C) 2022 The Android Open Source Project
3*84e33947SAndroid Build Coastguard Worker *
4*84e33947SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*84e33947SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*84e33947SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*84e33947SAndroid Build Coastguard Worker *
8*84e33947SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*84e33947SAndroid Build Coastguard Worker *
10*84e33947SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*84e33947SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*84e33947SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*84e33947SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*84e33947SAndroid Build Coastguard Worker * limitations under the License.
15*84e33947SAndroid Build Coastguard Worker */
16*84e33947SAndroid Build Coastguard Worker
17*84e33947SAndroid Build Coastguard Worker #include <inttypes.h>
18*84e33947SAndroid Build Coastguard Worker #include <cstdint>
19*84e33947SAndroid Build Coastguard Worker
20*84e33947SAndroid Build Coastguard Worker #include "chre/platform/log.h"
21*84e33947SAndroid Build Coastguard Worker #include "chre/platform/shared/authentication.h"
22*84e33947SAndroid Build Coastguard Worker #include "chre/util/macros.h"
23*84e33947SAndroid Build Coastguard Worker
24*84e33947SAndroid Build Coastguard Worker #include "mbedtls/pk.h"
25*84e33947SAndroid Build Coastguard Worker #include "mbedtls/sha256.h"
26*84e33947SAndroid Build Coastguard Worker
27*84e33947SAndroid Build Coastguard Worker #include "cpufreq_vote.h"
28*84e33947SAndroid Build Coastguard Worker
29*84e33947SAndroid Build Coastguard Worker namespace chre {
30*84e33947SAndroid Build Coastguard Worker namespace {
31*84e33947SAndroid Build Coastguard Worker
32*84e33947SAndroid Build Coastguard Worker // A data structure needed for SCP chip frequency change
33*84e33947SAndroid Build Coastguard Worker DECLARE_OPPDEV_CPLUSPLUS(gChreScpFreqVote);
34*84e33947SAndroid Build Coastguard Worker
35*84e33947SAndroid Build Coastguard Worker // All the size below are in bytes
36*84e33947SAndroid Build Coastguard Worker constexpr uint32_t kEcdsaP256SigSize = 64;
37*84e33947SAndroid Build Coastguard Worker constexpr uint32_t kEcdsaP256PublicKeySize = 64;
38*84e33947SAndroid Build Coastguard Worker constexpr uint32_t kHeaderSize = 0x1000;
39*84e33947SAndroid Build Coastguard Worker constexpr uint32_t kSha256HashSize = 32;
40*84e33947SAndroid Build Coastguard Worker
41*84e33947SAndroid Build Coastguard Worker // ASCII of "CHRE", in BE
42*84e33947SAndroid Build Coastguard Worker constexpr uint32_t kChreMagicNumber = 0x45524843;
43*84e33947SAndroid Build Coastguard Worker
44*84e33947SAndroid Build Coastguard Worker // Production public key
45*84e33947SAndroid Build Coastguard Worker const uint8_t kGooglePublicKey[kEcdsaP256PublicKeySize] = {
46*84e33947SAndroid Build Coastguard Worker 0x97, 0x66, 0x1f, 0xe7, 0x26, 0xc5, 0xc3, 0x9c, 0xe6, 0x71, 0x59,
47*84e33947SAndroid Build Coastguard Worker 0x1f, 0x26, 0x3b, 0x1c, 0x87, 0x50, 0x7f, 0xad, 0x4f, 0xeb, 0x4b,
48*84e33947SAndroid Build Coastguard Worker 0xe5, 0x3b, 0xee, 0x76, 0xff, 0x80, 0x6a, 0x8b, 0x6d, 0xed, 0x58,
49*84e33947SAndroid Build Coastguard Worker 0xd7, 0xed, 0xf3, 0x18, 0x9e, 0x9a, 0xac, 0xcf, 0xfc, 0xd2, 0x7,
50*84e33947SAndroid Build Coastguard Worker 0x35, 0x64, 0x54, 0xcc, 0xbc, 0x8b, 0xe0, 0x6c, 0x77, 0xbe, 0xbb,
51*84e33947SAndroid Build Coastguard Worker 0x1b, 0xdd, 0x18, 0x6d, 0x77, 0xfe, 0xb7, 0x0, 0xd5};
52*84e33947SAndroid Build Coastguard Worker
53*84e33947SAndroid Build Coastguard Worker const uint8_t *const kTrustedPublicKeys[] = {kGooglePublicKey};
54*84e33947SAndroid Build Coastguard Worker
55*84e33947SAndroid Build Coastguard Worker /**
56*84e33947SAndroid Build Coastguard Worker * A data structure encapsulating metadata necessary for nanoapp binary
57*84e33947SAndroid Build Coastguard Worker * signature verification.
58*84e33947SAndroid Build Coastguard Worker *
59*84e33947SAndroid Build Coastguard Worker * Note that the structure field names that start with 'reserved' are currently
60*84e33947SAndroid Build Coastguard Worker * unused.
61*84e33947SAndroid Build Coastguard Worker */
62*84e33947SAndroid Build Coastguard Worker struct HeaderInfo {
63*84e33947SAndroid Build Coastguard Worker /**
64*84e33947SAndroid Build Coastguard Worker * A magic number indicating the start of the header info, ASCII decodes to
65*84e33947SAndroid Build Coastguard Worker * 'CHRE'.
66*84e33947SAndroid Build Coastguard Worker */
67*84e33947SAndroid Build Coastguard Worker uint32_t magic;
68*84e33947SAndroid Build Coastguard Worker
69*84e33947SAndroid Build Coastguard Worker uint32_t headerVersion;
70*84e33947SAndroid Build Coastguard Worker
71*84e33947SAndroid Build Coastguard Worker // TODO(b/260099197): We should have a hardware backed rollback info check.
72*84e33947SAndroid Build Coastguard Worker uint32_t reservedRollbackInfo;
73*84e33947SAndroid Build Coastguard Worker
74*84e33947SAndroid Build Coastguard Worker /** The size in bytes of the actual nanoapp binary. */
75*84e33947SAndroid Build Coastguard Worker uint32_t binaryLength;
76*84e33947SAndroid Build Coastguard Worker
77*84e33947SAndroid Build Coastguard Worker /** The flag indicating the public key size. */
78*84e33947SAndroid Build Coastguard Worker uint64_t flags[2];
79*84e33947SAndroid Build Coastguard Worker
80*84e33947SAndroid Build Coastguard Worker /** The SHA-256 hash of the actual nanoapp binary. */
81*84e33947SAndroid Build Coastguard Worker uint8_t binarySha256[kSha256HashSize];
82*84e33947SAndroid Build Coastguard Worker
83*84e33947SAndroid Build Coastguard Worker uint8_t reservedChipId[32];
84*84e33947SAndroid Build Coastguard Worker
85*84e33947SAndroid Build Coastguard Worker uint8_t reservedAuthConfig[256];
86*84e33947SAndroid Build Coastguard Worker
87*84e33947SAndroid Build Coastguard Worker uint8_t reservedImageConfig[256];
88*84e33947SAndroid Build Coastguard Worker };
89*84e33947SAndroid Build Coastguard Worker
90*84e33947SAndroid Build Coastguard Worker /**
91*84e33947SAndroid Build Coastguard Worker * A header containing information relevant to nanoapp signature authentication
92*84e33947SAndroid Build Coastguard Worker * that is tacked onto every signed nanoapp.
93*84e33947SAndroid Build Coastguard Worker */
94*84e33947SAndroid Build Coastguard Worker struct ImageHeader {
95*84e33947SAndroid Build Coastguard Worker /** The zero-padded signature of the nanoapp binary. */
96*84e33947SAndroid Build Coastguard Worker uint8_t signature[512];
97*84e33947SAndroid Build Coastguard Worker
98*84e33947SAndroid Build Coastguard Worker /** The zero-padded public key for the key pair used to sign the hash, which
99*84e33947SAndroid Build Coastguard Worker * we use to verify whether we trust the signer or not. */
100*84e33947SAndroid Build Coastguard Worker uint8_t publicKey[512];
101*84e33947SAndroid Build Coastguard Worker
102*84e33947SAndroid Build Coastguard Worker /** @see struct HeaderInfo. */
103*84e33947SAndroid Build Coastguard Worker HeaderInfo headerInfo;
104*84e33947SAndroid Build Coastguard Worker };
105*84e33947SAndroid Build Coastguard Worker
106*84e33947SAndroid Build Coastguard Worker class Authenticator {
107*84e33947SAndroid Build Coastguard Worker public:
Authenticator()108*84e33947SAndroid Build Coastguard Worker Authenticator() {
109*84e33947SAndroid Build Coastguard Worker scp_vote_opp(&gChreScpFreqVote, CLK_OPP2);
110*84e33947SAndroid Build Coastguard Worker mbedtls_ecp_group_init(&mGroup);
111*84e33947SAndroid Build Coastguard Worker mbedtls_ecp_point_init(&mQ);
112*84e33947SAndroid Build Coastguard Worker mbedtls_mpi_init(&mR);
113*84e33947SAndroid Build Coastguard Worker mbedtls_mpi_init(&mS);
114*84e33947SAndroid Build Coastguard Worker }
115*84e33947SAndroid Build Coastguard Worker
~Authenticator()116*84e33947SAndroid Build Coastguard Worker ~Authenticator() {
117*84e33947SAndroid Build Coastguard Worker mbedtls_mpi_free(&mS);
118*84e33947SAndroid Build Coastguard Worker mbedtls_mpi_free(&mR);
119*84e33947SAndroid Build Coastguard Worker mbedtls_ecp_point_free(&mQ);
120*84e33947SAndroid Build Coastguard Worker mbedtls_ecp_group_free(&mGroup);
121*84e33947SAndroid Build Coastguard Worker scp_unvote_opp(&gChreScpFreqVote, CLK_OPP2);
122*84e33947SAndroid Build Coastguard Worker }
123*84e33947SAndroid Build Coastguard Worker
loadEcpGroup()124*84e33947SAndroid Build Coastguard Worker bool loadEcpGroup() {
125*84e33947SAndroid Build Coastguard Worker int result = mbedtls_ecp_group_load(&mGroup, MBEDTLS_ECP_DP_SECP256R1);
126*84e33947SAndroid Build Coastguard Worker if (result != 0) {
127*84e33947SAndroid Build Coastguard Worker LOGE("Failed to load ecp group. Error code: %d", result);
128*84e33947SAndroid Build Coastguard Worker return false;
129*84e33947SAndroid Build Coastguard Worker }
130*84e33947SAndroid Build Coastguard Worker return true;
131*84e33947SAndroid Build Coastguard Worker }
132*84e33947SAndroid Build Coastguard Worker
loadPublicKey(const uint8_t * publicKey)133*84e33947SAndroid Build Coastguard Worker bool loadPublicKey(const uint8_t *publicKey) {
134*84e33947SAndroid Build Coastguard Worker // 0x04 prefix is required by mbedtls
135*84e33947SAndroid Build Coastguard Worker constexpr uint8_t kPublicKeyPrefix = 0x04;
136*84e33947SAndroid Build Coastguard Worker uint8_t buffer[kEcdsaP256PublicKeySize + 1] = {kPublicKeyPrefix};
137*84e33947SAndroid Build Coastguard Worker memcpy(buffer + 1, publicKey, kEcdsaP256PublicKeySize);
138*84e33947SAndroid Build Coastguard Worker int result =
139*84e33947SAndroid Build Coastguard Worker mbedtls_ecp_point_read_binary(&mGroup, &mQ, buffer, ARRAY_SIZE(buffer));
140*84e33947SAndroid Build Coastguard Worker if (result != 0) {
141*84e33947SAndroid Build Coastguard Worker LOGE("Failed to load the public key. Error code: %d", result);
142*84e33947SAndroid Build Coastguard Worker return false;
143*84e33947SAndroid Build Coastguard Worker }
144*84e33947SAndroid Build Coastguard Worker return true;
145*84e33947SAndroid Build Coastguard Worker }
146*84e33947SAndroid Build Coastguard Worker
loadSignature(const ImageHeader * header)147*84e33947SAndroid Build Coastguard Worker bool loadSignature(const ImageHeader *header) {
148*84e33947SAndroid Build Coastguard Worker constexpr uint32_t kRSigSize = kEcdsaP256SigSize / 2;
149*84e33947SAndroid Build Coastguard Worker constexpr uint32_t kSSigSize = kEcdsaP256SigSize / 2;
150*84e33947SAndroid Build Coastguard Worker int result = mbedtls_mpi_read_binary(&mR, header->signature, kRSigSize);
151*84e33947SAndroid Build Coastguard Worker if (result != 0) {
152*84e33947SAndroid Build Coastguard Worker LOGE("Failed to read r signature. Error code: %d", result);
153*84e33947SAndroid Build Coastguard Worker return false;
154*84e33947SAndroid Build Coastguard Worker }
155*84e33947SAndroid Build Coastguard Worker result =
156*84e33947SAndroid Build Coastguard Worker mbedtls_mpi_read_binary(&mS, header->signature + kRSigSize, kSSigSize);
157*84e33947SAndroid Build Coastguard Worker if (result != 0) {
158*84e33947SAndroid Build Coastguard Worker LOGE("Failed to read s signature. Error code: %d", result);
159*84e33947SAndroid Build Coastguard Worker return false;
160*84e33947SAndroid Build Coastguard Worker }
161*84e33947SAndroid Build Coastguard Worker return true;
162*84e33947SAndroid Build Coastguard Worker }
163*84e33947SAndroid Build Coastguard Worker
authenticate(const void * binary)164*84e33947SAndroid Build Coastguard Worker bool authenticate(const void *binary) {
165*84e33947SAndroid Build Coastguard Worker constexpr size_t kDataOffset = 0x200;
166*84e33947SAndroid Build Coastguard Worker constexpr size_t kDataSize = kHeaderSize - kDataOffset;
167*84e33947SAndroid Build Coastguard Worker auto data = static_cast<const uint8_t *>(binary) + kDataOffset;
168*84e33947SAndroid Build Coastguard Worker unsigned char digest[kSha256HashSize] = {};
169*84e33947SAndroid Build Coastguard Worker mbedtls_sha256(data, kDataSize, digest, /* is224= */ 0);
170*84e33947SAndroid Build Coastguard Worker int result = mbedtls_ecdsa_verify(&mGroup, digest, ARRAY_SIZE(digest), &mQ,
171*84e33947SAndroid Build Coastguard Worker &mR, &mS);
172*84e33947SAndroid Build Coastguard Worker if (result != 0) {
173*84e33947SAndroid Build Coastguard Worker LOGE("Signature verification failed. Error code: %d", result);
174*84e33947SAndroid Build Coastguard Worker return false;
175*84e33947SAndroid Build Coastguard Worker }
176*84e33947SAndroid Build Coastguard Worker return true;
177*84e33947SAndroid Build Coastguard Worker }
178*84e33947SAndroid Build Coastguard Worker
179*84e33947SAndroid Build Coastguard Worker private:
180*84e33947SAndroid Build Coastguard Worker mbedtls_ecp_group mGroup;
181*84e33947SAndroid Build Coastguard Worker mbedtls_ecp_point mQ;
182*84e33947SAndroid Build Coastguard Worker mbedtls_mpi mR;
183*84e33947SAndroid Build Coastguard Worker mbedtls_mpi mS;
184*84e33947SAndroid Build Coastguard Worker };
185*84e33947SAndroid Build Coastguard Worker
186*84e33947SAndroid Build Coastguard Worker /** Retrieves the public key length based on the flag. */
getPublicKeyLength(const uint64_t * flag)187*84e33947SAndroid Build Coastguard Worker uint32_t getPublicKeyLength(const uint64_t *flag) {
188*84e33947SAndroid Build Coastguard Worker constexpr int kPkSizeMaskPosition = 9;
189*84e33947SAndroid Build Coastguard Worker constexpr uint64_t kPkSizeMask = 0x3;
190*84e33947SAndroid Build Coastguard Worker uint8_t keySizeFlag = ((*flag) >> kPkSizeMaskPosition) & kPkSizeMask;
191*84e33947SAndroid Build Coastguard Worker switch (keySizeFlag) {
192*84e33947SAndroid Build Coastguard Worker case 0:
193*84e33947SAndroid Build Coastguard Worker return 64;
194*84e33947SAndroid Build Coastguard Worker case 1:
195*84e33947SAndroid Build Coastguard Worker return 96;
196*84e33947SAndroid Build Coastguard Worker case 2:
197*84e33947SAndroid Build Coastguard Worker return 132;
198*84e33947SAndroid Build Coastguard Worker default:
199*84e33947SAndroid Build Coastguard Worker LOGE("Unsupported flags in nanoapp header!");
200*84e33947SAndroid Build Coastguard Worker return 0;
201*84e33947SAndroid Build Coastguard Worker }
202*84e33947SAndroid Build Coastguard Worker }
203*84e33947SAndroid Build Coastguard Worker
204*84e33947SAndroid Build Coastguard Worker /** Checks if the hash prvided in the header is derived from the image. */
hasCorrectHash(const void * head,size_t realImageSize,const uint8_t * hashProvided)205*84e33947SAndroid Build Coastguard Worker bool hasCorrectHash(const void *head, size_t realImageSize,
206*84e33947SAndroid Build Coastguard Worker const uint8_t *hashProvided) {
207*84e33947SAndroid Build Coastguard Worker auto image = static_cast<const uint8_t *>(head) + kHeaderSize;
208*84e33947SAndroid Build Coastguard Worker uint8_t hashCalculated[kSha256HashSize] = {};
209*84e33947SAndroid Build Coastguard Worker mbedtls_sha256(image, realImageSize, hashCalculated, /* is224= */ 0);
210*84e33947SAndroid Build Coastguard Worker return memcmp(hashCalculated, hashProvided, kSha256HashSize) == 0;
211*84e33947SAndroid Build Coastguard Worker }
212*84e33947SAndroid Build Coastguard Worker
213*84e33947SAndroid Build Coastguard Worker /** Checks if the public key in the header matches the production public key. */
isValidProductionPublicKey(const uint8_t * publicKey,size_t publicKeyLength)214*84e33947SAndroid Build Coastguard Worker bool isValidProductionPublicKey(const uint8_t *publicKey,
215*84e33947SAndroid Build Coastguard Worker size_t publicKeyLength) {
216*84e33947SAndroid Build Coastguard Worker if (publicKeyLength != kEcdsaP256PublicKeySize) {
217*84e33947SAndroid Build Coastguard Worker LOGE("Public key length %zu is unexpected.", publicKeyLength);
218*84e33947SAndroid Build Coastguard Worker return false;
219*84e33947SAndroid Build Coastguard Worker }
220*84e33947SAndroid Build Coastguard Worker for (size_t i = 0; i < ARRAY_SIZE(kTrustedPublicKeys); i++) {
221*84e33947SAndroid Build Coastguard Worker if (memcmp(kTrustedPublicKeys[i], publicKey, kEcdsaP256PublicKeySize) ==
222*84e33947SAndroid Build Coastguard Worker 0) {
223*84e33947SAndroid Build Coastguard Worker return true;
224*84e33947SAndroid Build Coastguard Worker }
225*84e33947SAndroid Build Coastguard Worker }
226*84e33947SAndroid Build Coastguard Worker return false;
227*84e33947SAndroid Build Coastguard Worker }
228*84e33947SAndroid Build Coastguard Worker } // anonymous namespace
229*84e33947SAndroid Build Coastguard Worker
authenticateBinary(const void * binary,size_t appBinaryLen,void ** realBinaryStart)230*84e33947SAndroid Build Coastguard Worker bool authenticateBinary(const void *binary, size_t appBinaryLen,
231*84e33947SAndroid Build Coastguard Worker void **realBinaryStart) {
232*84e33947SAndroid Build Coastguard Worker #ifndef CHRE_NAPP_AUTHENTICATION_ENABLED
233*84e33947SAndroid Build Coastguard Worker UNUSED_VAR(binary);
234*84e33947SAndroid Build Coastguard Worker UNUSED_VAR(realBinaryStart);
235*84e33947SAndroid Build Coastguard Worker LOGW(
236*84e33947SAndroid Build Coastguard Worker "Nanoapp authentication is disabled, which exposes the device to "
237*84e33947SAndroid Build Coastguard Worker "security risks!");
238*84e33947SAndroid Build Coastguard Worker return true;
239*84e33947SAndroid Build Coastguard Worker #endif
240*84e33947SAndroid Build Coastguard Worker if (appBinaryLen <= kHeaderSize) {
241*84e33947SAndroid Build Coastguard Worker LOGE("Binary size %zu is too short.", appBinaryLen);
242*84e33947SAndroid Build Coastguard Worker return false;
243*84e33947SAndroid Build Coastguard Worker }
244*84e33947SAndroid Build Coastguard Worker Authenticator authenticator;
245*84e33947SAndroid Build Coastguard Worker auto *header = static_cast<const ImageHeader *>(binary);
246*84e33947SAndroid Build Coastguard Worker const uint8_t *imageHash = header->headerInfo.binarySha256;
247*84e33947SAndroid Build Coastguard Worker const uint8_t *publicKey = header->publicKey;
248*84e33947SAndroid Build Coastguard Worker const uint32_t expectedAppBinaryLength =
249*84e33947SAndroid Build Coastguard Worker header->headerInfo.binaryLength + kHeaderSize;
250*84e33947SAndroid Build Coastguard Worker
251*84e33947SAndroid Build Coastguard Worker if (header->headerInfo.magic != kChreMagicNumber) {
252*84e33947SAndroid Build Coastguard Worker LOGE("Mismatched magic number.");
253*84e33947SAndroid Build Coastguard Worker } else if (header->headerInfo.headerVersion != 1) {
254*84e33947SAndroid Build Coastguard Worker LOGE("Header version %" PRIu32 " is unsupported.",
255*84e33947SAndroid Build Coastguard Worker header->headerInfo.headerVersion);
256*84e33947SAndroid Build Coastguard Worker } else if (expectedAppBinaryLength != appBinaryLen) {
257*84e33947SAndroid Build Coastguard Worker LOGE("Invalid binary length %zu. Expected %" PRIu32, appBinaryLen,
258*84e33947SAndroid Build Coastguard Worker expectedAppBinaryLength);
259*84e33947SAndroid Build Coastguard Worker } else if (!isValidProductionPublicKey(
260*84e33947SAndroid Build Coastguard Worker publicKey, getPublicKeyLength(header->headerInfo.flags))) {
261*84e33947SAndroid Build Coastguard Worker LOGE("Invalid public key attached on the image.");
262*84e33947SAndroid Build Coastguard Worker } else if (!hasCorrectHash(binary, header->headerInfo.binaryLength,
263*84e33947SAndroid Build Coastguard Worker imageHash)) {
264*84e33947SAndroid Build Coastguard Worker LOGE("Hash of the nanoapp image is incorrect.");
265*84e33947SAndroid Build Coastguard Worker } else if (!authenticator.loadEcpGroup() ||
266*84e33947SAndroid Build Coastguard Worker !authenticator.loadPublicKey(publicKey) ||
267*84e33947SAndroid Build Coastguard Worker !authenticator.loadSignature(header)) {
268*84e33947SAndroid Build Coastguard Worker LOGE("Failed to load authentication data.");
269*84e33947SAndroid Build Coastguard Worker } else if (!authenticator.authenticate(binary)) {
270*84e33947SAndroid Build Coastguard Worker LOGE("Failed to authenticate the image.");
271*84e33947SAndroid Build Coastguard Worker } else {
272*84e33947SAndroid Build Coastguard Worker *realBinaryStart = reinterpret_cast<void *>(
273*84e33947SAndroid Build Coastguard Worker reinterpret_cast<uintptr_t>(binary) + kHeaderSize);
274*84e33947SAndroid Build Coastguard Worker LOGI("Image is authenticated successfully!");
275*84e33947SAndroid Build Coastguard Worker return true;
276*84e33947SAndroid Build Coastguard Worker }
277*84e33947SAndroid Build Coastguard Worker return false;
278*84e33947SAndroid Build Coastguard Worker }
279*84e33947SAndroid Build Coastguard Worker } // namespace chre
280